alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (ScaledBlit.c)

This example Java source code file (ScaledBlit.c) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

java_sun_java2d_loops_scaledblit_scale, jni_false, jni_true, null, region_isrectangular, region_nextiteration, sd_success, srcloc, surfacedata_getops, surfacedata_invokerelease, surfacedata_invokeunlock, surfacedataops, surfacedatarasinfo, tilestart

The ScaledBlit.c Java example source code

/*
 * 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 <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<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<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);
}

Other Java examples (source code examples)

Here is a short list of links related to this Java ScaledBlit.c source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.