| /* |
| * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| #include <math.h> |
| |
| #include "jni_util.h" |
| #include "GraphicsPrimitiveMgr.h" |
| #include "Region.h" |
| |
| #include "sun_java2d_loops_ScaledBlit.h" |
| |
| /* |
| * The scaling loops used inside the helper functions are based on the |
| * following pseudocode for stepping through the source image: |
| * |
| * shift - number of bits of sub-pixel precision in scaled values |
| * srcxorig, srcyorig - scaled location of first pixel |
| * srcxinc, srcyinc - scaled x and y increments |
| * dstwidth, dstheight - number of pixels to process across and down |
| * |
| * 1. srcy = srcyorig; |
| * 2. for (dstheight) { |
| * 3. srcx = srcxorig; |
| * 4. for (dstwidth) { |
| * 5. fetch and process pixel for (srcx >> shift, srcy >> shift) |
| * 6. srcx += srcxinc; |
| * 7. } |
| * 8. srcy += srcyinc; |
| * 9. } |
| * |
| * Note that each execution of line 6 or 8 accumulates error of |
| * +/- 1 into the scaled coordinate variables. These lines are |
| * each executed once per pixel across or once per pixel down |
| * the region being iterated over, thus the error can accumulate |
| * up to a magnitude of dstwidth in the horizontal direction and |
| * dstheight in the vertical direction. |
| * |
| * If the error ever reaches a magnitude of (1 << shift) then we |
| * will be off by at least 1 source pixel in our mapping. |
| * |
| * Note that we increment the source coordinates by the srcxinc |
| * and srcyinc variables in each step. Thus, if our error ever |
| * accumulates to a magnitude equal to srcxinc or srcyinc then |
| * we will be ahead or behind of "where we should be" by at least |
| * one iteration. Since each iteration is a destination pixel, |
| * this means that our actual location will be off by at least |
| * one destination pixel. |
| * |
| * This means that all of the values: |
| * |
| * - (1 << shift) |
| * - srcxinc |
| * - srcyinc |
| * |
| * all represent a maximum bound on how much error we can accumulate |
| * before we are off by a source or a destination pixel. Thus, |
| * we should make sure that we never process more than that many |
| * pixels if we want to maintain single pixel accuracy. Even |
| * better would be to process many fewer pixels than those bounds |
| * to ensure that our accumulated error is much smaller than a |
| * pixel. |
| */ |
| |
| /* |
| * Find and return the largest tile size that is a power of 2 and |
| * which is small enough to yield some reassuring degree of subpixel |
| * accuracy. The degree of subpixel accuracy that will be preserved |
| * by the tile size it chooses will vary and the details on how |
| * it makes this decision are detailed in the comments below. |
| */ |
| static jint |
| findpow2tilesize(jint shift, jint sxinc, jint syinc) |
| { |
| /* |
| * The initial value of shift is our first estimate for |
| * the power of 2 for our tilesize since it ensures |
| * less than 1 source pixel of error. |
| * |
| * Reducing it until (1 << shift) is not larger than the |
| * smallest of our increments ensures we will have no more |
| * than 1 destination pixel of error as well. |
| */ |
| if (sxinc > syinc) { |
| sxinc = syinc; |
| } |
| if (sxinc == 0) { |
| /* Degenerate case will cause infinite loop in next loop... */ |
| return 1; |
| } |
| while ((1 << shift) > sxinc) { |
| shift--; |
| } |
| /* |
| * shift is now the largest it can be for less than 1 pixel |
| * of error in either source or destination spaces. |
| * |
| * Now we will try for at least 8 bits of subpixel accuracy |
| * with a tile size of at least 256x256 and reduce our subpixel |
| * accuracy on a sliding scale down to a tilesize of 1x1 when |
| * we have no bits of sub-pixel accuracy. |
| */ |
| if (shift >= 16) { |
| /* Subtracting 8 asks for 8 bits of subpixel accuracy. */ |
| shift -= 8; |
| } else { |
| /* Ask for half of the remaining bits to be subpixel accuracy. */ |
| /* Rounding is in favor of subpixel accuracy over tile size. */ |
| /* Worst case, shift == 0 and tilesize == (1 << 0) == 1 */ |
| shift /= 2; |
| } |
| return (1 << shift); |
| } |
| |
| /* |
| * For a given integer destination pixel coordinate "id", calculate the |
| * integer destination coordinate of the start of the "ts" sized tile |
| * in which it resides. |
| * Tiles all start at even multiples of the tile size from the integer |
| * destination origin "io". |
| * |
| * id == integer destination coordinate |
| * io == integer destination operation origin |
| * ts == tilesize (must be power of 2) |
| */ |
| #define TILESTART(id, io, ts) ((io) + (((id)-(io)) & (~((ts)-1)))) |
| |
| /* |
| * For a given integer destination pixel coordinate "id", calculate the |
| * sub-pixel accurate source coordinate from which its sample comes. |
| * The returned source coordinate is expressed in a shifted fractional |
| * arithmetic number system. |
| * |
| * id == integer destination coordinate |
| * fo == floating point destination operation origin, |
| * sf == source coordinate scale factor per destination pixel |
| * (multiplied by fractional arithmetic "shift") |
| * |
| * The caller is required to cast this value to the appropriate |
| * integer type for the needed precision. The rendering code which |
| * deals only with valid coordinates within the bounds of the source |
| * rectangle uses jint. The setup code, which occasionally deals |
| * with coordinates that run out of bounds, uses jlong. |
| * |
| * Note that the rounding in this calculation is at a fraction of a |
| * source pixel of (1.0 / (1<<shift)) since the scale factor includes |
| * the fractional shift. As a result, the type of rounding used is |
| * not very significant (floor, floor(x+.5), or ceil(x-.5)), but the |
| * ceil(x-.5) version is used for consistency with the way that pixel |
| * coordinates are rounded to assign the ".5" value to the lower |
| * integer. |
| */ |
| #define SRCLOC(id, fo, sf) (ceil((((id) + 0.5) - (fo)) * (sf) - 0.5)) |
| |
| /* |
| * Reverse map a srctarget coordinate into device space and refine the |
| * answer. More specifically, what we are looking for is the smallest |
| * destination coordinate that maps to a source coordinate that is |
| * greater than or equal to the given target source coordinate. |
| * |
| * Note that since the inner loops use math that maps a destination |
| * coordinate into source space and that, even though the equation |
| * we use below is the theoretical inverse of the dst->src mapping, |
| * we cannot rely on floating point math to guarantee that applying |
| * both of these equations in sequence will give us an exact mapping |
| * of src->dst->src. Thus, we must search back and forth to see if |
| * we really map back to the given source coordinate and that we are |
| * the smallest destination coordinate that does so. |
| * |
| * Note that, in practice, the answer from the initial guess tends to |
| * be the right answer most of the time and the loop ends up finding |
| * one iteration to be ">= srctarget" and the next to be "< srctarget" |
| * and thus finds the answer in 2 iterations. A small number of |
| * times, the initial guess is 1 too low and so we do one iteration |
| * at "< srctarget" and the next at ">= srctarget" and again find the |
| * answer in 2 iterations. All cases encountered during testing ended |
| * up falling into one of those 2 categories and so the loop was always |
| * executed exactly twice. |
| * |
| * Note also that the calculation of srcloc below may attempt to calculate |
| * the src location of the destination pixel which is "1 beyond" the |
| * end of the source image. Since our shift calculation code in the |
| * main function only guaranteed that "srcw << shift" did not overflow |
| * a 32-bit signed integer, we cannot guarantee that "(srcw+1) << shift" |
| * or, more generally, "(srcw << shift)+srcinc" does not overflow. |
| * As a result, we perform our calculations here with jlong values |
| * so that we aren't affected by this overflow. Since srcw (shifted) |
| * and srcinc are both 32-bit values, their sum cannot possibly overflow |
| * a jlong. In fact, we can step up to a couple of billion steps of |
| * size "srcinc" past the end of the image before we have to worry |
| * about overflow - in practice, though, the search never steps more |
| * than 1 past the end of the image so this buffer is more than enough. |
| */ |
| static jint |
| refine(jint intorigin, jdouble dblorigin, jint tilesize, |
| jdouble scale, jint srctarget, jint srcinc) |
| { |
| /* Make a first estimate of dest coordinate from srctarget */ |
| jint dstloc = (jint) ceil(dblorigin + srctarget / scale - 0.5); |
| /* Loop until we get at least one value < and one >= the target */ |
| jboolean wasneg = JNI_FALSE; |
| jboolean waspos = JNI_FALSE; |
| jlong lsrcinc = srcinc; |
| jlong lsrctarget = srctarget; |
| |
| while (JNI_TRUE) { |
| /* |
| * Find src coordinate from dest coordinate using the same |
| * math we will use below when iterating over tiles. |
| */ |
| jint tilestart = TILESTART(dstloc, intorigin, tilesize); |
| jlong lsrcloc = (jlong) SRCLOC(tilestart, dblorigin, scale); |
| if (dstloc > tilestart) { |
| lsrcloc += lsrcinc * ((jlong) dstloc - tilestart); |
| } |
| if (lsrcloc >= lsrctarget) { |
| /* |
| * If we were previously less than target, then the current |
| * dstloc is the smallest dst which maps >= the target. |
| */ |
| if (wasneg) break; |
| dstloc--; |
| waspos = JNI_TRUE; |
| } else { |
| /* |
| * If we were previously greater than target, then this must |
| * be the first dstloc which maps to < the target. Since we |
| * want the smallest which maps >= the target, increment it |
| * first before returning. |
| */ |
| dstloc++; |
| if (waspos) break; |
| wasneg = JNI_TRUE; |
| } |
| } |
| return dstloc; |
| } |
| |
| /* |
| * Class: sun_java2d_loops_ScaledBlit |
| * Method: Scale |
| * Signature: (Lsun/java2d/SurfaceData;Lsun/java2d/SurfaceData;Ljava/awt/Composite;Lsun/java2d/pipe/Region;IIIIDDDD)V |
| */ |
| JNIEXPORT void JNICALL |
| Java_sun_java2d_loops_ScaledBlit_Scale |
| (JNIEnv *env, jobject self, |
| jobject srcData, jobject dstData, |
| jobject comp, jobject clip, |
| jint sx1, jint sy1, jint sx2, jint sy2, |
| jdouble ddx1, jdouble ddy1, jdouble ddx2, jdouble ddy2) |
| { |
| SurfaceDataOps *srcOps; |
| SurfaceDataOps *dstOps; |
| SurfaceDataRasInfo srcInfo; |
| SurfaceDataRasInfo dstInfo; |
| NativePrimitive *pPrim; |
| CompositeInfo compInfo; |
| jint sxinc, syinc, shift; |
| jint tilesize; |
| jint idx1, idy1; |
| jdouble scalex, scaley; |
| RegionData clipInfo; |
| jint dstFlags; |
| jboolean xunderflow, yunderflow; |
| |
| pPrim = GetNativePrim(env, self); |
| if (pPrim == NULL) { |
| return; |
| } |
| if (pPrim->pCompType->getCompInfo != NULL) { |
| (*pPrim->pCompType->getCompInfo)(env, &compInfo, comp); |
| } |
| if (Region_GetInfo(env, clip, &clipInfo)) { |
| return; |
| } |
| |
| srcOps = SurfaceData_GetOps(env, srcData); |
| dstOps = SurfaceData_GetOps(env, dstData); |
| if (srcOps == 0 || dstOps == 0) { |
| return; |
| } |
| |
| /* |
| * Determine the precision to use for the fixed point math |
| * for the coordinate scaling. |
| * - OR together srcw and srch to get the MSB between the two |
| * - Next shift it up until it goes negative |
| * - Count the shifts and that will be the most accurate |
| * precision available for the fixed point math |
| * - a source coordinate of 1.0 will be (1 << shift) |
| * - srcw & srch will be (srcw << shift) and (srch << shift) |
| * and will not overflow |
| * Note that if srcw or srch are so large that they are |
| * negative numbers before shifting, then: |
| * - shift will be 0 |
| * - tilesize will end up being 1x1 tiles |
| * - we will brute force calculate the source location |
| * of every destination pixel using the TILESTART and |
| * SRCLOC macros in this function and then call the |
| * scale helper function to copy one pixel at a time. |
| * - TILESTART involves mostly jdouble calculations so |
| * it should not have integer overflow problems. |
| */ |
| sxinc = (sx2 - sx1) | (sy2 - sy1); |
| shift = 0; |
| if (sxinc > 0) { |
| while ((sxinc <<= 1) > 0) { |
| shift++; |
| } |
| } |
| /* |
| * Now determine the scaled integer increments used to traverse |
| * the source image for each destination pixel. Our shift value |
| * has been calculated above so that any location within the |
| * destination image can be represented as a scaled integer |
| * without incurring integer overflow. |
| * |
| * But we also need to worry about overflow of the sxinc and syinc |
| * parameters. We already know that "srcw<<shift" and "srch<<shift" |
| * cannot overflow a jint, and the only time that sxinc and syinc |
| * can be larger than those two values is if ddy2-ddy1 or ddx2-ddx1 |
| * are smaller than 1. Since this situation implies that the |
| * output area is no more than one pixel wide or tall, then we are |
| * stepping by distances that are at least the size of the image |
| * and only one destination pixel will ever be rendered - thus the |
| * amount by which we step is largely irrelevant since after |
| * drawing the first "in bounds" pixel, we will step completely |
| * out of the source image and render nothing more. As a result, |
| * we assign the appropriate "size of image" stepping parameter |
| * for any scale to smaller than one device pixel. |
| */ |
| yunderflow = (ddy2 - ddy1) < 1.0; |
| scaley = (((jdouble) (sy2 - sy1)) / (ddy2 - ddy1)) * (1 << shift); |
| syinc = (yunderflow ? ((sy2 - sy1) << shift) : (jint) scaley); |
| xunderflow = (ddx2 - ddx1) < 1.0; |
| scalex = (((jdouble) (sx2 - sx1)) / (ddx2 - ddx1)) * (1 << shift); |
| sxinc = (xunderflow ? ((sx2 - sx1) << shift) : (jint) scalex); |
| tilesize = findpow2tilesize(shift, sxinc, syinc); |
| |
| |
| srcInfo.bounds.x1 = sx1; |
| srcInfo.bounds.y1 = sy1; |
| srcInfo.bounds.x2 = sx2; |
| srcInfo.bounds.y2 = sy2; |
| if (srcOps->Lock(env, srcOps, &srcInfo, pPrim->srcflags) != SD_SUCCESS) { |
| return; |
| } |
| if (srcInfo.bounds.x2 <= srcInfo.bounds.x1 || |
| srcInfo.bounds.y2 <= srcInfo.bounds.y1) |
| { |
| SurfaceData_InvokeUnlock(env, srcOps, &srcInfo); |
| return; |
| } |
| |
| /* |
| * Only refine lower bounds if lower source coordinate was clipped |
| * because the math will work out to be exactly idx1, idy1 if not. |
| * Always refine upper bounds since we want to make sure not to |
| * overstep the source bounds based on the tiled iteration math. |
| * |
| * For underflow cases, simply check if the SRCLOC for the single |
| * destination pixel maps inside the source bounds. If it does, |
| * we render that pixel row or column (and only that pixel row |
| * or column). If it does not, we render nothing. |
| */ |
| idx1 = (jint) ceil(ddx1 - 0.5); |
| idy1 = (jint) ceil(ddy1 - 0.5); |
| if (xunderflow) { |
| jdouble x = sx1 + (SRCLOC(idx1, ddx1, scalex) / (1 << shift)); |
| dstInfo.bounds.x1 = dstInfo.bounds.x2 = idx1; |
| if (x >= srcInfo.bounds.x1 && x < srcInfo.bounds.x2) { |
| dstInfo.bounds.x2++; |
| } |
| } else { |
| dstInfo.bounds.x1 = ((srcInfo.bounds.x1 <= sx1) |
| ? idx1 |
| : refine(idx1, ddx1, tilesize, scalex, |
| (srcInfo.bounds.x1-sx1) << shift, sxinc)); |
| dstInfo.bounds.x2 = refine(idx1, ddx1, tilesize, scalex, |
| (srcInfo.bounds.x2-sx1) << shift, sxinc); |
| } |
| if (yunderflow) { |
| jdouble y = sy1 + (SRCLOC(idy1, ddy1, scaley) / (1 << shift)); |
| dstInfo.bounds.y1 = dstInfo.bounds.y2 = idy1; |
| if (y >= srcInfo.bounds.y1 && y < srcInfo.bounds.y2) { |
| dstInfo.bounds.y2++; |
| } |
| } else { |
| dstInfo.bounds.y1 = ((srcInfo.bounds.y1 <= sy1) |
| ? idy1 |
| : refine(idy1, ddy1, tilesize, scaley, |
| (srcInfo.bounds.y1-sy1) << shift, syinc)); |
| dstInfo.bounds.y2 = refine(idy1, ddy1, tilesize, scaley, |
| (srcInfo.bounds.y2-sy1) << shift, syinc); |
| } |
| |
| SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds); |
| dstFlags = pPrim->dstflags; |
| if (!Region_IsRectangular(&clipInfo)) { |
| dstFlags |= SD_LOCK_PARTIAL_WRITE; |
| } |
| if (dstOps->Lock(env, dstOps, &dstInfo, dstFlags) != SD_SUCCESS) { |
| SurfaceData_InvokeUnlock(env, srcOps, &srcInfo); |
| return; |
| } |
| |
| if (dstInfo.bounds.x2 > dstInfo.bounds.x1 && |
| dstInfo.bounds.y2 > dstInfo.bounds.y1) |
| { |
| srcOps->GetRasInfo(env, srcOps, &srcInfo); |
| dstOps->GetRasInfo(env, dstOps, &dstInfo); |
| if (srcInfo.rasBase && dstInfo.rasBase) { |
| SurfaceDataBounds span; |
| void *pSrc = PtrCoord(srcInfo.rasBase, |
| sx1, srcInfo.pixelStride, |
| sy1, srcInfo.scanStride); |
| |
| Region_IntersectBounds(&clipInfo, &dstInfo.bounds); |
| Region_StartIteration(env, &clipInfo); |
| if (tilesize >= (ddx2 - ddx1) && |
| tilesize >= (ddy2 - ddy1)) |
| { |
| /* Do everything in one tile */ |
| jint sxloc = (jint) SRCLOC(idx1, ddx1, scalex); |
| jint syloc = (jint) SRCLOC(idy1, ddy1, scaley); |
| while (Region_NextIteration(&clipInfo, &span)) { |
| jint tsxloc = sxloc; |
| jint tsyloc = syloc; |
| void *pDst; |
| |
| if (span.y1 > idy1) { |
| tsyloc += syinc * (span.y1 - idy1); |
| } |
| if (span.x1 > idx1) { |
| tsxloc += sxinc * (span.x1 - idx1); |
| } |
| |
| pDst = PtrCoord(dstInfo.rasBase, |
| span.x1, dstInfo.pixelStride, |
| span.y1, dstInfo.scanStride); |
| (*pPrim->funcs.scaledblit)(pSrc, pDst, |
| span.x2-span.x1, span.y2-span.y1, |
| tsxloc, tsyloc, |
| sxinc, syinc, shift, |
| &srcInfo, &dstInfo, |
| pPrim, &compInfo); |
| } |
| } else { |
| /* Break each clip span into tiles for better accuracy. */ |
| while (Region_NextIteration(&clipInfo, &span)) { |
| jint tilex, tiley; |
| jint sxloc, syloc; |
| jint x1, y1, x2, y2; |
| void *pDst; |
| |
| for (tiley = TILESTART(span.y1, idy1, tilesize); |
| tiley < span.y2; |
| tiley += tilesize) |
| { |
| /* Clip span to Y range of current tile */ |
| y1 = tiley; |
| y2 = tiley + tilesize; |
| if (y1 < span.y1) y1 = span.y1; |
| if (y2 > span.y2) y2 = span.y2; |
| |
| /* Find scaled source coordinate of first pixel */ |
| syloc = (jint) SRCLOC(tiley, ddy1, scaley); |
| if (y1 > tiley) { |
| syloc += syinc * (y1 - tiley); |
| } |
| |
| for (tilex = TILESTART(span.x1, idx1, tilesize); |
| tilex < span.x2; |
| tilex += tilesize) |
| { |
| /* Clip span to X range of current tile */ |
| x1 = tilex; |
| x2 = tilex + tilesize; |
| if (x1 < span.x1) x1 = span.x1; |
| if (x2 > span.x2) x2 = span.x2; |
| |
| /* Find scaled source coordinate of first pixel */ |
| sxloc = (jint) SRCLOC(tilex, ddx1, scalex); |
| if (x1 > tilex) { |
| sxloc += sxinc * (x1 - tilex); |
| } |
| |
| pDst = PtrCoord(dstInfo.rasBase, |
| x1, dstInfo.pixelStride, |
| y1, dstInfo.scanStride); |
| (*pPrim->funcs.scaledblit)(pSrc, pDst, x2-x1, y2-y1, |
| sxloc, syloc, |
| sxinc, syinc, shift, |
| &srcInfo, &dstInfo, |
| pPrim, &compInfo); |
| } |
| } |
| } |
| } |
| Region_EndIteration(env, &clipInfo); |
| } |
| SurfaceData_InvokeRelease(env, dstOps, &dstInfo); |
| SurfaceData_InvokeRelease(env, srcOps, &srcInfo); |
| } |
| SurfaceData_InvokeUnlock(env, dstOps, &dstInfo); |
| SurfaceData_InvokeUnlock(env, srcOps, &srcInfo); |
| } |