/*
 * Class:     sun_java2d_loops_MaskBlit
 * Method:    MaskBlit
 * Signature: (Lsun/java2d/SurfaceData;Lsun/java2d/SurfaceData;Ljava/awt/Composite;IIIIII[BII)V
 */
JNIEXPORT void JNICALL
Java_sun_java2d_loops_MaskBlit_MaskBlit
    (JNIEnv *env, jobject self,
     jobject srcData, jobject dstData, jobject comp, jobject clip,
     jint srcx, jint srcy, jint dstx, jint dsty, jint width, jint height,
     jbyteArray maskArray, jint maskoff, jint maskscan)
{
    SurfaceDataOps *srcOps;
    SurfaceDataOps *dstOps;
    SurfaceDataRasInfo srcInfo;
    SurfaceDataRasInfo dstInfo;
    NativePrimitive *pPrim;
    CompositeInfo compInfo;
    RegionData clipInfo;

    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;
    }

    srcInfo.bounds.x1 = srcx;
    srcInfo.bounds.y1 = srcy;
    srcInfo.bounds.x2 = srcx + width;
    srcInfo.bounds.y2 = srcy + height;
    dstInfo.bounds.x1 = dstx;
    dstInfo.bounds.y1 = dsty;
    dstInfo.bounds.x2 = dstx + width;
    dstInfo.bounds.y2 = dsty + height;
    srcx -= dstx;
    srcy -= dsty;
    SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds);
    if (srcOps->Lock(env, srcOps, &srcInfo, pPrim->srcflags) != SD_SUCCESS) {
        return;
    }
    if (dstOps->Lock(env, dstOps, &dstInfo, pPrim->dstflags) != SD_SUCCESS) {
        SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
        return;
    }
    SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds,
                                    srcx, srcy);
    Region_IntersectBounds(&clipInfo, &dstInfo.bounds);

    if (!Region_IsEmpty(&clipInfo)) {
        srcOps->GetRasInfo(env, srcOps, &srcInfo);
        dstOps->GetRasInfo(env, dstOps, &dstInfo);
        if (srcInfo.rasBase && dstInfo.rasBase) {
            SurfaceDataBounds span;
            unsigned char *pMask =
                (maskArray
                 ? (*env)->GetPrimitiveArrayCritical(env, maskArray, 0)
                 : 0);
            jint savesx = srcInfo.bounds.x1;
            jint savedx = dstInfo.bounds.x1;
            Region_StartIteration(env, &clipInfo);
            while (Region_NextIteration(&clipInfo, &span)) {
                void *pSrc = PtrCoord(srcInfo.rasBase,
                                      srcx + span.x1, srcInfo.pixelStride,
                                      srcy + span.y1, srcInfo.scanStride);
                void *pDst = PtrCoord(dstInfo.rasBase,
                                      span.x1, dstInfo.pixelStride,
                                      span.y1, dstInfo.scanStride);
                maskoff += ((span.y1 - dsty) * maskscan + (span.x1 - dstx));
                /*
                 * Fix for 4804375
                 * REMIND: There should probably be a better
                 * way to give the span coordinates to the
                 * inner loop.  This is only really needed
                 * for the 1, 2, and 4 bit loops.
                 */
                srcInfo.bounds.x1 = srcx + span.x1;
                dstInfo.bounds.x1 = span.x1;
                (*pPrim->funcs.maskblit)(pDst, pSrc,
                                         pMask, maskoff, maskscan,
                                         span.x2 - span.x1, span.y2 - span.y1,
                                         &dstInfo, &srcInfo,
                                         pPrim, &compInfo);
            }
            Region_EndIteration(env, &clipInfo);
            if (pMask) {
                (*env)->ReleasePrimitiveArrayCritical(env, maskArray,
                                                      pMask, JNI_ABORT);
            }
            srcInfo.bounds.x1 = savesx;
            dstInfo.bounds.x1 = savedx;
        }
        SurfaceData_InvokeRelease(env, dstOps, &dstInfo);
        SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
    }
    SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
    SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
}
示例#2
0
JNIEXPORT void JNICALL
Java_sun_awt_X11PMBlitLoops_Blit
    (JNIEnv *env, jobject joSelf,
     jobject srcData, jobject dstData,
     jobject composite, jobject clip,
     jint srcx, jint srcy,
     jint dstx, jint dsty,
     jint width, jint height)
{
#ifndef HEADLESS
    X11SDOps *srcXsdo, *dstXsdo;
    SurfaceDataBounds span;
    RegionData clipInfo;
    GC xgc;

    if (width <= 0 || height <= 0) {
	return;
    }

    srcXsdo = X11SurfaceData_GetOps(env, srcData);
    if (srcXsdo == NULL) {
	return;
    }
    dstXsdo = X11SurfaceData_GetOps(env, dstData);
    if (dstXsdo == NULL) {
	return;
    }
    if (Region_GetInfo(env, clip, &clipInfo)) {
	return;
    }

    xgc = dstXsdo->GetGC(env, dstXsdo, NULL, NULL, dstXsdo->lastpixel);
    if (xgc == NULL) {
	return;
    }

#ifdef MITSHM
    if (srcXsdo->isPixmap) {
	X11SD_UnPuntPixmap(srcXsdo);
    }
#endif /* MITSHM */

    if (srcXsdo->bitmask != 0) {
	XSetClipOrigin(awt_display, xgc, dstx - srcx, dsty - srcy);
	XSetClipMask(awt_display, xgc, srcXsdo->bitmask);
    }
    span.x1 = dstx;
    span.y1 = dsty;
    span.x2 = dstx + width;
    span.y2 = dsty + height;
    Region_IntersectBounds(&clipInfo, &span);
    if (!Region_IsEmpty(&clipInfo)) {
	Region_StartIteration(env, &clipInfo);
	srcx -= dstx;
	srcy -= dsty;
	while (Region_NextIteration(&clipInfo, &span)) {
	    XCopyArea(awt_display, srcXsdo->drawable, dstXsdo->drawable, xgc,
		      srcx + span.x1, srcy + span.y1,
		      span.x2 - span.x1, span.y2 - span.y1,
		      span.x1, span.y1);
	}
	Region_EndIteration(env, &clipInfo);
    }

    if (srcXsdo->bitmask != 0) {
	XSetClipMask(awt_display, xgc, None);
    }

#ifdef MITSHM
    if (srcXsdo->shmPMData.usingShmPixmap) { 
	srcXsdo->shmPMData.xRequestSent = JNI_TRUE;
    }
#endif /* MITSHM */
    dstXsdo->ReleaseGC(env, dstXsdo, xgc);
#endif /* !HEADLESS */
}
示例#3
0
/*
 * Class:     sun_java2d_loops_TransformHelper
 * Method:    Transform
 * 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
 */
JNIEXPORT void JNICALL
Java_sun_java2d_loops_TransformHelper_Transform
    (JNIEnv *env, jobject self,
     jobject maskblit,
     jobject srcData, jobject dstData,
     jobject comp, jobject clip,
     jobject itxform, jint txtype,
     jint sx1, jint sy1, jint sx2, jint sy2,
     jint dx1, jint dy1, jint dx2, jint dy2,
     jintArray edgeArray, jint dxoff, jint dyoff)
{
    SurfaceDataOps *srcOps;
    SurfaceDataOps *dstOps;
    SurfaceDataRasInfo srcInfo;
    SurfaceDataRasInfo dstInfo;
    NativePrimitive *pHelperPrim;
    NativePrimitive *pMaskBlitPrim;
    CompositeInfo compInfo;
    RegionData clipInfo;
    TransformInfo itxInfo;
    jint maxlinepix;
    TransformHelperFunc *pHelperFunc;
    TransformInterpFunc *pInterpFunc;
    jdouble xorig, yorig;
    jlong numedges;
    jint *pEdges;
    jint edgebuf[2 + MAXEDGES * 2];
    union {
        jlong align;
        jint data[LINE_SIZE];
    } rgb;

#ifdef MAKE_STUBS
    static int th_initialized;

    /* For debugging only - used to swap in alternate funcs for perf testing */
    if (!th_initialized) {
        if (getenv("TXSTUB") != 0) {
            pBilinearFunc = BilinearInterpStub;
            pBicubicFunc = BicubicInterpStub;
        } else if (getenv("TXNOVIS") != 0) {
            pBilinearFunc = BilinearInterp;
            pBicubicFunc = BicubicInterp;
        }
        th_initialized = 1;
    }
#endif /* MAKE_STUBS */

    pHelperPrim = GetNativePrim(env, self);
    if (pHelperPrim == NULL) {
        /* Should never happen... */
        return;
    }
    pMaskBlitPrim = GetNativePrim(env, maskblit);
    if (pMaskBlitPrim == NULL) {
        /* Exception was thrown by GetNativePrim */
        return;
    }
    if (pMaskBlitPrim->pCompType->getCompInfo != NULL) {
        (*pMaskBlitPrim->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;
    }

    /*
     * Grab the appropriate pointer to the helper and interpolation
     * routines and calculate the maximum number of destination pixels
     * that can be processed in one intermediate buffer based on the
     * size of the buffer and the number of samples needed per pixel.
     */
    switch (txtype) {
    case java_awt_image_AffineTransformOp_TYPE_NEAREST_NEIGHBOR:
        pHelperFunc = pHelperPrim->funcs.transformhelpers->nnHelper;
        pInterpFunc = NULL;
        maxlinepix = LINE_SIZE;
        break;
    case java_awt_image_AffineTransformOp_TYPE_BILINEAR:
        pHelperFunc = pHelperPrim->funcs.transformhelpers->blHelper;
        pInterpFunc = pBilinearFunc;
        maxlinepix = LINE_SIZE / 4;
        break;
    case java_awt_image_AffineTransformOp_TYPE_BICUBIC:
        pHelperFunc = pHelperPrim->funcs.transformhelpers->bcHelper;
        pInterpFunc = pBicubicFunc;
        maxlinepix = LINE_SIZE / 16;
        break;
    }

    srcInfo.bounds.x1 = sx1;
    srcInfo.bounds.y1 = sy1;
    srcInfo.bounds.x2 = sx2;
    srcInfo.bounds.y2 = sy2;
    dstInfo.bounds.x1 = dx1;
    dstInfo.bounds.y1 = dy1;
    dstInfo.bounds.x2 = dx2;
    dstInfo.bounds.y2 = dy2;
    SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds);
    if (srcOps->Lock(env, srcOps, &srcInfo, pHelperPrim->srcflags)
        != SD_SUCCESS)
    {
        /* edgeArray should already contain zeros for min/maxy */
        return;
    }
    if (dstOps->Lock(env, dstOps, &dstInfo, pMaskBlitPrim->dstflags)
        != SD_SUCCESS)
    {
        SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
        /* edgeArray should already contain zeros for min/maxy */
        return;
    }
    Region_IntersectBounds(&clipInfo, &dstInfo.bounds);

    numedges = (((jlong) dstInfo.bounds.y2) - ((jlong) dstInfo.bounds.y1));
    if (numedges <= 0) {
        pEdges = NULL;
    } else if (!JNU_IsNull(env, edgeArray)) {
        /*
         * Ideally Java should allocate an array large enough, but if
         * we ever have a miscommunication about the number of edge
         * lines, or if the Java array calculation should overflow to
         * a positive number and succeed in allocating an array that
         * is too small, we need to verify that it can still hold the
         * number of integers that we plan to store to be safe.
         */
        jsize edgesize = (*env)->GetArrayLength(env, edgeArray);
        /* (edgesize/2 - 1) should avoid any overflow or underflow. */
        pEdges = (((edgesize / 2) - 1) >= numedges)
            ? (*env)->GetPrimitiveArrayCritical(env, edgeArray, NULL)
            : NULL;
    } else if (numedges > MAXEDGES) {
        /* numedges variable (jlong) can be at most ((1<<32)-1) */
        /* memsize can overflow a jint, but not a jlong */
        jlong memsize = ((numedges * 2) + 2) * sizeof(*pEdges);
        pEdges = (memsize == ((size_t) memsize))
            ? malloc((size_t) memsize)
            : NULL;
    } else {
        pEdges = edgebuf;
    }

    if (pEdges == NULL) {
        if (numedges > 0) {
            JNU_ThrowInternalError(env, "Unable to allocate edge list");
        }
        SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
        SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
        /* edgeArray should already contain zeros for min/maxy */
        return;
    }

    Transform_GetInfo(env, itxform, &itxInfo);

    if (!Region_IsEmpty(&clipInfo)) {
        srcOps->GetRasInfo(env, srcOps, &srcInfo);
        dstOps->GetRasInfo(env, dstOps, &dstInfo);
        if (srcInfo.rasBase == NULL || dstInfo.rasBase == NULL) {
            pEdges[0] = pEdges[1] = 0;
        } else if (checkOverflow(dxoff, dyoff, &dstInfo.bounds,
                                 &itxInfo, &xorig, &yorig))
        {
            Transform_SafeHelper(env, srcOps, dstOps,
                                 &srcInfo, &dstInfo,
                                 pMaskBlitPrim, &compInfo,
                                 pHelperFunc, pInterpFunc,
                                 &clipInfo, &itxInfo, rgb.data, pEdges,
                                 dxoff, dyoff, sx2-sx1, sy2-sy1);
        } else {
            SurfaceDataBounds span;
            jlong dxdxlong, dydxlong;
            jlong dxdylong, dydylong;
            jlong xbase, ybase;

            dxdxlong = DblToLong(itxInfo.dxdx);
            dydxlong = DblToLong(itxInfo.dydx);
            dxdylong = DblToLong(itxInfo.dxdy);
            dydylong = DblToLong(itxInfo.dydy);
            xbase = DblToLong(xorig);
            ybase = DblToLong(yorig);

            calculateEdges(pEdges, &dstInfo.bounds, &itxInfo,
                           xbase, ybase, sx2-sx1, sy2-sy1);

            Region_StartIteration(env, &clipInfo);
            while (Region_NextIteration(&clipInfo, &span)) {
                jlong rowxlong, rowylong;
                void *pDst;

                dy1 = span.y1;
                dy2 = span.y2;
                rowxlong = xbase + (dy1 - dstInfo.bounds.y1) * dxdylong;
                rowylong = ybase + (dy1 - dstInfo.bounds.y1) * dydylong;

                while (dy1 < dy2) {
                    jlong xlong, ylong;

                    /* Note - process at most one scanline at a time. */

                    dx1 = pEdges[(dy1 - dstInfo.bounds.y1) * 2 + 2];
                    dx2 = pEdges[(dy1 - dstInfo.bounds.y1) * 2 + 3];
                    if (dx1 < span.x1) dx1 = span.x1;
                    if (dx2 > span.x2) dx2 = span.x2;

                    /* All pixels from dx1 to dx2 have centers in bounds */
                    while (dx1 < dx2) {
                        /* Can process at most one buffer full at a time */
                        jint numpix = dx2 - dx1;
                        if (numpix > maxlinepix) {
                            numpix = maxlinepix;
                        }

                        xlong =
                            rowxlong + ((dx1 - dstInfo.bounds.x1) * dxdxlong);
                        ylong =
                            rowylong + ((dx1 - dstInfo.bounds.x1) * dydxlong);

                        /* Get IntArgbPre pixel data from source */
                        (*pHelperFunc)(&srcInfo,
                                       rgb.data, numpix,
                                       xlong, dxdxlong,
                                       ylong, dydxlong);

                        /* Interpolate result pixels if needed */
                        if (pInterpFunc) {
                            (*pInterpFunc)(rgb.data, numpix,
                                           FractOfLong(xlong-LongOneHalf),
                                           FractOfLong(dxdxlong),
                                           FractOfLong(ylong-LongOneHalf),
                                           FractOfLong(dydxlong));
                        }

                        /* Store/Composite interpolated pixels into dest */
                        pDst = PtrCoord(dstInfo.rasBase,
                                        dx1, dstInfo.pixelStride,
                                        dy1, dstInfo.scanStride);
                        (*pMaskBlitPrim->funcs.maskblit)(pDst, rgb.data,
                                                         0, 0, 0,
                                                         numpix, 1,
                                                         &dstInfo, &srcInfo,
                                                         pMaskBlitPrim,
                                                         &compInfo);

                        /* Increment to next buffer worth of input pixels */
                        dx1 += maxlinepix;
                    }

                    /* Increment to next scanline */
                    rowxlong += dxdylong;
                    rowylong += dydylong;
                    dy1++;
                }
            }
            Region_EndIteration(env, &clipInfo);
        }
        SurfaceData_InvokeRelease(env, dstOps, &dstInfo);
        SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
    } else {
        pEdges[0] = pEdges[1] = 0;
    }

    if (!JNU_IsNull(env, edgeArray)) {
        (*env)->ReleasePrimitiveArrayCritical(env, edgeArray, pEdges, 0);
    } else if (pEdges != edgebuf) {
        free(pEdges);
    }
    SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
    SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
}
示例#4
0
/*
 * Class:     sun_java2d_loops_Blit
 * Method:    Blit
 * Signature: (Lsun/java2d/SurfaceData;Lsun/java2d/SurfaceData;Ljava/awt/Composite;IIIIII)V
 */
JNIEXPORT void JNICALL
Java_sun_java2d_loops_Blit_Blit
    (JNIEnv *env, jobject self,
     jobject srcData, jobject dstData, jobject comp, jobject clip,
     jint srcx, jint srcy, jint dstx, jint dsty, jint width, jint height)
{
    SurfaceDataOps *srcOps;
    SurfaceDataOps *dstOps;
    SurfaceDataRasInfo srcInfo;
    SurfaceDataRasInfo dstInfo;
    NativePrimitive *pPrim;
    CompositeInfo compInfo;
    RegionData clipInfo;
    jint dstFlags;

    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;
    }

    srcInfo.bounds.x1 = srcx;
    srcInfo.bounds.y1 = srcy;
    srcInfo.bounds.x2 = srcx + width;
    srcInfo.bounds.y2 = srcy + height;
    dstInfo.bounds.x1 = dstx;
    dstInfo.bounds.y1 = dsty;
    dstInfo.bounds.x2 = dstx + width;
    dstInfo.bounds.y2 = dsty + height;
    srcx -= dstx;
    srcy -= dsty;
    SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds);
    if (srcOps->Lock(env, srcOps, &srcInfo, pPrim->srcflags) != SD_SUCCESS) {
	return;
    }

    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;
    }
    SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds,
				    srcx, srcy);
    Region_IntersectBounds(&clipInfo, &dstInfo.bounds);

    if (!Region_IsEmpty(&clipInfo)) {
	srcOps->GetRasInfo(env, srcOps, &srcInfo);
	dstOps->GetRasInfo(env, dstOps, &dstInfo);
	if (srcInfo.rasBase && dstInfo.rasBase) {
	    SurfaceDataBounds span;
	    jint savesx = srcInfo.bounds.x1;
	    jint savedx = dstInfo.bounds.x1;
	    Region_StartIteration(env, &clipInfo);
	    while (Region_NextIteration(&clipInfo, &span)) {
		void *pSrc = PtrCoord(srcInfo.rasBase,
				      srcx + span.x1, srcInfo.pixelStride,
				      srcy + span.y1, srcInfo.scanStride);
		void *pDst = PtrCoord(dstInfo.rasBase,
				      span.x1, dstInfo.pixelStride,
				      span.y1, dstInfo.scanStride);
		/*
		 * Fix for 4804375
		 * REMIND: There should probably be a better
		 * way to give the span coordinates to the
		 * inner loop.  This is only really needed
		 * for the 1, 2, and 4 bit loops.
		 */
		srcInfo.bounds.x1 = srcx + span.x1;
		dstInfo.bounds.x1 = span.x1;
		(*pPrim->funcs.blit)(pSrc, pDst,
				     span.x2 - span.x1, span.y2 - span.y1,
				     &srcInfo, &dstInfo, pPrim, &compInfo);
	    }
	    Region_EndIteration(env, &clipInfo);
	    srcInfo.bounds.x1 = savesx;
	    dstInfo.bounds.x1 = savedx;
	}
	SurfaceData_InvokeRelease(env, dstOps, &dstInfo);
	SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
    }
    SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
    SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
}
示例#5
0
/*
 * Class:     sun_java2d_loops_TransformHelper
 * Method:    Transform
 * 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
 */
JNIEXPORT void JNICALL
Java_sun_java2d_loops_TransformHelper_Transform
    (JNIEnv *env, jobject self,
     jobject maskblit,
     jobject srcData, jobject dstData,
     jobject comp, jobject clip,
     jobject itxform, jint txtype,
     jint sx1, jint sy1, jint sx2, jint sy2,
     jint dx1, jint dy1, jint dx2, jint dy2,
     jintArray edgeArray, jint dxoff, jint dyoff)
{
    SurfaceDataOps *srcOps;
    SurfaceDataOps *dstOps;
    SurfaceDataRasInfo srcInfo;
    SurfaceDataRasInfo dstInfo;
    NativePrimitive *pHelperPrim;
    NativePrimitive *pMaskBlitPrim;
    CompositeInfo compInfo;
    RegionData clipInfo;
    TransformInfo itxInfo;
    jint maxlinepix;
    TransformHelperFunc *pHelperFunc;
    TransformInterpFunc *pInterpFunc;
    jint edgebuf[MAXEDGES * 2];
    jint *pEdges;
    jdouble x, y;
    jlong xbase, ybase;
    jlong dxdxlong, dydxlong;
    jlong dxdylong, dydylong;

#ifdef MAKE_STUBS
    static int th_initialized;

    /* For debugging only - used to swap in alternate funcs for perf testing */
    if (!th_initialized) {
        if (getenv("TXSTUB") != 0) {
            pBilinearFunc = BilinearInterpStub;
            pBicubicFunc = BicubicInterpStub;
        } else if (getenv("TXNOVIS") != 0) {
            pBilinearFunc = BilinearInterp;
            pBicubicFunc = BicubicInterp;
        }
        th_initialized = 1;
    }
#endif /* MAKE_STUBS */

    pHelperPrim = GetNativePrim(env, self);
    if (pHelperPrim == NULL) {
        /* Should never happen... */
        return;
    }
    pMaskBlitPrim = GetNativePrim(env, maskblit);
    if (pMaskBlitPrim == NULL) {
        /* Exception was thrown by GetNativePrim */
        return;
    }
    if (pMaskBlitPrim->pCompType->getCompInfo != NULL) {
        (*pMaskBlitPrim->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;
    }

    /*
     * Grab the appropriate pointer to the helper and interpolation
     * routines and calculate the maximum number of destination pixels
     * that can be processed in one intermediate buffer based on the
     * size of the buffer and the number of samples needed per pixel.
     */
    switch (txtype) {
    case java_awt_image_AffineTransformOp_TYPE_NEAREST_NEIGHBOR:
        pHelperFunc = pHelperPrim->funcs.transformhelpers->nnHelper;
        pInterpFunc = NULL;
        maxlinepix = LINE_SIZE;
        break;
    case java_awt_image_AffineTransformOp_TYPE_BILINEAR:
        pHelperFunc = pHelperPrim->funcs.transformhelpers->blHelper;
        pInterpFunc = pBilinearFunc;
        maxlinepix = LINE_SIZE / 4;
        break;
    case java_awt_image_AffineTransformOp_TYPE_BICUBIC:
        pHelperFunc = pHelperPrim->funcs.transformhelpers->bcHelper;
        pInterpFunc = pBicubicFunc;
        maxlinepix = LINE_SIZE / 16;
        break;
    }

    srcInfo.bounds.x1 = sx1;
    srcInfo.bounds.y1 = sy1;
    srcInfo.bounds.x2 = sx2;
    srcInfo.bounds.y2 = sy2;
    dstInfo.bounds.x1 = dx1;
    dstInfo.bounds.y1 = dy1;
    dstInfo.bounds.x2 = dx2;
    dstInfo.bounds.y2 = dy2;
    SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds);
    if (srcOps->Lock(env, srcOps, &srcInfo, pHelperPrim->srcflags)
        != SD_SUCCESS)
    {
        return;
    }
    if (dstOps->Lock(env, dstOps, &dstInfo, pMaskBlitPrim->dstflags)
        != SD_SUCCESS)
    {
        SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
        return;
    }
    Region_IntersectBounds(&clipInfo, &dstInfo.bounds);

    Transform_GetInfo(env, itxform, &itxInfo);
    dxdxlong = DblToLong(itxInfo.dxdx);
    dydxlong = DblToLong(itxInfo.dydx);
    dxdylong = DblToLong(itxInfo.dxdy);
    dydylong = DblToLong(itxInfo.dydy);
    x = dxoff+dstInfo.bounds.x1+0.5; /* Center of pixel x1 */
    y = dyoff+dstInfo.bounds.y1+0.5; /* Center of pixel y1 */
    Transform_transform(&itxInfo, &x, &y);
    xbase = DblToLong(x);
    ybase = DblToLong(y);

    pEdges = calculateEdges(edgebuf, &dstInfo.bounds, &itxInfo,
                            xbase, ybase, sx2-sx1, sy2-sy1);

    if (!Region_IsEmpty(&clipInfo)) {
        srcOps->GetRasInfo(env, srcOps, &srcInfo);
        dstOps->GetRasInfo(env, dstOps, &dstInfo);
        if (srcInfo.rasBase && dstInfo.rasBase) {
            union {
                jlong align;
                jint data[LINE_SIZE];
            } rgb;
            SurfaceDataBounds span;

            Region_StartIteration(env, &clipInfo);
            while (Region_NextIteration(&clipInfo, &span)) {
                jlong rowxlong, rowylong;
                void *pDst;

                dy1 = span.y1;
                dy2 = span.y2;
                rowxlong = xbase + (dy1 - dstInfo.bounds.y1) * dxdylong;
                rowylong = ybase + (dy1 - dstInfo.bounds.y1) * dydylong;

                while (dy1 < dy2) {
                    jlong xlong, ylong;

                    /* Note - process at most one scanline at a time. */

                    dx1 = pEdges[(dy1 - dstInfo.bounds.y1) * 2];
                    dx2 = pEdges[(dy1 - dstInfo.bounds.y1) * 2 + 1];
                    if (dx1 < span.x1) dx1 = span.x1;
                    if (dx2 > span.x2) dx2 = span.x2;

                    /* All pixels from dx1 to dx2 have centers in bounds */
                    while (dx1 < dx2) {
                        /* Can process at most one buffer full at a time */
                        jint numpix = dx2 - dx1;
                        if (numpix > maxlinepix) {
                            numpix = maxlinepix;
                        }

                        xlong =
                            rowxlong + ((dx1 - dstInfo.bounds.x1) * dxdxlong);
                        ylong =
                            rowylong + ((dx1 - dstInfo.bounds.x1) * dydxlong);

                        /* Get IntArgbPre pixel data from source */
                        (*pHelperFunc)(&srcInfo,
                                       rgb.data, numpix,
                                       xlong, dxdxlong,
                                       ylong, dydxlong);

                        /* Interpolate result pixels if needed */
                        if (pInterpFunc) {
                            (*pInterpFunc)(rgb.data, numpix,
                                           FractOfLong(xlong-LongOneHalf),
                                           FractOfLong(dxdxlong),
                                           FractOfLong(ylong-LongOneHalf),
                                           FractOfLong(dydxlong));
                        }

                        /* Store/Composite interpolated pixels into dest */
                        pDst = PtrCoord(dstInfo.rasBase,
                                        dx1, dstInfo.pixelStride,
                                        dy1, dstInfo.scanStride);
                        (*pMaskBlitPrim->funcs.maskblit)(pDst, rgb.data,
                                                         0, 0, 0,
                                                         numpix, 1,
                                                         &dstInfo, &srcInfo,
                                                         pMaskBlitPrim,
                                                         &compInfo);

                        /* Increment to next buffer worth of input pixels */
                        dx1 += maxlinepix;
                    }

                    /* Increment to next scanline */
                    rowxlong += dxdylong;
                    rowylong += dydylong;
                    dy1++;
                }
            }
            Region_EndIteration(env, &clipInfo);
        }
        SurfaceData_InvokeRelease(env, dstOps, &dstInfo);
        SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
    }
    SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
    SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
    if (!JNU_IsNull(env, edgeArray)) {
        (*env)->SetIntArrayRegion(env, edgeArray, 0, 1, &dstInfo.bounds.y1);
        (*env)->SetIntArrayRegion(env, edgeArray, 1, 1, &dstInfo.bounds.y2);
        (*env)->SetIntArrayRegion(env, edgeArray,
                                  2, (dstInfo.bounds.y2 - dstInfo.bounds.y1)*2,
                                  pEdges);
    }
    if (pEdges != edgebuf) {
        free(pEdges);
    }
}