jdk/src/share/native/sun/java2d/loops/DrawLine.c
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/native/sun/java2d/loops/DrawLine.c	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,431 @@
+/*
+ * Copyright 2000-2001 Sun Microsystems, Inc.  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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "GraphicsPrimitiveMgr.h"
+
+#include "LineUtils.h"
+
+#include "sun_java2d_loops_DrawLine.h"
+
+#define OUTCODE_TOP     1
+#define OUTCODE_BOTTOM  2
+#define OUTCODE_LEFT    4
+#define OUTCODE_RIGHT   8
+
+static void
+RefineBounds(SurfaceDataBounds *bounds, jint x1, jint y1, jint x2, jint y2)
+{
+    jint min, max;
+    if (x1 < x2) {
+        min = x1;
+        max = x2;
+    } else {
+        min = x2;
+        max = x1;
+    }
+    max++;
+    if (max <= min) {
+        /* integer overflow */
+        max--;
+    }
+    if (bounds->x1 < min) bounds->x1 = min;
+    if (bounds->x2 > max) bounds->x2 = max;
+    if (y1 < y2) {
+        min = y1;
+        max = y2;
+    } else {
+        min = y2;
+        max = y1;
+    }
+    max++;
+    if (max <= min) {
+        /* integer overflow */
+        max--;
+    }
+    if (bounds->y1 < min) bounds->y1 = min;
+    if (bounds->y2 > max) bounds->y2 = max;
+}
+
+#define _out(v, vmin, vmax, cmin, cmax) \
+    ((v < vmin) ? cmin : ((v > vmax) ? cmax : 0))
+
+#define outcode(x, y, xmin, ymin, xmax, ymax) \
+    (_out(y, ymin, ymax, OUTCODE_TOP, OUTCODE_BOTTOM) | \
+     _out(x, xmin, xmax, OUTCODE_LEFT, OUTCODE_RIGHT))
+
+/*
+ * "Small" math here will be done if the coordinates are less
+ * than 15 bits in range (-16384 => 16383).  This could be
+ * expanded to 16 bits if we rearrange some of the math in
+ * the normal version of SetupBresenham.
+ * "Big" math here will be done with coordinates with 30 bits
+ * of total range - 2 bits less than a jint holds.
+ * Intermediate calculations for "Big" coordinates will be
+ * done using jlong variables.
+ */
+#define OverflowsSmall(v)       ((v) != (((v) << 17) >> 17))
+#define OverflowsBig(v)         ((v) != (((v) << 2) >> 2))
+#define BIG_MAX                 ((1 << 29) - 1)
+#define BIG_MIN                 (-(1 << 29))
+
+#define SETUP_BRESENHAM(CALC_TYPE, ORIGX1, ORIGY1, ORIGX2, ORIGY2, SHORTEN) \
+do { \
+    jint X1 = ORIGX1, Y1 = ORIGY1, X2 = ORIGX2, Y2 = ORIGY2; \
+    jint dx, dy, ax, ay; \
+    jint cxmin, cymin, cxmax, cymax; \
+    jint outcode1, outcode2; \
+    jboolean xmajor; \
+    jint errminor, errmajor; \
+    jint error; \
+    jint steps; \
+ \
+    dx = X2 - X1; \
+    dy = Y2 - Y1; \
+    ax = (dx < 0) ? -dx : dx; \
+    ay = (dy < 0) ? -dy : dy; \
+ \
+    cxmin = pBounds->x1; \
+    cymin = pBounds->y1; \
+    cxmax = pBounds->x2 - 1; \
+    cymax = pBounds->y2 - 1; \
+    xmajor = (ax >= ay); \
+ \
+    outcode1 = outcode(X1, Y1, cxmin, cymin, cxmax, cymax); \
+    outcode2 = outcode(X2, Y2, cxmin, cymin, cxmax, cymax); \
+    while ((outcode1 | outcode2) != 0) { \
+        CALC_TYPE xsteps, ysteps; \
+        if ((outcode1 & outcode2) != 0) { \
+            return JNI_FALSE; \
+        } \
+        if (outcode1 != 0) { \
+            if (outcode1 & (OUTCODE_TOP | OUTCODE_BOTTOM)) { \
+                if (outcode1 & OUTCODE_TOP) { \
+                    Y1 = cymin; \
+                } else { \
+                    Y1 = cymax; \
+                } \
+                ysteps = Y1 - ORIGY1; \
+                if (ysteps < 0) { \
+                    ysteps = -ysteps; \
+                } \
+                xsteps = 2 * ysteps * ax + ay; \
+                if (xmajor) { \
+                    xsteps += ay - ax - 1; \
+                } \
+                xsteps = xsteps / (2 * ay); \
+                if (dx < 0) { \
+                    xsteps = -xsteps; \
+                } \
+                X1 = ORIGX1 + (jint) xsteps; \
+            } else if (outcode1 & (OUTCODE_LEFT | OUTCODE_RIGHT)) { \
+                if (outcode1 & OUTCODE_LEFT) { \
+                    X1 = cxmin; \
+                } else { \
+                    X1 = cxmax; \
+                } \
+                xsteps = X1 - ORIGX1; \
+                if (xsteps < 0) { \
+                    xsteps = -xsteps; \
+                } \
+                ysteps = 2 * xsteps * ay + ax; \
+                if (!xmajor) { \
+                    ysteps += ax - ay - 1; \
+                } \
+                ysteps = ysteps / (2 * ax); \
+                if (dy < 0) { \
+                    ysteps = -ysteps; \
+                } \
+                Y1 = ORIGY1 + (jint) ysteps; \
+            } \
+            outcode1 = outcode(X1, Y1, cxmin, cymin, cxmax, cymax); \
+        } else { \
+            if (outcode2 & (OUTCODE_TOP | OUTCODE_BOTTOM)) { \
+                if (outcode2 & OUTCODE_TOP) { \
+                    Y2 = cymin; \
+                } else { \
+                    Y2 = cymax; \
+                } \
+                ysteps = Y2 - ORIGY2; \
+                if (ysteps < 0) { \
+                    ysteps = -ysteps; \
+                } \
+                xsteps = 2 * ysteps * ax + ay; \
+                if (xmajor) { \
+                    xsteps += ay - ax; \
+                } else { \
+                    xsteps -= 1; \
+                } \
+                xsteps = xsteps / (2 * ay); \
+                if (dx > 0) { \
+                    xsteps = -xsteps; \
+                } \
+                X2 = ORIGX2 + (jint) xsteps; \
+            } else if (outcode2 & (OUTCODE_LEFT | OUTCODE_RIGHT)) { \
+                if (outcode2 & OUTCODE_LEFT) { \
+                    X2 = cxmin; \
+                } else { \
+                    X2 = cxmax; \
+                } \
+                xsteps = X2 - ORIGX2; \
+                if (xsteps < 0) { \
+                    xsteps = -xsteps; \
+                } \
+                ysteps = 2 * xsteps * ay + ax; \
+                if (xmajor) { \
+                    ysteps -= 1; \
+                } else { \
+                    ysteps += ax - ay; \
+                } \
+                ysteps = ysteps / (2 * ax); \
+                if (dy > 0) { \
+                    ysteps = -ysteps; \
+                } \
+                Y2 = ORIGY2 + (jint) ysteps; \
+            } \
+            outcode2 = outcode(X2, Y2, cxmin, cymin, cxmax, cymax); \
+        } \
+    } \
+    *pStartX = X1; \
+    *pStartY = Y1; \
+ \
+    if (xmajor) { \
+        errmajor = ay * 2; \
+        errminor = ax * 2; \
+        *pBumpMajorMask = (dx < 0) ? BUMP_NEG_PIXEL : BUMP_POS_PIXEL; \
+        *pBumpMinorMask = (dy < 0) ? BUMP_NEG_SCAN : BUMP_POS_SCAN; \
+        ax = -ax; /* For clipping adjustment below */ \
+        steps = X2 - X1; \
+        if (X2 != ORIGX2) { \
+            SHORTEN = 0; \
+        } \
+    } else { \
+        errmajor = ax * 2; \
+        errminor = ay * 2; \
+        *pBumpMajorMask = (dy < 0) ? BUMP_NEG_SCAN : BUMP_POS_SCAN; \
+        *pBumpMinorMask = (dx < 0) ? BUMP_NEG_PIXEL : BUMP_POS_PIXEL; \
+        ay = -ay; /* For clipping adjustment below */ \
+        steps = Y2 - Y1; \
+        if (Y2 != ORIGY2) { \
+            SHORTEN = 0; \
+        } \
+    } \
+    if ((steps = ((steps >= 0) ? steps : -steps) + 1 - SHORTEN) == 0) { \
+        return JNI_FALSE; \
+    } \
+    error = - (errminor / 2); \
+    if (Y1 != ORIGY1) { \
+        jint ysteps = Y1 - ORIGY1; \
+        if (ysteps < 0) { \
+            ysteps = -ysteps; \
+        } \
+        error += ysteps * ax * 2; \
+    } \
+    if (X1 != ORIGX1) { \
+        jint xsteps = X1 - ORIGX1; \
+        if (xsteps < 0) { \
+            xsteps = -xsteps; \
+        } \
+        error += xsteps * ay * 2; \
+    } \
+    error += errmajor; \
+    errminor -= errmajor; \
+ \
+    *pSteps = steps; \
+    *pError = error; \
+    *pErrMajor = errmajor; \
+    *pErrMinor = errminor; \
+} while (0)
+
+static jboolean
+LineUtils_SetupBresenhamBig(jint _x1, jint _y1, jint _x2, jint _y2,
+                            jint shorten,
+                            SurfaceDataBounds *pBounds,
+                            jint *pStartX, jint *pStartY,
+                            jint *pSteps, jint *pError,
+                            jint *pErrMajor, jint *pBumpMajorMask,
+                            jint *pErrMinor, jint *pBumpMinorMask)
+{
+    /*
+     * Part of calculating the Bresenham parameters for line stepping
+     * involves being able to store numbers that are twice the magnitude
+     * of the biggest absolute difference in coordinates.  Since we
+     * want the stepping parameters to be stored in jints, we then need
+     * to avoid any absolute differences more than 30 bits.  Thus, we
+     * need to preprocess the coordinates to reduce their range to 30
+     * bits regardless of clipping.  We need to cut their range back
+     * before we do the clipping because the Bresenham stepping values
+     * need to be calculated based on the "unclipped" coordinates.
+     *
+     * Thus, first we perform a "pre-clipping" stage to bring the
+     * coordinates within the 30-bit range and then we proceed to the
+     * regular clipping procedure, pretending that these were the
+     * original coordinates all along.  Since this operation occurs
+     * based on a constant "pre-clip" rectangle of +/- 30 bits without
+     * any consideration for the final clip, the rounding errors that
+     * occur here will depend only on the line coordinates and be
+     * invariant with respect to the particular device/user clip
+     * rectangles in effect at the time.  Thus, rendering a given
+     * large-range line will be consistent under a variety of
+     * clipping conditions.
+     */
+    if (OverflowsBig(_x1) || OverflowsBig(_y1) ||
+        OverflowsBig(_x2) || OverflowsBig(_y2))
+    {
+        /*
+         * Use doubles to get us into range for "Big" arithmetic.
+         *
+         * The math of adjusting an endpoint for clipping can involve
+         * an intermediate result with twice the number of bits as the
+         * original coordinate range.  Since we want to maintain as
+         * much as 30 bits of precision in the resulting coordinates,
+         * we will get roundoff here even using IEEE double-precision
+         * arithmetic which cannot carry 60 bits of mantissa.  Since
+         * the rounding errors will be consistent for a given set
+         * of input coordinates the potential roundoff error should
+         * not affect the consistency of our rendering.
+         */
+        double X1d = _x1;
+        double Y1d = _y1;
+        double X2d = _x2;
+        double Y2d = _y2;
+        double DXd = X2d - X1d;
+        double DYd = Y2d - Y1d;
+        if (_x1 < BIG_MIN) {
+            Y1d = _y1 + (BIG_MIN - _x1) * DYd / DXd;
+            X1d = BIG_MIN;
+        } else if (_x1 > BIG_MAX) {
+            Y1d = _y1 - (_x1 - BIG_MAX) * DYd / DXd;
+            X1d = BIG_MAX;
+        }
+        /* Use Y1d instead of _y1 for testing now as we may have modified it */
+        if (Y1d < BIG_MIN) {
+            X1d = _x1 + (BIG_MIN - _y1) * DXd / DYd;
+            Y1d = BIG_MIN;
+        } else if (Y1d > BIG_MAX) {
+            X1d = _x1 - (_y1 - BIG_MAX) * DXd / DYd;
+            Y1d = BIG_MAX;
+        }
+        if (_x2 < BIG_MIN) {
+            Y2d = _y2 + (BIG_MIN - _x2) * DYd / DXd;
+            X2d = BIG_MIN;
+        } else if (_x2 > BIG_MAX) {
+            Y2d = _y2 - (_x2 - BIG_MAX) * DYd / DXd;
+            X2d = BIG_MAX;
+        }
+        /* Use Y2d instead of _y2 for testing now as we may have modified it */
+        if (Y2d < BIG_MIN) {
+            X2d = _x2 + (BIG_MIN - _y2) * DXd / DYd;
+            Y2d = BIG_MIN;
+        } else if (Y2d > BIG_MAX) {
+            X2d = _x2 - (_y2 - BIG_MAX) * DXd / DYd;
+            Y2d = BIG_MAX;
+        }
+        _x1 = (int) X1d;
+        _y1 = (int) Y1d;
+        _x2 = (int) X2d;
+        _y2 = (int) Y2d;
+    }
+
+    SETUP_BRESENHAM(jlong, _x1, _y1, _x2, _y2, shorten);
+
+    return JNI_TRUE;
+}
+
+jboolean
+LineUtils_SetupBresenham(jint _x1, jint _y1, jint _x2, jint _y2,
+                         jint shorten,
+                         SurfaceDataBounds *pBounds,
+                         jint *pStartX, jint *pStartY,
+                         jint *pSteps, jint *pError,
+                         jint *pErrMajor, jint *pBumpMajorMask,
+                         jint *pErrMinor, jint *pBumpMinorMask)
+{
+    if (OverflowsSmall(_x1) || OverflowsSmall(_y1) ||
+        OverflowsSmall(_x2) || OverflowsSmall(_y2))
+    {
+        return LineUtils_SetupBresenhamBig(_x1, _y1, _x2, _y2, shorten,
+                                           pBounds,
+                                           pStartX, pStartY,
+                                           pSteps, pError,
+                                           pErrMajor, pBumpMajorMask,
+                                           pErrMinor, pBumpMinorMask);
+    }
+
+    SETUP_BRESENHAM(jint, _x1, _y1, _x2, _y2, shorten);
+
+    return JNI_TRUE;
+}
+
+/*
+ * Class:     sun_java2d_loops_DrawLine
+ * Method:    DrawLine
+ * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;IIII)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_java2d_loops_DrawLine_DrawLine
+    (JNIEnv *env, jobject self,
+     jobject sg2d, jobject sData,
+     jint x1, jint y1, jint x2, jint y2)
+{
+    SurfaceDataOps *sdOps;
+    SurfaceDataRasInfo rasInfo;
+    NativePrimitive *pPrim;
+    CompositeInfo compInfo;
+    jint pixel = GrPrim_Sg2dGetPixel(env, sg2d);
+
+    pPrim = GetNativePrim(env, self);
+    if (pPrim == NULL) {
+        return;
+    }
+    if (pPrim->pCompType->getCompInfo != NULL) {
+        GrPrim_Sg2dGetCompInfo(env, sg2d, pPrim, &compInfo);
+    }
+
+    sdOps = SurfaceData_GetOps(env, sData);
+    if (sdOps == 0) {
+        return;
+    }
+
+    GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds);
+
+    RefineBounds(&rasInfo.bounds, x1, y1, x2, y2);
+
+    if (sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags) != SD_SUCCESS) {
+        return;
+    }
+
+    if (rasInfo.bounds.x2 > rasInfo.bounds.x1 &&
+        rasInfo.bounds.y2 > rasInfo.bounds.y1)
+    {
+        sdOps->GetRasInfo(env, sdOps, &rasInfo);
+        if (rasInfo.rasBase) {
+            LineUtils_ProcessLine(&rasInfo, pixel,
+                                  pPrim->funcs.drawline, pPrim, &compInfo,
+                                  x1, y1, x2, y2, 0);
+        }
+        SurfaceData_InvokeRelease(env, sdOps, &rasInfo);
+    }
+    SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
+}