|
Java example source code file (ShapeSpanIterator.c)
The ShapeSpanIterator.c Java example source code/* * Copyright (c) 1998, 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 <stdlib.h> #include <string.h> #include <math.h> #include "jni.h" #include "jni_util.h" #include <jlong.h> #include "j2d_md.h" #include "PathConsumer2D.h" #include "SpanIterator.h" #include "sun_java2d_pipe_ShapeSpanIterator.h" #include "java_awt_geom_PathIterator.h" /* * This structure holds all of the information needed to trace and * manage a single line segment of the shape's outline. */ typedef struct { jint curx; jint cury; jint lasty; jint error; jint bumpx; jint bumperr; jbyte windDir; jbyte pad0; jbyte pad1; jbyte pad2; } segmentData; /* * This structure holds all of the information needed to trace out * the entire span list of a single Shape object. */ typedef struct { PathConsumerVec funcs; /* Native PathConsumer function vector */ char state; /* Path delivery sequence state */ char evenodd; /* non-zero if path has EvenOdd winding rule */ char first; /* non-zero if first path segment */ char adjust; /* normalize to nearest (0.25, 0.25) */ jint lox; /* clip bbox low X */ jint loy; /* clip bbox low Y */ jint hix; /* clip bbox high X */ jint hiy; /* clip bbox high Y */ jfloat curx; /* current path point X coordinate */ jfloat cury; /* current path point Y coordinate */ jfloat movx; /* last moveto X coordinate */ jfloat movy; /* last moveto Y coordinate */ jfloat adjx; /* last X coordinate adjustment */ jfloat adjy; /* last Y coordinate adjustment */ jfloat pathlox; /* lowest X coordinate in path */ jfloat pathloy; /* lowest Y coordinate in path */ jfloat pathhix; /* highest X coordinate in path */ jfloat pathhiy; /* highest Y coordinate in path */ segmentData *segments; /* pointer to array of path segments */ int numSegments; /* number of segments entries in array */ int segmentsSize; /* size of array of path segments */ int lowSegment; /* lower limit of segments in active range */ int curSegment; /* index of next active segment to return */ int hiSegment; /* upper limit of segments in active range */ segmentData **segmentTable; /* pointers to segments being stepped */ } pathData; #define STATE_INIT 0 #define STATE_HAVE_CLIP 1 #define STATE_HAVE_RULE 2 #define STATE_PATH_DONE 3 #define STATE_SPAN_STARTED 4 static jboolean subdivideLine(pathData *pd, int level, jfloat x0, jfloat y0, jfloat x1, jfloat y1); static jboolean subdivideQuad(pathData *pd, int level, jfloat x0, jfloat y0, jfloat x1, jfloat y1, jfloat x2, jfloat y2); static jboolean subdivideCubic(pathData *pd, int level, jfloat x0, jfloat y0, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3); static jboolean appendSegment(pathData *pd, jfloat x0, jfloat y0, jfloat x1, jfloat y1); static jboolean initSegmentTable(pathData *pd); static void *ShapeSIOpen(JNIEnv *env, jobject iterator); static void ShapeSIClose(JNIEnv *env, void *private); static void ShapeSIGetPathBox(JNIEnv *env, void *private, jint pathbox[]); static void ShapeSIIntersectClipBox(JNIEnv *env, void *private, jint lox, jint loy, jint hix, jint hiy); static jboolean ShapeSINextSpan(void *state, jint spanbox[]); static void ShapeSISkipDownTo(void *private, jint y); static jfieldID pSpanDataID; static SpanIteratorFuncs ShapeSIFuncs = { ShapeSIOpen, ShapeSIClose, ShapeSIGetPathBox, ShapeSIIntersectClipBox, ShapeSINextSpan, ShapeSISkipDownTo }; static LineToFunc PCLineTo; static MoveToFunc PCMoveTo; static QuadToFunc PCQuadTo; static CubicToFunc PCCubicTo; static ClosePathFunc PCClosePath; static PathDoneFunc PCPathDone; #define PDBOXPOINT(pd, x, y) \ do { \ if (pd->first) { \ pd->pathlox = pd->pathhix = x; \ pd->pathloy = pd->pathhiy = y; \ pd->first = 0; \ } else { \ if (pd->pathlox > x) pd->pathlox = x; \ if (pd->pathloy > y) pd->pathloy = y; \ if (pd->pathhix < x) pd->pathhix = x; \ if (pd->pathhiy < y) pd->pathhiy = y; \ } \ } while (0) /* * _ADJUST is the internal macro used to adjust a new endpoint * and then invoke the "additional" code from the callers below * which will adjust the control points as needed to match. * * When the "additional" code is executed, newadj[xy] will * contain the adjustment applied to the new endpoint and * pd->adj[xy] will still contain the previous adjustment * that was applied to the old endpoint. */ #define _ADJUST(pd, x, y, additional) \ do { \ if (pd->adjust) { \ jfloat newx = (jfloat) floor(x + 0.25f) + 0.25f; \ jfloat newy = (jfloat) floor(y + 0.25f) + 0.25f; \ jfloat newadjx = newx - x; \ jfloat newadjy = newy - y; \ x = newx; \ y = newy; \ additional; \ pd->adjx = newadjx; \ pd->adjy = newadjy; \ } \ } while (0) /* * Adjust a single endpoint with no control points. * "additional" code is a null statement. */ #define ADJUST1(pd, x1, y1) \ _ADJUST(pd, x1, y1, \ do { \ } while (0)) /* * Adjust a quadratic curve. The _ADJUST macro takes care * of the new endpoint and the "additional" code adjusts * the single quadratic control point by the averge of * the prior and the new adjustment amounts. */ #define ADJUST2(pd, x1, y1, x2, y2) \ _ADJUST(pd, x2, y2, \ do { \ x1 += (pd->adjx + newadjy) / 2; \ y1 += (pd->adjy + newadjy) / 2; \ } while (0)) /* * Adjust a cubic curve. The _ADJUST macro takes care * of the new endpoint and the "additional" code adjusts * the first of the two cubic control points by the same * amount that the prior endpoint was adjusted and then * adjusts the second of the two control points by the * same amount as the new endpoint was adjusted. This * keeps the tangent lines from xy0 to xy1 and xy3 to xy2 * parallel before and after the adjustment. */ #define ADJUST3(pd, x1, y1, x2, y2, x3, y3) \ _ADJUST(pd, x3, y3, \ do { \ x1 += pd->adjx; \ y1 += pd->adjy; \ x2 += newadjx; \ y2 += newadjy; \ } while (0)) #define HANDLEMOVETO(pd, x0, y0, OOMERR) \ do { \ HANDLECLOSE(pd, OOMERR); \ ADJUST1(pd, x0, y0); \ pd->movx = x0; \ pd->movy = y0; \ PDBOXPOINT(pd, x0, y0); \ pd->curx = x0; \ pd->cury = y0; \ } while (0) #define HANDLELINETO(pd, x1, y1, OOMERR) \ do { \ ADJUST1(pd, x1, y1); \ if (!subdivideLine(pd, 0, \ pd->curx, pd->cury, \ x1, y1)) { \ OOMERR; \ break; \ } \ PDBOXPOINT(pd, x1, y1); \ pd->curx = x1; \ pd->cury = y1; \ } while (0) #define HANDLEQUADTO(pd, x1, y1, x2, y2, OOMERR) \ do { \ ADJUST2(pd, x1, y1, x2, y2); \ if (!subdivideQuad(pd, 0, \ pd->curx, pd->cury, \ x1, y1, x2, y2)) { \ OOMERR; \ break; \ } \ PDBOXPOINT(pd, x1, y1); \ PDBOXPOINT(pd, x2, y2); \ pd->curx = x2; \ pd->cury = y2; \ } while (0) #define HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, OOMERR) \ do { \ ADJUST3(pd, x1, y1, x2, y2, x3, y3); \ if (!subdivideCubic(pd, 0, \ pd->curx, pd->cury, \ x1, y1, x2, y2, x3, y3)) { \ OOMERR; \ break; \ } \ PDBOXPOINT(pd, x1, y1); \ PDBOXPOINT(pd, x2, y2); \ PDBOXPOINT(pd, x3, y3); \ pd->curx = x3; \ pd->cury = y3; \ } while (0) #define HANDLECLOSE(pd, OOMERR) \ do { \ if (pd->curx != pd->movx || pd->cury != pd->movy) { \ if (!subdivideLine(pd, 0, \ pd->curx, pd->cury, \ pd->movx, pd->movy)) { \ OOMERR; \ break; \ } \ pd->curx = pd->movx; \ pd->cury = pd->movy; \ } \ } while (0) #define HANDLEENDPATH(pd, OOMERR) \ do { \ HANDLECLOSE(pd, OOMERR); \ pd->state = STATE_PATH_DONE; \ } while (0) static pathData * GetSpanData(JNIEnv *env, jobject sr, int minState, int maxState) { pathData *pd = (pathData *) JNU_GetLongFieldAsPtr(env, sr, pSpanDataID); if (pd == NULL) { JNU_ThrowNullPointerException(env, "private data"); } else if (pd->state < minState || pd->state > maxState) { JNU_ThrowInternalError(env, "bad path delivery sequence"); pd = NULL; } return pd; } static pathData * MakeSpanData(JNIEnv *env, jobject sr) { pathData *pd = (pathData *) JNU_GetLongFieldAsPtr(env, sr, pSpanDataID); if (pd != NULL) { JNU_ThrowInternalError(env, "private data already initialized"); return NULL; } pd = calloc(1, sizeof(pathData)); if (pd == NULL) { JNU_ThrowOutOfMemoryError(env, "private data"); } else { /* Initialize PathConsumer2D struct header */ pd->funcs.moveTo = PCMoveTo; pd->funcs.lineTo = PCLineTo; pd->funcs.quadTo = PCQuadTo; pd->funcs.cubicTo = PCCubicTo; pd->funcs.closePath = PCClosePath; pd->funcs.pathDone = PCPathDone; /* Initialize ShapeSpanIterator fields */ pd->first = 1; (*env)->SetLongField(env, sr, pSpanDataID, ptr_to_jlong(pd)); } return pd; } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_initIDs (JNIEnv *env, jclass src) { pSpanDataID = (*env)->GetFieldID(env, src, "pData", "J"); } /* * Class: sun_java2d_pipe_ShapeSpanIterator * Method: setNormalize * Signature: (Z)V */ JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_setNormalize (JNIEnv *env, jobject sr, jboolean adjust) { pathData *pd; pd = MakeSpanData(env, sr); if (pd == NULL) { return; } pd->adjust = adjust; } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_setOutputAreaXYXY (JNIEnv *env, jobject sr, jint lox, jint loy, jint hix, jint hiy) { pathData *pd; pd = GetSpanData(env, sr, STATE_INIT, STATE_INIT); if (pd == NULL) { return; } pd->lox = lox; pd->loy = loy; pd->hix = hix; pd->hiy = hiy; pd->state = STATE_HAVE_CLIP; } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_setRule (JNIEnv *env, jobject sr, jint rule) { pathData *pd; pd = GetSpanData(env, sr, STATE_HAVE_CLIP, STATE_HAVE_CLIP); if (pd == NULL) { return; } pd->evenodd = (rule == java_awt_geom_PathIterator_WIND_EVEN_ODD); pd->state = STATE_HAVE_RULE; } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_addSegment (JNIEnv *env, jobject sr, jint type, jfloatArray coordObj) { jfloat coords[6]; jfloat x1, y1, x2, y2, x3, y3; jboolean oom = JNI_FALSE; pathData *pd; int numpts = 0; pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE); if (pd == NULL) { return; } (*env)->GetFloatArrayRegion(env, coordObj, 0, 6, coords); if ((*env)->ExceptionCheck(env)) { return; } switch (type) { case java_awt_geom_PathIterator_SEG_MOVETO: x1 = coords[0]; y1 = coords[1]; HANDLEMOVETO(pd, x1, y1, {oom = JNI_TRUE;}); break; case java_awt_geom_PathIterator_SEG_LINETO: x1 = coords[0]; y1 = coords[1]; HANDLELINETO(pd, x1, y1, {oom = JNI_TRUE;}); break; case java_awt_geom_PathIterator_SEG_QUADTO: x1 = coords[0]; y1 = coords[1]; x2 = coords[2]; y2 = coords[3]; HANDLEQUADTO(pd, x1, y1, x2, y2, {oom = JNI_TRUE;}); break; case java_awt_geom_PathIterator_SEG_CUBICTO: x1 = coords[0]; y1 = coords[1]; x2 = coords[2]; y2 = coords[3]; x3 = coords[4]; y3 = coords[5]; HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, {oom = JNI_TRUE;}); break; case java_awt_geom_PathIterator_SEG_CLOSE: HANDLECLOSE(pd, {oom = JNI_TRUE;}); break; default: JNU_ThrowInternalError(env, "bad path segment type"); return; } if (oom) { JNU_ThrowOutOfMemoryError(env, "path segment data"); return; } } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_getPathBox (JNIEnv *env, jobject sr, jintArray spanbox) { pathData *pd; jint coords[4]; pd = GetSpanData(env, sr, STATE_PATH_DONE, STATE_PATH_DONE); if (pd == NULL) { return; } ShapeSIGetPathBox(env, pd, coords); (*env)->SetIntArrayRegion(env, spanbox, 0, 4, coords); } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_intersectClipBox (JNIEnv *env, jobject ri, jint clox, jint cloy, jint chix, jint chiy) { pathData *pd; pd = GetSpanData(env, ri, STATE_PATH_DONE, STATE_PATH_DONE); if (pd == NULL) { return; } ShapeSIIntersectClipBox(env, pd, clox, cloy, chix, chiy); } JNIEXPORT jboolean JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_nextSpan (JNIEnv *env, jobject sr, jintArray spanbox) { pathData *pd; jboolean ret; jint coords[4]; pd = GetSpanData(env, sr, STATE_PATH_DONE, STATE_SPAN_STARTED); if (pd == NULL) { return JNI_FALSE; } ret = ShapeSINextSpan(pd, coords); if (ret) { (*env)->SetIntArrayRegion(env, spanbox, 0, 4, coords); } return ret; } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_skipDownTo (JNIEnv *env, jobject sr, jint y) { pathData *pd; pd = GetSpanData(env, sr, STATE_PATH_DONE, STATE_SPAN_STARTED); if (pd == NULL) { return; } ShapeSISkipDownTo(pd, y); } JNIEXPORT jlong JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_getNativeIterator (JNIEnv *env, jobject sr) { return ptr_to_jlong(&ShapeSIFuncs); } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_dispose (JNIEnv *env, jobject sr) { pathData *pd = (pathData *) JNU_GetLongFieldAsPtr(env, sr, pSpanDataID); if (pd == NULL) { return; } if (pd->segments != NULL) { free(pd->segments); } if (pd->segmentTable != NULL) { free(pd->segmentTable); } free(pd); (*env)->SetLongField(env, sr, pSpanDataID, jlong_zero); } #define OUT_XLO 1 #define OUT_XHI 2 #define OUT_YLO 4 #define OUT_YHI 8 #define CALCULATE_OUTCODES(pd, outc, x, y) \ do { \ if (y <= pd->loy) outc = OUT_YLO; \ else if (y >= pd->hiy) outc = OUT_YHI; \ else outc = 0; \ if (x <= pd->lox) outc |= OUT_XLO; \ else if (x >= pd->hix) outc |= OUT_XHI; \ } while (0) JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_appendPoly (JNIEnv *env, jobject sr, jintArray xArray, jintArray yArray, jint nPoints, jint ixoff, jint iyoff) { pathData *pd; int i; jint *xPoints, *yPoints; jboolean oom = JNI_FALSE; jfloat xoff = (jfloat) ixoff, yoff = (jfloat) iyoff; pd = GetSpanData(env, sr, STATE_HAVE_CLIP, STATE_HAVE_CLIP); if (pd == NULL) { return; } pd->evenodd = JNI_TRUE; pd->state = STATE_HAVE_RULE; if (pd->adjust) { xoff += 0.25f; yoff += 0.25f; } if (xArray == NULL || yArray == NULL) { JNU_ThrowNullPointerException(env, "polygon data arrays"); return; } if ((*env)->GetArrayLength(env, xArray) < nPoints || (*env)->GetArrayLength(env, yArray) < nPoints) { JNU_ThrowArrayIndexOutOfBoundsException(env, "polygon data arrays"); return; } if (nPoints > 0) { xPoints = (*env)->GetPrimitiveArrayCritical(env, xArray, NULL); if (xPoints != NULL) { yPoints = (*env)->GetPrimitiveArrayCritical(env, yArray, NULL); if (yPoints != NULL) { jint outc0; jfloat x, y; x = xPoints[0] + xoff; y = yPoints[0] + yoff; CALCULATE_OUTCODES(pd, outc0, x, y); pd->movx = pd->curx = x; pd->movy = pd->cury = y; pd->pathlox = pd->pathhix = x; pd->pathloy = pd->pathhiy = y; pd->first = 0; for (i = 1; !oom && i < nPoints; i++) { jint outc1; x = xPoints[i] + xoff; y = yPoints[i] + yoff; if (y == pd->cury) { /* Horizontal segment - do not append */ if (x != pd->curx) { /* Not empty segment - track change in X */ CALCULATE_OUTCODES(pd, outc0, x, y); pd->curx = x; if (pd->pathlox > x) pd->pathlox = x; if (pd->pathhix < x) pd->pathhix = x; } continue; } CALCULATE_OUTCODES(pd, outc1, x, y); outc0 &= outc1; if (outc0 == 0) { oom = !appendSegment(pd, pd->curx, pd->cury, x, y); } else if (outc0 == OUT_XLO) { oom = !appendSegment(pd, (jfloat) pd->lox, pd->cury, (jfloat) pd->lox, y); } if (pd->pathlox > x) pd->pathlox = x; if (pd->pathloy > y) pd->pathloy = y; if (pd->pathhix < x) pd->pathhix = x; if (pd->pathhiy < y) pd->pathhiy = y; outc0 = outc1; pd->curx = x; pd->cury = y; } (*env)->ReleasePrimitiveArrayCritical(env, yArray, yPoints, JNI_ABORT); } (*env)->ReleasePrimitiveArrayCritical(env, xArray, xPoints, JNI_ABORT); } if (xPoints == NULL || yPoints == NULL) { return; } } if (!oom) { HANDLEENDPATH(pd, {oom = JNI_TRUE;}); } if (oom) { JNU_ThrowOutOfMemoryError(env, "path segment data"); } } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_moveTo (JNIEnv *env, jobject sr, jfloat x0, jfloat y0) { pathData *pd; pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE); if (pd == NULL) { return; } HANDLEMOVETO(pd, x0, y0, {JNU_ThrowOutOfMemoryError(env, "path segment data");}); } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_lineTo (JNIEnv *env, jobject sr, jfloat x1, jfloat y1) { pathData *pd; pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE); if (pd == NULL) { return; } HANDLELINETO(pd, x1, y1, {JNU_ThrowOutOfMemoryError(env, "path segment data");}); } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_quadTo (JNIEnv *env, jobject sr, jfloat xm, jfloat ym, jfloat x1, jfloat y1) { pathData *pd; pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE); if (pd == NULL) { return; } HANDLEQUADTO(pd, xm, ym, x1, y1, {JNU_ThrowOutOfMemoryError(env, "path segment data");}); } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_curveTo (JNIEnv *env, jobject sr, jfloat xm, jfloat ym, jfloat xn, jfloat yn, jfloat x1, jfloat y1) { pathData *pd; pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE); if (pd == NULL) { return; } HANDLECUBICTO(pd, xm, ym, xn, yn, x1, y1, {JNU_ThrowOutOfMemoryError(env, "path segment data");}); } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_closePath (JNIEnv *env, jobject sr) { pathData *pd; pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE); if (pd == NULL) { return; } HANDLECLOSE(pd, {JNU_ThrowOutOfMemoryError(env, "path segment data");}); } JNIEXPORT void JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_pathDone (JNIEnv *env, jobject sr) { pathData *pd; pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE); if (pd == NULL) { return; } HANDLEENDPATH(pd, {JNU_ThrowOutOfMemoryError(env, "path segment data");}); } JNIEXPORT jlong JNICALL Java_sun_java2d_pipe_ShapeSpanIterator_getNativeConsumer (JNIEnv *env, jobject sr) { pathData *pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE); if (pd == NULL) { return jlong_zero; } return ptr_to_jlong(&(pd->funcs)); } static jboolean PCMoveTo(PathConsumerVec *consumer, jfloat x0, jfloat y0) { pathData *pd = (pathData *) consumer; jboolean oom = JNI_FALSE; HANDLEMOVETO(pd, x0, y0, {oom = JNI_TRUE;}); return oom; } static jboolean PCLineTo(PathConsumerVec *consumer, jfloat x1, jfloat y1) { pathData *pd = (pathData *) consumer; jboolean oom = JNI_FALSE; HANDLELINETO(pd, x1, y1, {oom = JNI_TRUE;}); return oom; } static jboolean PCQuadTo(PathConsumerVec *consumer, jfloat x1, jfloat y1, jfloat x2, jfloat y2) { pathData *pd = (pathData *) consumer; jboolean oom = JNI_FALSE; HANDLEQUADTO(pd, x1, y1, x2, y2, {oom = JNI_TRUE;}); return oom; } static jboolean PCCubicTo(PathConsumerVec *consumer, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) { pathData *pd = (pathData *) consumer; jboolean oom = JNI_FALSE; HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, {oom = JNI_TRUE;}); return oom; } static jboolean PCClosePath(PathConsumerVec *consumer) { pathData *pd = (pathData *) consumer; jboolean oom = JNI_FALSE; HANDLECLOSE(pd, {oom = JNI_TRUE;}); return oom; } static jboolean PCPathDone(PathConsumerVec *consumer) { pathData *pd = (pathData *) consumer; jboolean oom = JNI_FALSE; HANDLEENDPATH(pd, {oom = JNI_TRUE;}); return oom; } /* * REMIND: CDECL needed for WIN32 "qsort" */ #ifdef _WIN32 #define CDECL __cdecl #else #define CDECL #endif #define SUBDIVIDE_MAX 10 #define MAX_FLAT_SQ (1.0 * 1.0) #define GROW_SIZE 20 #define ERRSTEP_MAX (0x7fffffff) #define FRACTTOJINT(f) ((jint) ((f) * (double) ERRSTEP_MAX)) #define minmax2(v1, v2, min, max) \ do { \ if (v1 < v2) { \ min = v1; \ max = v2; \ } else { \ min = v2; \ max = v1; \ } \ } while(0) #define minmax3(v1, v2, v3, min, max) \ do { \ if (v1 < v2) { \ if (v1 < v3) { \ min = v1; \ max = (v2 < v3) ? v3 : v2; \ } else { \ max = v2; \ min = v3; \ } \ } else { \ if (v1 < v3) { \ max = v3; \ min = v2; \ } else { \ max = v1; \ min = (v2 < v3) ? v2 : v3; \ } \ } \ } while (0) #define minmax4(v1, v2, v3, v4, min, max) \ do { \ if (v1 < v2) { \ if (v3 < v4) { \ max = (v2 < v4) ? v4 : v2; \ min = (v1 < v3) ? v1 : v3; \ } else { \ max = (v2 < v3) ? v3 : v2; \ min = (v1 < v4) ? v1 : v4; \ } \ } else { \ if (v3 < v4) { \ max = (v1 < v4) ? v4 : v1; \ min = (v2 < v3) ? v2 : v3; \ } else { \ max = (v1 < v3) ? v3 : v1; \ min = (v2 < v4) ? v2 : v4; \ } \ } \ } while(0) static jfloat ptSegDistSq(jfloat x0, jfloat y0, jfloat x1, jfloat y1, jfloat px, jfloat py) { jfloat dotprod, projlenSq; /* Adjust vectors relative to x0,y0 */ /* x1,y1 becomes relative vector from x0,y0 to end of segment */ x1 -= x0; y1 -= y0; /* px,py becomes relative vector from x0,y0 to test point */ px -= x0; py -= y0; dotprod = px * x1 + py * y1; if (dotprod <= 0.0) { /* px,py is on the side of x0,y0 away from x1,y1 */ /* distance to segment is length of px,py vector */ /* "length of its (clipped) projection" is now 0.0 */ projlenSq = 0.0; } else { /* switch to backwards vectors relative to x1,y1 */ /* x1,y1 are already the negative of x0,y0=>x1,y1 */ /* to get px,py to be the negative of px,py=>x1,y1 */ /* the dot product of two negated vectors is the same */ /* as the dot product of the two normal vectors */ px = x1 - px; py = y1 - py; dotprod = px * x1 + py * y1; if (dotprod <= 0.0) { /* px,py is on the side of x1,y1 away from x0,y0 */ /* distance to segment is length of (backwards) px,py vector */ /* "length of its (clipped) projection" is now 0.0 */ projlenSq = 0.0; } else { /* px,py is between x0,y0 and x1,y1 */ /* dotprod is the length of the px,py vector */ /* projected on the x1,y1=>x0,y0 vector times the */ /* length of the x1,y1=>x0,y0 vector */ projlenSq = dotprod * dotprod / (x1 * x1 + y1 * y1); } } /* Distance to line is now the length of the relative point */ /* vector minus the length of its projection onto the line */ /* (which is zero if the projection falls outside the range */ /* of the line segment). */ return px * px + py * py - projlenSq; } static jboolean appendSegment(pathData *pd, jfloat x0, jfloat y0, jfloat x1, jfloat y1) { jbyte windDir; jint istartx, istarty, ilasty; jfloat dx, dy, slope; jfloat ystartbump; jint bumpx, bumperr, error; segmentData *seg; if (y0 > y1) { jfloat t; t = x0; x0 = x1; x1 = t; t = y0; y0 = y1; y1 = t; windDir = -1; } else { windDir = 1; } /* We want to iterate at every horizontal pixel center (HPC) crossing. */ /* First calculate next highest HPC we will cross at the start. */ istarty = (jint) ceil(y0 - 0.5f); /* Then calculate next highest HPC we would cross at the end. */ ilasty = (jint) ceil(y1 - 0.5f); /* Ignore if we start and end outside clip, or on the same scanline. */ if (istarty >= ilasty || istarty >= pd->hiy || ilasty <= pd->loy) { return JNI_TRUE; } /* We will need to insert this segment, check for room. */ if (pd->numSegments >= pd->segmentsSize) { segmentData *newSegs; int newSize = pd->segmentsSize + GROW_SIZE; newSegs = (segmentData *) calloc(newSize, sizeof(segmentData)); if (newSegs == NULL) { return JNI_FALSE; } if (pd->segments != NULL) { memcpy(newSegs, pd->segments, sizeof(segmentData) * pd->segmentsSize); free(pd->segments); } pd->segments = newSegs; pd->segmentsSize = newSize; } dx = x1 - x0; dy = y1 - y0; slope = dx / dy; /* * The Y coordinate of the first HPC was calculated as istarty. We * now need to calculate the corresponding X coordinate (both integer * version for span start coordinate and float version for sub-pixel * error calculation). */ /* First, how far does y bump to get to next HPC? */ ystartbump = istarty + 0.5f - y0; /* Now, bump the float x coordinate to get X sample at that HPC. */ x0 += ystartbump * dx / dy; /* Now calculate the integer coordinate that such a span starts at. */ /* NOTE: Span inclusion is based on vertical pixel centers (VPC). */ istartx = (jint) ceil(x0 - 0.5f); /* What is the lower bound of the per-scanline change in the X coord? */ bumpx = (jint) floor(slope); /* What is the subpixel amount by which the bumpx is off? */ bumperr = FRACTTOJINT(slope - floor(slope)); /* Finally, find out how far the x coordinate can go before next VPC. */ error = FRACTTOJINT(x0 - (istartx - 0.5f)); seg = &pd->segments[pd->numSegments++]; seg->curx = istartx; seg->cury = istarty; seg->lasty = ilasty; seg->error = error; seg->bumpx = bumpx; seg->bumperr = bumperr; seg->windDir = windDir; return JNI_TRUE; } /* * Lines don't really need to be subdivided, but this function performs * the same trivial rejections and reductions that the curve subdivision * functions perform before it hands the coordinates off to the appendSegment * function. */ static jboolean subdivideLine(pathData *pd, int level, jfloat x0, jfloat y0, jfloat x1, jfloat y1) { jfloat miny, maxy; jfloat minx, maxx; minmax2(x0, x1, minx, maxx); minmax2(y0, y1, miny, maxy); if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) { return JNI_TRUE; } if (maxx <= pd->lox) { return appendSegment(pd, maxx, y0, maxx, y1); } return appendSegment(pd, x0, y0, x1, y1); } static jboolean subdivideQuad(pathData *pd, int level, jfloat x0, jfloat y0, jfloat x1, jfloat y1, jfloat x2, jfloat y2) { jfloat miny, maxy; jfloat minx, maxx; minmax3(x0, x1, x2, minx, maxx); minmax3(y0, y1, y2, miny, maxy); if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) { return JNI_TRUE; } if (maxx <= pd->lox) { return appendSegment(pd, maxx, y0, maxx, y2); } if (level < SUBDIVIDE_MAX) { /* Test if the curve is flat enough for insertion. */ if (ptSegDistSq(x0, y0, x2, y2, x1, y1) > MAX_FLAT_SQ) { jfloat cx1, cx2; jfloat cy1, cy2; cx1 = (x0 + x1) / 2.0f; cx2 = (x1 + x2) / 2.0f; x1 = (cx1 + cx2) / 2.0f; cy1 = (y0 + y1) / 2.0f; cy2 = (y1 + y2) / 2.0f; y1 = (cy1 + cy2) / 2.0f; level++; return (subdivideQuad(pd, level, x0, y0, cx1, cy1, x1, y1) && subdivideQuad(pd, level, x1, y1, cx2, cy2, x2, y2)); } } return appendSegment(pd, x0, y0, x2, y2); } static jboolean subdivideCubic(pathData *pd, int level, jfloat x0, jfloat y0, jfloat x1, jfloat y1, jfloat x2, jfloat y2, jfloat x3, jfloat y3) { jfloat miny, maxy; jfloat minx, maxx; minmax4(x0, x1, x2, x3, minx, maxx); minmax4(y0, y1, y2, y3, miny, maxy); if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) { return JNI_TRUE; } if (maxx <= pd->lox) { return appendSegment(pd, maxx, y0, maxx, y3); } if (level < SUBDIVIDE_MAX) { /* Test if the curve is flat enough for insertion. */ if (ptSegDistSq(x0, y0, x3, y3, x1, y1) > MAX_FLAT_SQ || ptSegDistSq(x0, y0, x3, y3, x2, y2) > MAX_FLAT_SQ) { jfloat ctrx, cx12, cx21; jfloat ctry, cy12, cy21; ctrx = (x1 + x2) / 2.0f; x1 = (x0 + x1) / 2.0f; x2 = (x2 + x3) / 2.0f; cx12 = (x1 + ctrx) / 2.0f; cx21 = (ctrx + x2) / 2.0f; ctrx = (cx12 + cx21) / 2.0f; ctry = (y1 + y2) / 2.0f; y1 = (y0 + y1) / 2.0f; y2 = (y2 + y3) / 2.0f; cy12 = (y1 + ctry) / 2.0f; cy21 = (ctry + y2) / 2.0f; ctry = (cy12 + cy21) / 2.0f; level++; return (subdivideCubic(pd, level, x0, y0, x1, y1, cx12, cy12, ctrx, ctry) && subdivideCubic(pd, level, ctrx, ctry, cx21, cy21, x2, y2, x3, y3)); } } return appendSegment(pd, x0, y0, x3, y3); } static int CDECL sortSegmentsByLeadingY(const void *elem1, const void *elem2) { segmentData *seg1 = *(segmentData **)elem1; segmentData *seg2 = *(segmentData **)elem2; if (seg1->cury < seg2->cury) { return -1; } if (seg1->cury > seg2->cury) { return 1; } if (seg1->curx < seg2->curx) { return -1; } if (seg1->curx > seg2->curx) { return 1; } if (seg1->lasty < seg2->lasty) { return -1; } if (seg1->lasty > seg2->lasty) { return 1; } return 0; } static void * ShapeSIOpen(JNIEnv *env, jobject iterator) { return GetSpanData(env, iterator, STATE_PATH_DONE, STATE_PATH_DONE); } static void ShapeSIClose(JNIEnv *env, void *private) { } static void ShapeSIGetPathBox(JNIEnv *env, void *private, jint pathbox[]) { pathData *pd = (pathData *)private; pathbox[0] = (jint) floor(pd->pathlox); pathbox[1] = (jint) floor(pd->pathloy); pathbox[2] = (jint) ceil(pd->pathhix); pathbox[3] = (jint) ceil(pd->pathhiy); } /* Adjust the clip box from the given bounds. Used to constrain the output to a device clip */ static void ShapeSIIntersectClipBox(JNIEnv *env, void *private, jint clox, jint cloy, jint chix, jint chiy) { pathData *pd = (pathData *)private; if (clox > pd->lox) { pd->lox = clox; } if (cloy > pd->loy) { pd->loy = cloy; } if (chix < pd->hix) { pd->hix = chix; } if (chiy < pd->hiy) { pd->hiy = chiy; } } static jboolean ShapeSINextSpan(void *state, jint spanbox[]) { pathData *pd = (pathData *)state; int lo, cur, new, hi; int num = pd->numSegments; jint x0, x1, y0, err; jint loy; int ret = JNI_FALSE; segmentData **segmentTable; segmentData *seg; if (pd->state != STATE_SPAN_STARTED) { if (!initSegmentTable(pd)) { /* REMIND: - throw exception? */ pd->lowSegment = num; return JNI_FALSE; } } lo = pd->lowSegment; cur = pd->curSegment; hi = pd->hiSegment; num = pd->numSegments; loy = pd->loy; segmentTable = pd->segmentTable; while (lo < num) { if (cur < hi) { seg = segmentTable[cur]; x0 = seg->curx; if (x0 >= pd->hix) { cur = hi; continue; } if (x0 < pd->lox) { x0 = pd->lox; } if (pd->evenodd) { cur += 2; if (cur <= hi) { x1 = segmentTable[cur - 1]->curx; } else { x1 = pd->hix; } } else { int wind = seg->windDir; cur++; while (JNI_TRUE) { if (cur >= hi) { x1 = pd->hix; break; } seg = segmentTable[cur++]; wind += seg->windDir; if (wind == 0) { x1 = seg->curx; break; } } } if (x1 > pd->hix) { x1 = pd->hix; } if (x1 <= x0) { continue; } spanbox[0] = x0; spanbox[1] = loy; spanbox[2] = x1; spanbox[3] = loy + 1; ret = JNI_TRUE; break; } if (++loy >= pd->hiy) { lo = cur = hi = num; break; } /* Go through active segments and toss which end "above" loy */ cur = new = hi; while (--cur >= lo) { seg = segmentTable[cur]; if (seg->lasty > loy) { segmentTable[--new] = seg; } } lo = new; if (lo == hi && lo < num) { /* The current list of segments is empty so we need to * jump to the beginning of the next set of segments. * Since the segments are not clipped to the output * area we need to make sure we don't jump "backwards" */ seg = segmentTable[lo]; if (loy < seg->cury) { loy = seg->cury; } } /* Go through new segments and accept any which start "above" loy */ while (hi < num && segmentTable[hi]->cury <= loy) { hi++; } /* Update and sort the active segments by x0 */ for (cur = lo; cur < hi; cur++) { seg = segmentTable[cur]; /* First update the x0, y0 of the segment */ x0 = seg->curx; y0 = seg->cury; err = seg->error; if (++y0 == loy) { x0 += seg->bumpx; err += seg->bumperr; x0 -= (err >> 31); err &= ERRSTEP_MAX; } else { jlong steps = loy; steps -= y0 - 1; y0 = loy; x0 += (jint) (steps * seg->bumpx); steps = err + (steps * seg->bumperr); x0 += (jint) (steps >> 31); err = ((jint) steps) & ERRSTEP_MAX; } seg->curx = x0; seg->cury = y0; seg->error = err; /* Then make sure the segment is sorted by x0 */ for (new = cur; new > lo; new--) { segmentData *seg2 = segmentTable[new - 1]; if (seg2->curx <= x0) { break; } segmentTable[new] = seg2; } segmentTable[new] = seg; } cur = lo; } pd->lowSegment = lo; pd->hiSegment = hi; pd->curSegment = cur; pd->loy = loy; return ret; } static void ShapeSISkipDownTo(void *private, jint y) { pathData *pd = (pathData *)private; if (pd->state != STATE_SPAN_STARTED) { if (!initSegmentTable(pd)) { /* REMIND: - throw exception? */ pd->lowSegment = pd->numSegments; return; } } /* Make sure we are jumping forward */ if (pd->loy < y) { /* Pretend like we just finished with the span line y-1... */ pd->loy = y - 1; pd->curSegment = pd->hiSegment; /* no more segments on that line */ } } static jboolean initSegmentTable(pathData *pd) { int i, cur, num, loy; segmentData **segmentTable; segmentTable = malloc(pd->numSegments * sizeof(segmentData *)); if (segmentTable == NULL) { return JNI_FALSE; } pd->state = STATE_SPAN_STARTED; for (i = 0; i < pd->numSegments; i++) { segmentTable[i] = &pd->segments[i]; } qsort(segmentTable, pd->numSegments, sizeof(segmentData *), sortSegmentsByLeadingY); pd->segmentTable = segmentTable; /* Skip to the first segment that ends below the top clip edge */ cur = 0; num = pd->numSegments; loy = pd->loy; while (cur < num && segmentTable[cur]->lasty <= loy) { cur++; } pd->lowSegment = pd->curSegment = pd->hiSegment = cur; /* Prepare for next action to increment loy and prepare new segments */ pd->loy--; return JNI_TRUE; } Other Java examples (source code examples)Here is a short list of links related to this Java ShapeSpanIterator.c source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 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.