0N/A/*
2362N/A * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A#include <stdlib.h>
0N/A#include "jni_util.h"
0N/A#include "math.h"
0N/A
0N/A#include "GraphicsPrimitiveMgr.h"
0N/A#include "Region.h"
0N/A
0N/A#include "sun_java2d_loops_TransformHelper.h"
0N/A#include "java_awt_image_AffineTransformOp.h"
0N/A
0N/A/*
0N/A * The stub functions replace the bilinear and bicubic interpolation
0N/A * functions with NOP versions so that the performance of the helper
0N/A * functions that fetch the data can be more directly tested. They
0N/A * are not compiled or enabled by default. Change the following
0N/A * #undef to a #define to build the stub functions.
0N/A *
0N/A * When compiled, they are enabled by the environment variable TXSTUB.
0N/A * When compiled, there is also code to disable the VIS versions and
0N/A * use the C versions in this file in their place by defining the TXNOVIS
0N/A * environment variable.
0N/A */
0N/A#undef MAKE_STUBS
0N/A
0N/A/* The number of IntArgbPre samples to store in the temporary buffer. */
0N/A#define LINE_SIZE 2048
0N/A
0N/A/* The size of a stack allocated buffer to hold edge coordinates (see below). */
0N/A#define MAXEDGES 1024
0N/A
0N/A/* Declare the software interpolation functions. */
0N/Astatic TransformInterpFunc BilinearInterp;
0N/Astatic TransformInterpFunc BicubicInterp;
0N/A
0N/A#ifdef MAKE_STUBS
0N/A/* Optionally Declare the stub interpolation functions. */
0N/Astatic TransformInterpFunc BilinearInterpStub;
0N/Astatic TransformInterpFunc BicubicInterpStub;
0N/A#endif /* MAKE_STUBS */
0N/A
0N/A/*
0N/A * Initially choose the software interpolation functions.
0N/A * These choices can be overridden by platform code that runs during the
0N/A * primitive registration phase of initialization by storing pointers to
0N/A * better functions in these pointers.
0N/A * Compiling the stubs also turns on code below that can re-install the
0N/A * software functions or stub functions on the first call to this primitive.
0N/A */
0N/ATransformInterpFunc *pBilinearFunc = BilinearInterp;
0N/ATransformInterpFunc *pBicubicFunc = BicubicInterp;
0N/A
0N/A/*
4301N/A * The dxydxy parameters of the inverse transform determine how
4301N/A * quickly we step through the source image. For tiny scale
4301N/A * factors (on the order of 1E-16 or so) the stepping distances
4301N/A * are huge. The image has been scaled so small that stepping
4301N/A * a single pixel in device space moves the sampling point by
4301N/A * billions (or more) pixels in the source image space. These
4301N/A * huge stepping values can overflow the whole part of the longs
4301N/A * we use for the fixed point stepping equations and so we need
4301N/A * a more robust solution. We could simply iterate over every
4301N/A * device pixel, use the inverse transform to transform it back
4301N/A * into the source image coordinate system and then test it for
4301N/A * being in range and sample pixel-by-pixel, but that is quite
4301N/A * a bit more expensive. Fortunately, if the scale factors are
4301N/A * so tiny that we overflow our long values then the number of
4301N/A * pixels we are planning to visit should be very tiny. The only
4301N/A * exception to that rule is if the scale factor along one
4301N/A * dimension is tiny (creating the huge stepping values), and
4301N/A * the scale factor along the other dimension is fairly regular
4301N/A * or an up-scale. In that case we have a lot of pixels along
4301N/A * the direction of the larger axis to sample, but few along the
4301N/A * smaller axis. Though, pessimally, with an added shear factor
4301N/A * such a linearly tiny image could have bounds that cover a large
4301N/A * number of pixels. Such odd transformations should be very
4301N/A * rare and the absolute limit on calculations would involve a
4301N/A * single reverse transform of every pixel in the output image
4301N/A * which is not fast, but it should not cause an undue stall
4301N/A * of the rendering software.
4301N/A *
4301N/A * The specific test we will use is to calculate the inverse
4301N/A * transformed values of every corner of the destination bounds
4301N/A * (in order to be user-clip independent) and if we can
4301N/A * perform a fixed-point-long inverse transform of all of
4301N/A * those points without overflowing we will use the fast
4301N/A * fixed point algorithm. Otherwise we will use the safe
4301N/A * per-pixel transform algorithm.
4301N/A * The 4 corners are 0,0, 0,dsth, dstw,0, dstw,dsth
4301N/A * Transformed they are:
4301N/A * tx, ty
4301N/A * tx +dxdy*H, ty +dydy*H
4301N/A * tx+dxdx*W, ty+dydx*W
4301N/A * tx+dxdx*W+dxdy*H, ty+dydx*W+dydy*H
4301N/A */
4301N/A/* We reject coordinates not less than 1<<30 so that the distance between */
4301N/A/* any 2 of them is less than 1<<31 which would overflow into the sign */
4301N/A/* bit of a signed long value used to represent fixed point coordinates. */
4301N/A#define TX_FIXED_UNSAFE(v) (fabs(v) >= (1<<30))
4301N/Astatic jboolean
4301N/AcheckOverflow(jint dxoff, jint dyoff,
4301N/A SurfaceDataBounds *pBounds,
4301N/A TransformInfo *pItxInfo,
4301N/A jdouble *retx, jdouble *rety)
4301N/A{
4301N/A jdouble x, y;
4301N/A
4301N/A x = dxoff+pBounds->x1+0.5; /* Center of pixel x1 */
4301N/A y = dyoff+pBounds->y1+0.5; /* Center of pixel y1 */
4301N/A Transform_transform(pItxInfo, &x, &y);
4301N/A *retx = x;
4301N/A *rety = y;
4301N/A if (TX_FIXED_UNSAFE(x) || TX_FIXED_UNSAFE(y)) {
4301N/A return JNI_TRUE;
4301N/A }
4301N/A
4301N/A x = dxoff+pBounds->x2-0.5; /* Center of pixel x2-1 */
4301N/A y = dyoff+pBounds->y1+0.5; /* Center of pixel y1 */
4301N/A Transform_transform(pItxInfo, &x, &y);
4301N/A if (TX_FIXED_UNSAFE(x) || TX_FIXED_UNSAFE(y)) {
4301N/A return JNI_TRUE;
4301N/A }
4301N/A
4301N/A x = dxoff+pBounds->x1+0.5; /* Center of pixel x1 */
4301N/A y = dyoff+pBounds->y2-0.5; /* Center of pixel y2-1 */
4301N/A Transform_transform(pItxInfo, &x, &y);
4301N/A if (TX_FIXED_UNSAFE(x) || TX_FIXED_UNSAFE(y)) {
4301N/A return JNI_TRUE;
4301N/A }
4301N/A
4301N/A x = dxoff+pBounds->x2-0.5; /* Center of pixel x2-1 */
4301N/A y = dyoff+pBounds->y2-0.5; /* Center of pixel y2-1 */
4301N/A Transform_transform(pItxInfo, &x, &y);
4301N/A if (TX_FIXED_UNSAFE(x) || TX_FIXED_UNSAFE(y)) {
4301N/A return JNI_TRUE;
4301N/A }
4301N/A
4301N/A return JNI_FALSE;
4301N/A}
4301N/A
4301N/A/*
0N/A * Fill the edge buffer with pairs of coordinates representing the maximum
0N/A * left and right pixels of the destination surface that should be processed
0N/A * on each scanline, clipped to the bounds parameter.
0N/A * The number of scanlines to calculate is implied by the bounds parameter.
0N/A * Only pixels that map back through the specified (inverse) transform to a
0N/A * source coordinate that falls within the (0, 0, sw, sh) bounds of the
0N/A * source image should be processed.
4301N/A * pEdges points to an array of jints that holds 2 + numedges*2 values where
4301N/A * numedges should match (pBounds->y2 - pBounds->y1).
4301N/A * The first two jints in pEdges should be set to y1 and y2 and every pair
4301N/A * of jints after that represent the xmin,xmax of all pixels in range of
4301N/A * the transformed blit for the corresponding scanline.
0N/A */
4301N/Astatic void
4301N/AcalculateEdges(jint *pEdges,
0N/A SurfaceDataBounds *pBounds,
0N/A TransformInfo *pItxInfo,
0N/A jlong xbase, jlong ybase,
0N/A juint sw, juint sh)
0N/A{
0N/A jlong dxdxlong, dydxlong;
0N/A jlong dxdylong, dydylong;
0N/A jlong drowxlong, drowylong;
0N/A jint dx1, dy1, dx2, dy2;
0N/A
0N/A dxdxlong = DblToLong(pItxInfo->dxdx);
0N/A dydxlong = DblToLong(pItxInfo->dydx);
0N/A dxdylong = DblToLong(pItxInfo->dxdy);
0N/A dydylong = DblToLong(pItxInfo->dydy);
0N/A
0N/A dx1 = pBounds->x1;
0N/A dy1 = pBounds->y1;
0N/A dx2 = pBounds->x2;
0N/A dy2 = pBounds->y2;
4301N/A *pEdges++ = dy1;
4301N/A *pEdges++ = dy2;
0N/A
0N/A drowxlong = (dx2-dx1-1) * dxdxlong;
0N/A drowylong = (dx2-dx1-1) * dydxlong;
0N/A
0N/A while (dy1 < dy2) {
0N/A jlong xlong, ylong;
0N/A
0N/A dx1 = pBounds->x1;
0N/A dx2 = pBounds->x2;
0N/A
0N/A xlong = xbase;
0N/A ylong = ybase;
0N/A while (dx1 < dx2 &&
0N/A (((juint) WholeOfLong(ylong)) >= sh ||
0N/A ((juint) WholeOfLong(xlong)) >= sw))
0N/A {
0N/A dx1++;
0N/A xlong += dxdxlong;
0N/A ylong += dydxlong;
0N/A }
0N/A
0N/A xlong = xbase + drowxlong;
0N/A ylong = ybase + drowylong;
0N/A while (dx2 > dx1 &&
0N/A (((juint) WholeOfLong(ylong)) >= sh ||
0N/A ((juint) WholeOfLong(xlong)) >= sw))
0N/A {
0N/A dx2--;
0N/A xlong -= dxdxlong;
0N/A ylong -= dydxlong;
0N/A }
0N/A
0N/A *pEdges++ = dx1;
0N/A *pEdges++ = dx2;
0N/A
0N/A /* Increment to next scanline */
0N/A xbase += dxdylong;
0N/A ybase += dydylong;
0N/A dy1++;
0N/A }
4301N/A}
0N/A
4301N/Astatic void
4301N/ATransform_SafeHelper(JNIEnv *env,
4301N/A SurfaceDataOps *srcOps,
4301N/A SurfaceDataOps *dstOps,
4301N/A SurfaceDataRasInfo *pSrcInfo,
4301N/A SurfaceDataRasInfo *pDstInfo,
4301N/A NativePrimitive *pMaskBlitPrim,
4301N/A CompositeInfo *pCompInfo,
4301N/A TransformHelperFunc *pHelperFunc,
4301N/A TransformInterpFunc *pInterpFunc,
4301N/A RegionData *pClipInfo, TransformInfo *pItxInfo,
4301N/A jint *pData, jint *pEdges,
4301N/A jint dxoff, jint dyoff, jint sw, jint sh);
0N/A
0N/A/*
0N/A * Class: sun_java2d_loops_TransformHelper
0N/A * Method: Transform
0N/A * Signature: (Lsun/java2d/loops/MaskBlit;Lsun/java2d/SurfaceData;Lsun/java2d/SurfaceData;Ljava/awt/Composite;Lsun/java2d/pipe/Region;Ljava/awt/geom/AffineTransform;IIIIIIIII[I)V
0N/A */
0N/AJNIEXPORT void JNICALL
0N/AJava_sun_java2d_loops_TransformHelper_Transform
0N/A (JNIEnv *env, jobject self,
0N/A jobject maskblit,
0N/A jobject srcData, jobject dstData,
0N/A jobject comp, jobject clip,
0N/A jobject itxform, jint txtype,
0N/A jint sx1, jint sy1, jint sx2, jint sy2,
0N/A jint dx1, jint dy1, jint dx2, jint dy2,
0N/A jintArray edgeArray, jint dxoff, jint dyoff)
0N/A{
0N/A SurfaceDataOps *srcOps;
0N/A SurfaceDataOps *dstOps;
0N/A SurfaceDataRasInfo srcInfo;
0N/A SurfaceDataRasInfo dstInfo;
0N/A NativePrimitive *pHelperPrim;
0N/A NativePrimitive *pMaskBlitPrim;
0N/A CompositeInfo compInfo;
0N/A RegionData clipInfo;
0N/A TransformInfo itxInfo;
0N/A jint maxlinepix;
0N/A TransformHelperFunc *pHelperFunc;
0N/A TransformInterpFunc *pInterpFunc;
4301N/A jdouble xorig, yorig;
4434N/A jlong numedges;
0N/A jint *pEdges;
4301N/A jint edgebuf[2 + MAXEDGES * 2];
4301N/A union {
4301N/A jlong align;
4301N/A jint data[LINE_SIZE];
4301N/A } rgb;
0N/A
0N/A#ifdef MAKE_STUBS
0N/A static int th_initialized;
0N/A
0N/A /* For debugging only - used to swap in alternate funcs for perf testing */
0N/A if (!th_initialized) {
0N/A if (getenv("TXSTUB") != 0) {
0N/A pBilinearFunc = BilinearInterpStub;
0N/A pBicubicFunc = BicubicInterpStub;
0N/A } else if (getenv("TXNOVIS") != 0) {
0N/A pBilinearFunc = BilinearInterp;
0N/A pBicubicFunc = BicubicInterp;
0N/A }
0N/A th_initialized = 1;
0N/A }
0N/A#endif /* MAKE_STUBS */
0N/A
0N/A pHelperPrim = GetNativePrim(env, self);
0N/A if (pHelperPrim == NULL) {
0N/A /* Should never happen... */
0N/A return;
0N/A }
0N/A pMaskBlitPrim = GetNativePrim(env, maskblit);
0N/A if (pMaskBlitPrim == NULL) {
0N/A /* Exception was thrown by GetNativePrim */
0N/A return;
0N/A }
0N/A if (pMaskBlitPrim->pCompType->getCompInfo != NULL) {
0N/A (*pMaskBlitPrim->pCompType->getCompInfo)(env, &compInfo, comp);
0N/A }
0N/A if (Region_GetInfo(env, clip, &clipInfo)) {
0N/A return;
0N/A }
0N/A
0N/A srcOps = SurfaceData_GetOps(env, srcData);
0N/A dstOps = SurfaceData_GetOps(env, dstData);
0N/A if (srcOps == 0 || dstOps == 0) {
0N/A return;
0N/A }
0N/A
0N/A /*
0N/A * Grab the appropriate pointer to the helper and interpolation
0N/A * routines and calculate the maximum number of destination pixels
0N/A * that can be processed in one intermediate buffer based on the
0N/A * size of the buffer and the number of samples needed per pixel.
0N/A */
0N/A switch (txtype) {
0N/A case java_awt_image_AffineTransformOp_TYPE_NEAREST_NEIGHBOR:
0N/A pHelperFunc = pHelperPrim->funcs.transformhelpers->nnHelper;
0N/A pInterpFunc = NULL;
0N/A maxlinepix = LINE_SIZE;
0N/A break;
0N/A case java_awt_image_AffineTransformOp_TYPE_BILINEAR:
0N/A pHelperFunc = pHelperPrim->funcs.transformhelpers->blHelper;
0N/A pInterpFunc = pBilinearFunc;
0N/A maxlinepix = LINE_SIZE / 4;
0N/A break;
0N/A case java_awt_image_AffineTransformOp_TYPE_BICUBIC:
0N/A pHelperFunc = pHelperPrim->funcs.transformhelpers->bcHelper;
0N/A pInterpFunc = pBicubicFunc;
0N/A maxlinepix = LINE_SIZE / 16;
0N/A break;
0N/A }
0N/A
0N/A srcInfo.bounds.x1 = sx1;
0N/A srcInfo.bounds.y1 = sy1;
0N/A srcInfo.bounds.x2 = sx2;
0N/A srcInfo.bounds.y2 = sy2;
0N/A dstInfo.bounds.x1 = dx1;
0N/A dstInfo.bounds.y1 = dy1;
0N/A dstInfo.bounds.x2 = dx2;
0N/A dstInfo.bounds.y2 = dy2;
0N/A SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds);
0N/A if (srcOps->Lock(env, srcOps, &srcInfo, pHelperPrim->srcflags)
0N/A != SD_SUCCESS)
0N/A {
4301N/A /* edgeArray should already contain zeros for min/maxy */
0N/A return;
0N/A }
0N/A if (dstOps->Lock(env, dstOps, &dstInfo, pMaskBlitPrim->dstflags)
0N/A != SD_SUCCESS)
0N/A {
0N/A SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
4301N/A /* edgeArray should already contain zeros for min/maxy */
0N/A return;
0N/A }
0N/A Region_IntersectBounds(&clipInfo, &dstInfo.bounds);
0N/A
4434N/A numedges = (((jlong) dstInfo.bounds.y2) - ((jlong) dstInfo.bounds.y1));
4434N/A if (numedges <= 0) {
4434N/A pEdges = NULL;
4434N/A } else if (!JNU_IsNull(env, edgeArray)) {
4434N/A /*
4434N/A * Ideally Java should allocate an array large enough, but if
4434N/A * we ever have a miscommunication about the number of edge
4434N/A * lines, or if the Java array calculation should overflow to
4434N/A * a positive number and succeed in allocating an array that
4434N/A * is too small, we need to verify that it can still hold the
4434N/A * number of integers that we plan to store to be safe.
4434N/A */
4434N/A jsize edgesize = (*env)->GetArrayLength(env, edgeArray);
4434N/A /* (edgesize/2 - 1) should avoid any overflow or underflow. */
4434N/A pEdges = (((edgesize / 2) - 1) >= numedges)
4434N/A ? (*env)->GetPrimitiveArrayCritical(env, edgeArray, NULL)
4434N/A : NULL;
4434N/A } else if (numedges > MAXEDGES) {
4434N/A /* numedges variable (jlong) can be at most ((1<<32)-1) */
4434N/A /* memsize can overflow a jint, but not a jlong */
4434N/A jlong memsize = ((numedges * 2) + 2) * sizeof(*pEdges);
4434N/A pEdges = (memsize == ((size_t) memsize))
4434N/A ? malloc((size_t) memsize)
4434N/A : NULL;
4301N/A } else {
4301N/A pEdges = edgebuf;
4301N/A }
4301N/A
4434N/A if (pEdges == NULL) {
4434N/A if (numedges > 0) {
4434N/A JNU_ThrowInternalError(env, "Unable to allocate edge list");
4434N/A }
4434N/A SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
4434N/A SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
4434N/A /* edgeArray should already contain zeros for min/maxy */
4434N/A return;
4434N/A }
4434N/A
0N/A Transform_GetInfo(env, itxform, &itxInfo);
0N/A
0N/A if (!Region_IsEmpty(&clipInfo)) {
0N/A srcOps->GetRasInfo(env, srcOps, &srcInfo);
0N/A dstOps->GetRasInfo(env, dstOps, &dstInfo);
4301N/A if (srcInfo.rasBase == NULL || dstInfo.rasBase == NULL) {
4301N/A pEdges[0] = pEdges[1] = 0;
4301N/A } else if (checkOverflow(dxoff, dyoff, &dstInfo.bounds,
4301N/A &itxInfo, &xorig, &yorig))
4301N/A {
4301N/A Transform_SafeHelper(env, srcOps, dstOps,
4301N/A &srcInfo, &dstInfo,
4301N/A pMaskBlitPrim, &compInfo,
4301N/A pHelperFunc, pInterpFunc,
4301N/A &clipInfo, &itxInfo, rgb.data, pEdges,
4301N/A dxoff, dyoff, sx2-sx1, sy2-sy1);
4301N/A } else {
0N/A SurfaceDataBounds span;
4301N/A jlong dxdxlong, dydxlong;
4301N/A jlong dxdylong, dydylong;
4301N/A jlong xbase, ybase;
4301N/A
4301N/A dxdxlong = DblToLong(itxInfo.dxdx);
4301N/A dydxlong = DblToLong(itxInfo.dydx);
4301N/A dxdylong = DblToLong(itxInfo.dxdy);
4301N/A dydylong = DblToLong(itxInfo.dydy);
4301N/A xbase = DblToLong(xorig);
4301N/A ybase = DblToLong(yorig);
4301N/A
4301N/A calculateEdges(pEdges, &dstInfo.bounds, &itxInfo,
4301N/A xbase, ybase, sx2-sx1, sy2-sy1);
0N/A
0N/A Region_StartIteration(env, &clipInfo);
0N/A while (Region_NextIteration(&clipInfo, &span)) {
0N/A jlong rowxlong, rowylong;
0N/A void *pDst;
0N/A
0N/A dy1 = span.y1;
0N/A dy2 = span.y2;
0N/A rowxlong = xbase + (dy1 - dstInfo.bounds.y1) * dxdylong;
0N/A rowylong = ybase + (dy1 - dstInfo.bounds.y1) * dydylong;
0N/A
0N/A while (dy1 < dy2) {
0N/A jlong xlong, ylong;
0N/A
0N/A /* Note - process at most one scanline at a time. */
0N/A
4301N/A dx1 = pEdges[(dy1 - dstInfo.bounds.y1) * 2 + 2];
4301N/A dx2 = pEdges[(dy1 - dstInfo.bounds.y1) * 2 + 3];
0N/A if (dx1 < span.x1) dx1 = span.x1;
0N/A if (dx2 > span.x2) dx2 = span.x2;
0N/A
0N/A /* All pixels from dx1 to dx2 have centers in bounds */
0N/A while (dx1 < dx2) {
0N/A /* Can process at most one buffer full at a time */
0N/A jint numpix = dx2 - dx1;
0N/A if (numpix > maxlinepix) {
0N/A numpix = maxlinepix;
0N/A }
0N/A
0N/A xlong =
0N/A rowxlong + ((dx1 - dstInfo.bounds.x1) * dxdxlong);
0N/A ylong =
0N/A rowylong + ((dx1 - dstInfo.bounds.x1) * dydxlong);
0N/A
0N/A /* Get IntArgbPre pixel data from source */
0N/A (*pHelperFunc)(&srcInfo,
0N/A rgb.data, numpix,
0N/A xlong, dxdxlong,
0N/A ylong, dydxlong);
0N/A
0N/A /* Interpolate result pixels if needed */
0N/A if (pInterpFunc) {
0N/A (*pInterpFunc)(rgb.data, numpix,
0N/A FractOfLong(xlong-LongOneHalf),
0N/A FractOfLong(dxdxlong),
0N/A FractOfLong(ylong-LongOneHalf),
0N/A FractOfLong(dydxlong));
0N/A }
0N/A
0N/A /* Store/Composite interpolated pixels into dest */
0N/A pDst = PtrCoord(dstInfo.rasBase,
0N/A dx1, dstInfo.pixelStride,
0N/A dy1, dstInfo.scanStride);
0N/A (*pMaskBlitPrim->funcs.maskblit)(pDst, rgb.data,
0N/A 0, 0, 0,
0N/A numpix, 1,
0N/A &dstInfo, &srcInfo,
0N/A pMaskBlitPrim,
0N/A &compInfo);
0N/A
0N/A /* Increment to next buffer worth of input pixels */
0N/A dx1 += maxlinepix;
0N/A }
0N/A
0N/A /* Increment to next scanline */
0N/A rowxlong += dxdylong;
0N/A rowylong += dydylong;
0N/A dy1++;
0N/A }
0N/A }
0N/A Region_EndIteration(env, &clipInfo);
0N/A }
0N/A SurfaceData_InvokeRelease(env, dstOps, &dstInfo);
0N/A SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
4301N/A } else {
4301N/A pEdges[0] = pEdges[1] = 0;
0N/A }
4434N/A if (!JNU_IsNull(env, edgeArray)) {
4434N/A (*env)->ReleasePrimitiveArrayCritical(env, edgeArray, pEdges, 0);
4434N/A } else if (pEdges != edgebuf) {
4434N/A free(pEdges);
4434N/A }
0N/A SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
0N/A SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
0N/A}
0N/A
4301N/Astatic void
4301N/ATransform_SafeHelper(JNIEnv *env,
4301N/A SurfaceDataOps *srcOps,
4301N/A SurfaceDataOps *dstOps,
4301N/A SurfaceDataRasInfo *pSrcInfo,
4301N/A SurfaceDataRasInfo *pDstInfo,
4301N/A NativePrimitive *pMaskBlitPrim,
4301N/A CompositeInfo *pCompInfo,
4301N/A TransformHelperFunc *pHelperFunc,
4301N/A TransformInterpFunc *pInterpFunc,
4301N/A RegionData *pClipInfo, TransformInfo *pItxInfo,
4301N/A jint *pData, jint *pEdges,
4301N/A jint dxoff, jint dyoff, jint sw, jint sh)
4301N/A{
4301N/A SurfaceDataBounds span;
4301N/A jint dx1, dx2;
4301N/A jint dy1, dy2;
4301N/A jint i, iy;
4301N/A
4301N/A dy1 = pDstInfo->bounds.y1;
4301N/A dy2 = pDstInfo->bounds.y2;
4301N/A dx1 = pDstInfo->bounds.x1;
4301N/A dx2 = pDstInfo->bounds.x2;
4301N/A pEdges[0] = dy1;
4301N/A pEdges[1] = dy2;
4301N/A for (iy = dy1; iy < dy2; iy++) {
4301N/A jint i = (iy - dy1) * 2;
4301N/A /* row spans are set to max,min until we find a pixel in range below */
4301N/A pEdges[i + 2] = dx2;
4301N/A pEdges[i + 3] = dx1;
4301N/A }
4301N/A
4301N/A Region_StartIteration(env, pClipInfo);
4301N/A while (Region_NextIteration(pClipInfo, &span)) {
4301N/A dy1 = span.y1;
4301N/A dy2 = span.y2;
4301N/A while (dy1 < dy2) {
4301N/A dx1 = span.x1;
4301N/A dx2 = span.x2;
4301N/A i = (dy1 - pDstInfo->bounds.y1) * 2;
4301N/A while (dx1 < dx2) {
4301N/A jdouble x, y;
4301N/A jlong xlong, ylong;
4301N/A
4301N/A x = dxoff + dx1 + 0.5;
4301N/A y = dyoff + dy1 + 0.5;
4301N/A Transform_transform(pItxInfo, &x, &y);
4301N/A xlong = DblToLong(x);
4301N/A ylong = DblToLong(y);
4301N/A
4301N/A /* Process only pixels with centers in bounds
4301N/A * Test double values to avoid overflow in conversion
4301N/A * to long values and then also test the long values
4301N/A * in case they rounded up and out of bounds during
4301N/A * the conversion.
4301N/A */
4301N/A if (x >= 0 && y >= 0 && x < sw && y < sh &&
4301N/A WholeOfLong(xlong) < sw &&
4301N/A WholeOfLong(ylong) < sh)
4301N/A {
4301N/A void *pDst;
4301N/A
4301N/A if (pEdges[i + 2] > dx1) {
4301N/A pEdges[i + 2] = dx1;
4301N/A }
4301N/A if (pEdges[i + 3] <= dx1) {
4301N/A pEdges[i + 3] = dx1 + 1;
4301N/A }
4301N/A
4301N/A /* Get IntArgbPre pixel data from source */
4301N/A (*pHelperFunc)(pSrcInfo,
4301N/A pData, 1,
4301N/A xlong, 0,
4301N/A ylong, 0);
4301N/A
4301N/A /* Interpolate result pixels if needed */
4301N/A if (pInterpFunc) {
4301N/A (*pInterpFunc)(pData, 1,
4301N/A FractOfLong(xlong-LongOneHalf), 0,
4301N/A FractOfLong(ylong-LongOneHalf), 0);
4301N/A }
4301N/A
4301N/A /* Store/Composite interpolated pixels into dest */
4301N/A pDst = PtrCoord(pDstInfo->rasBase,
4301N/A dx1, pDstInfo->pixelStride,
4301N/A dy1, pDstInfo->scanStride);
4301N/A (*pMaskBlitPrim->funcs.maskblit)(pDst, pData,
4301N/A 0, 0, 0,
4301N/A 1, 1,
4301N/A pDstInfo, pSrcInfo,
4301N/A pMaskBlitPrim,
4301N/A pCompInfo);
4301N/A }
4301N/A
4301N/A /* Increment to next input pixel */
4301N/A dx1++;
4301N/A }
4301N/A
4301N/A /* Increment to next scanline */
4301N/A dy1++;
4301N/A }
4301N/A }
4301N/A Region_EndIteration(env, pClipInfo);
4301N/A}
4301N/A
0N/A#define BL_INTERP_V1_to_V2_by_F(v1, v2, f) \
0N/A (((v1)<<8) + ((v2)-(v1))*(f))
0N/A
0N/A#define BL_ACCUM(comp) \
0N/A do { \
0N/A jint c1 = ((jubyte *) pRGB)[comp]; \
0N/A jint c2 = ((jubyte *) pRGB)[comp+4]; \
0N/A jint cR = BL_INTERP_V1_to_V2_by_F(c1, c2, xfactor); \
0N/A c1 = ((jubyte *) pRGB)[comp+8]; \
0N/A c2 = ((jubyte *) pRGB)[comp+12]; \
0N/A c2 = BL_INTERP_V1_to_V2_by_F(c1, c2, xfactor); \
0N/A cR = BL_INTERP_V1_to_V2_by_F(cR, c2, yfactor); \
0N/A ((jubyte *)pRes)[comp] = (jubyte) ((cR + (1<<15)) >> 16); \
0N/A } while (0)
0N/A
0N/Astatic void
0N/ABilinearInterp(jint *pRGB, jint numpix,
0N/A jint xfract, jint dxfract,
0N/A jint yfract, jint dyfract)
0N/A{
0N/A jint j;
0N/A jint *pRes = pRGB;
0N/A
0N/A for (j = 0; j < numpix; j++) {
0N/A jint xfactor;
0N/A jint yfactor;
0N/A xfactor = URShift(xfract, 32-8);
0N/A yfactor = URShift(yfract, 32-8);
0N/A BL_ACCUM(0);
0N/A BL_ACCUM(1);
0N/A BL_ACCUM(2);
0N/A BL_ACCUM(3);
0N/A pRes++;
0N/A pRGB += 4;
0N/A xfract += dxfract;
0N/A yfract += dyfract;
0N/A }
0N/A}
0N/A
0N/A#define SAT(val, max) \
0N/A do { \
0N/A val &= ~(val >> 31); /* negatives become 0 */ \
0N/A val -= max; /* only overflows are now positive */ \
0N/A val &= (val >> 31); /* positives become 0 */ \
0N/A val += max; /* range is now [0 -> max] */ \
0N/A } while (0)
0N/A
0N/A#ifdef __sparc
0N/A/* For sparc, floating point multiplies are faster than integer */
0N/A#define BICUBIC_USE_DBL_LUT
0N/A#else
0N/A/* For x86, integer multiplies are faster than floating point */
0N/A/* Note that on x86 Linux the choice of best algorithm varies
0N/A * depending on the compiler optimization and the processor type.
0N/A * Currently, the sun/awt x86 Linux builds are not optimized so
0N/A * all the variations produce mediocre performance.
0N/A * For now we will use the choice that works best for the Windows
0N/A * build until the (lack of) optimization issues on Linux are resolved.
0N/A */
0N/A#define BICUBIC_USE_INT_MATH
0N/A#endif
0N/A
0N/A#ifdef BICUBIC_USE_DBL_CAST
0N/A
0N/A#define BC_DblToCoeff(v) (v)
0N/A#define BC_COEFF_ONE 1.0
0N/A#define BC_TYPE jdouble
0N/A#define BC_V_HALF 0.5
0N/A#define BC_CompToV(v) ((jdouble) (v))
0N/A#define BC_STORE_COMPS(pRes) \
0N/A do { \
0N/A jint a = (jint) accumA; \
0N/A jint r = (jint) accumR; \
0N/A jint g = (jint) accumG; \
0N/A jint b = (jint) accumB; \
0N/A SAT(a, 255); \
0N/A SAT(r, a); \
0N/A SAT(g, a); \
0N/A SAT(b, a); \
0N/A *pRes = ((a << 24) | (r << 16) | (g << 8) | (b)); \
0N/A } while (0)
0N/A
0N/A#endif /* BICUBIC_USE_DBL_CAST */
0N/A
0N/A#ifdef BICUBIC_USE_DBL_LUT
0N/A
0N/A#define ItoD1(v) ((jdouble) (v))
0N/A#define ItoD4(v) ItoD1(v), ItoD1(v+1), ItoD1(v+2), ItoD1(v+3)
0N/A#define ItoD16(v) ItoD4(v), ItoD4(v+4), ItoD4(v+8), ItoD4(v+12)
0N/A#define ItoD64(v) ItoD16(v), ItoD16(v+16), ItoD16(v+32), ItoD16(v+48)
0N/A
0N/Astatic jdouble ItoD_table[] = {
0N/A ItoD64(0), ItoD64(64), ItoD64(128), ItoD64(192)
0N/A};
0N/A
0N/A#define BC_DblToCoeff(v) (v)
0N/A#define BC_COEFF_ONE 1.0
0N/A#define BC_TYPE jdouble
0N/A#define BC_V_HALF 0.5
0N/A#define BC_CompToV(v) ItoD_table[v]
0N/A#define BC_STORE_COMPS(pRes) \
0N/A do { \
0N/A jint a = (jint) accumA; \
0N/A jint r = (jint) accumR; \
0N/A jint g = (jint) accumG; \
0N/A jint b = (jint) accumB; \
0N/A SAT(a, 255); \
0N/A SAT(r, a); \
0N/A SAT(g, a); \
0N/A SAT(b, a); \
0N/A *pRes = ((a << 24) | (r << 16) | (g << 8) | (b)); \
0N/A } while (0)
0N/A
0N/A#endif /* BICUBIC_USE_DBL_LUT */
0N/A
0N/A#ifdef BICUBIC_USE_INT_MATH
0N/A
0N/A#define BC_DblToCoeff(v) ((jint) ((v) * 256))
0N/A#define BC_COEFF_ONE 256
0N/A#define BC_TYPE jint
0N/A#define BC_V_HALF (1 << 15)
0N/A#define BC_CompToV(v) ((jint) v)
0N/A#define BC_STORE_COMPS(pRes) \
0N/A do { \
0N/A accumA >>= 16; \
0N/A accumR >>= 16; \
0N/A accumG >>= 16; \
0N/A accumB >>= 16; \
0N/A SAT(accumA, 255); \
0N/A SAT(accumR, accumA); \
0N/A SAT(accumG, accumA); \
0N/A SAT(accumB, accumA); \
0N/A *pRes = ((accumA << 24) | (accumR << 16) | (accumG << 8) | (accumB)); \
0N/A } while (0)
0N/A
0N/A#endif /* BICUBIC_USE_INT_MATH */
0N/A
0N/A#define BC_ACCUM(index, ycindex, xcindex) \
0N/A do { \
0N/A BC_TYPE factor = bicubic_coeff[xcindex] * bicubic_coeff[ycindex]; \
0N/A int rgb; \
0N/A rgb = pRGB[index]; \
0N/A accumB += BC_CompToV((rgb >> 0) & 0xff) * factor; \
0N/A accumG += BC_CompToV((rgb >> 8) & 0xff) * factor; \
0N/A accumR += BC_CompToV((rgb >> 16) & 0xff) * factor; \
0N/A accumA += BC_CompToV((rgb >> 24) & 0xff) * factor; \
0N/A } while (0)
0N/A
0N/Astatic BC_TYPE bicubic_coeff[513];
0N/Astatic jboolean bicubictableinited;
0N/A
0N/Astatic void
0N/Ainit_bicubic_table(jdouble A)
0N/A{
0N/A /*
0N/A * The following formulas are designed to give smooth
0N/A * results when 'A' is -0.5 or -1.0.
0N/A */
0N/A int i;
0N/A for (i = 0; i < 256; i++) {
0N/A /* r(x) = (A + 2)|x|^3 - (A + 3)|x|^2 + 1 , 0 <= |x| < 1 */
0N/A jdouble x = i / 256.0;
0N/A x = ((A+2)*x - (A+3))*x*x + 1;
0N/A bicubic_coeff[i] = BC_DblToCoeff(x);
0N/A }
0N/A
0N/A for (; i < 384; i++) {
0N/A /* r(x) = A|x|^3 - 5A|x|^2 + 8A|x| - 4A , 1 <= |x| < 2 */
0N/A jdouble x = i / 256.0;
0N/A x = ((A*x - 5*A)*x + 8*A)*x - 4*A;
0N/A bicubic_coeff[i] = BC_DblToCoeff(x);
0N/A }
0N/A
0N/A bicubic_coeff[384] = (BC_COEFF_ONE - bicubic_coeff[128]*2) / 2;
0N/A
0N/A for (i++; i <= 512; i++) {
0N/A bicubic_coeff[i] = BC_COEFF_ONE - (bicubic_coeff[512-i] +
0N/A bicubic_coeff[i-256] +
0N/A bicubic_coeff[768-i]);
0N/A }
0N/A
0N/A bicubictableinited = JNI_TRUE;
0N/A}
0N/A
0N/Astatic void
0N/ABicubicInterp(jint *pRGB, jint numpix,
0N/A jint xfract, jint dxfract,
0N/A jint yfract, jint dyfract)
0N/A{
0N/A jint i;
0N/A jint *pRes = pRGB;
0N/A
0N/A if (!bicubictableinited) {
0N/A init_bicubic_table(-0.5);
0N/A }
0N/A
0N/A for (i = 0; i < numpix; i++) {
0N/A BC_TYPE accumA, accumR, accumG, accumB;
0N/A jint xfactor, yfactor;
0N/A
0N/A xfactor = URShift(xfract, 32-8);
0N/A yfactor = URShift(yfract, 32-8);
0N/A accumA = accumR = accumG = accumB = BC_V_HALF;
0N/A BC_ACCUM(0, yfactor+256, xfactor+256);
0N/A BC_ACCUM(1, yfactor+256, xfactor+ 0);
0N/A BC_ACCUM(2, yfactor+256, 256-xfactor);
0N/A BC_ACCUM(3, yfactor+256, 512-xfactor);
0N/A BC_ACCUM(4, yfactor+ 0, xfactor+256);
0N/A BC_ACCUM(5, yfactor+ 0, xfactor+ 0);
0N/A BC_ACCUM(6, yfactor+ 0, 256-xfactor);
0N/A BC_ACCUM(7, yfactor+ 0, 512-xfactor);
0N/A BC_ACCUM(8, 256-yfactor, xfactor+256);
0N/A BC_ACCUM(9, 256-yfactor, xfactor+ 0);
0N/A BC_ACCUM(10, 256-yfactor, 256-xfactor);
0N/A BC_ACCUM(11, 256-yfactor, 512-xfactor);
0N/A BC_ACCUM(12, 512-yfactor, xfactor+256);
0N/A BC_ACCUM(13, 512-yfactor, xfactor+ 0);
0N/A BC_ACCUM(14, 512-yfactor, 256-xfactor);
0N/A BC_ACCUM(15, 512-yfactor, 512-xfactor);
0N/A BC_STORE_COMPS(pRes);
0N/A pRes++;
0N/A pRGB += 16;
0N/A xfract += dxfract;
0N/A yfract += dyfract;
0N/A }
0N/A}
0N/A
0N/A#ifdef MAKE_STUBS
0N/A
0N/Astatic void
0N/ABilinearInterpStub(jint *pRGBbase, jint numpix,
0N/A jint xfract, jint dxfract,
0N/A jint yfract, jint dyfract)
0N/A{
0N/A jint *pRGB = pRGBbase;
0N/A while (--numpix >= 0) {
0N/A *pRGBbase = *pRGB;
0N/A pRGBbase += 1;
0N/A pRGB += 4;
0N/A }
0N/A}
0N/A
0N/Astatic void
0N/ABicubicInterpStub(jint *pRGBbase, jint numpix,
0N/A jint xfract, jint dxfract,
0N/A jint yfract, jint dyfract)
0N/A{
0N/A jint *pRGB = pRGBbase+5;
0N/A while (--numpix >= 0) {
0N/A *pRGBbase = *pRGB;
0N/A pRGBbase += 1;
0N/A pRGB += 16;
0N/A }
0N/A}
0N/A
0N/A#endif /* MAKE_STUBS */