| /* |
| * Copyright (c) 2000, 2001, 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 "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); |
| } |