/* * Copyright (c) 2001, 2013, 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 #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<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); if (srcOps == 0) { return; } dstOps = SurfaceData_GetOps(env, dstData); if (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<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); }