Path: blob/master/src/java.desktop/share/native/libawt/java2d/pipe/ShapeSpanIterator.c
41159 views
/*1* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include <stdlib.h>26#include <string.h>27#include <math.h>2829#include "jni.h"30#include "jni_util.h"31#include <jlong.h>3233#include "j2d_md.h"3435#include "PathConsumer2D.h"36#include "SpanIterator.h"3738#include "sun_java2d_pipe_ShapeSpanIterator.h"39#include "java_awt_geom_PathIterator.h"4041/*42* This structure holds all of the information needed to trace and43* manage a single line segment of the shape's outline.44*/45typedef struct {46jint curx;47jint cury;48jint lasty;49jint error;50jint bumpx;51jint bumperr;52jbyte windDir;53jbyte pad0;54jbyte pad1;55jbyte pad2;56} segmentData;5758/*59* This structure holds all of the information needed to trace out60* the entire span list of a single Shape object.61*/62typedef struct {63PathConsumerVec funcs; /* Native PathConsumer function vector */6465char state; /* Path delivery sequence state */66char evenodd; /* non-zero if path has EvenOdd winding rule */67char first; /* non-zero if first path segment */68char adjust; /* normalize to nearest (0.25, 0.25) */6970jint lox; /* clip bbox low X */71jint loy; /* clip bbox low Y */72jint hix; /* clip bbox high X */73jint hiy; /* clip bbox high Y */7475jfloat curx; /* current path point X coordinate */76jfloat cury; /* current path point Y coordinate */77jfloat movx; /* last moveto X coordinate */78jfloat movy; /* last moveto Y coordinate */7980jfloat adjx; /* last X coordinate adjustment */81jfloat adjy; /* last Y coordinate adjustment */8283jfloat pathlox; /* lowest X coordinate in path */84jfloat pathloy; /* lowest Y coordinate in path */85jfloat pathhix; /* highest X coordinate in path */86jfloat pathhiy; /* highest Y coordinate in path */8788segmentData *segments; /* pointer to array of path segments */89int numSegments; /* number of segments entries in array */90int segmentsSize; /* size of array of path segments */9192int lowSegment; /* lower limit of segments in active range */93int curSegment; /* index of next active segment to return */94int hiSegment; /* upper limit of segments in active range */9596segmentData **segmentTable; /* pointers to segments being stepped */97} pathData;9899#define STATE_INIT 0100#define STATE_HAVE_CLIP 1101#define STATE_HAVE_RULE 2102#define STATE_PATH_DONE 3103#define STATE_SPAN_STARTED 4104105static jboolean subdivideLine(pathData *pd, int level,106jfloat x0, jfloat y0,107jfloat x1, jfloat y1);108static jboolean subdivideQuad(pathData *pd, int level,109jfloat x0, jfloat y0,110jfloat x1, jfloat y1,111jfloat x2, jfloat y2);112static jboolean subdivideCubic(pathData *pd, int level,113jfloat x0, jfloat y0,114jfloat x1, jfloat y1,115jfloat x2, jfloat y2,116jfloat x3, jfloat y3);117static jboolean appendSegment(pathData *pd,118jfloat x0, jfloat y0,119jfloat x1, jfloat y1);120static jboolean initSegmentTable(pathData *pd);121122static void *ShapeSIOpen(JNIEnv *env, jobject iterator);123static void ShapeSIClose(JNIEnv *env, void *private);124static void ShapeSIGetPathBox(JNIEnv *env, void *private, jint pathbox[]);125static void ShapeSIIntersectClipBox(JNIEnv *env, void *private,126jint lox, jint loy, jint hix, jint hiy);127static jboolean ShapeSINextSpan(void *state, jint spanbox[]);128static void ShapeSISkipDownTo(void *private, jint y);129130static jfieldID pSpanDataID;131132static SpanIteratorFuncs ShapeSIFuncs = {133ShapeSIOpen,134ShapeSIClose,135ShapeSIGetPathBox,136ShapeSIIntersectClipBox,137ShapeSINextSpan,138ShapeSISkipDownTo139};140141static LineToFunc PCLineTo;142static MoveToFunc PCMoveTo;143static QuadToFunc PCQuadTo;144static CubicToFunc PCCubicTo;145static ClosePathFunc PCClosePath;146static PathDoneFunc PCPathDone;147148#define PDBOXPOINT(pd, x, y) \149do { \150if (pd->first) { \151pd->pathlox = pd->pathhix = x; \152pd->pathloy = pd->pathhiy = y; \153pd->first = 0; \154} else { \155if (pd->pathlox > x) pd->pathlox = x; \156if (pd->pathloy > y) pd->pathloy = y; \157if (pd->pathhix < x) pd->pathhix = x; \158if (pd->pathhiy < y) pd->pathhiy = y; \159} \160} while (0)161162/*163* _ADJUST is the internal macro used to adjust a new endpoint164* and then invoke the "additional" code from the callers below165* which will adjust the control points as needed to match.166*167* When the "additional" code is executed, newadj[xy] will168* contain the adjustment applied to the new endpoint and169* pd->adj[xy] will still contain the previous adjustment170* that was applied to the old endpoint.171*/172#define _ADJUST(pd, x, y, additional) \173do { \174if (pd->adjust) { \175jfloat newx = (jfloat) floor(x + 0.25f) + 0.25f; \176jfloat newy = (jfloat) floor(y + 0.25f) + 0.25f; \177jfloat newadjx = newx - x; \178jfloat newadjy = newy - y; \179x = newx; \180y = newy; \181additional; \182pd->adjx = newadjx; \183pd->adjy = newadjy; \184} \185} while (0)186187/*188* Adjust a single endpoint with no control points.189* "additional" code is a null statement.190*/191#define ADJUST1(pd, x1, y1) \192_ADJUST(pd, x1, y1, \193do { \194} while (0))195196/*197* Adjust a quadratic curve. The _ADJUST macro takes care198* of the new endpoint and the "additional" code adjusts199* the single quadratic control point by the averge of200* the prior and the new adjustment amounts.201*/202#define ADJUST2(pd, x1, y1, x2, y2) \203_ADJUST(pd, x2, y2, \204do { \205x1 += (pd->adjx + newadjy) / 2; \206y1 += (pd->adjy + newadjy) / 2; \207} while (0))208209/*210* Adjust a cubic curve. The _ADJUST macro takes care211* of the new endpoint and the "additional" code adjusts212* the first of the two cubic control points by the same213* amount that the prior endpoint was adjusted and then214* adjusts the second of the two control points by the215* same amount as the new endpoint was adjusted. This216* keeps the tangent lines from xy0 to xy1 and xy3 to xy2217* parallel before and after the adjustment.218*/219#define ADJUST3(pd, x1, y1, x2, y2, x3, y3) \220_ADJUST(pd, x3, y3, \221do { \222x1 += pd->adjx; \223y1 += pd->adjy; \224x2 += newadjx; \225y2 += newadjy; \226} while (0))227228#define HANDLEMOVETO(pd, x0, y0, OOMERR) \229do { \230HANDLECLOSE(pd, OOMERR); \231ADJUST1(pd, x0, y0); \232pd->movx = x0; \233pd->movy = y0; \234PDBOXPOINT(pd, x0, y0); \235pd->curx = x0; \236pd->cury = y0; \237} while (0)238239#define HANDLELINETO(pd, x1, y1, OOMERR) \240do { \241ADJUST1(pd, x1, y1); \242if (!subdivideLine(pd, 0, \243pd->curx, pd->cury, \244x1, y1)) { \245OOMERR; \246break; \247} \248PDBOXPOINT(pd, x1, y1); \249pd->curx = x1; \250pd->cury = y1; \251} while (0)252253#define HANDLEQUADTO(pd, x1, y1, x2, y2, OOMERR) \254do { \255ADJUST2(pd, x1, y1, x2, y2); \256if (!subdivideQuad(pd, 0, \257pd->curx, pd->cury, \258x1, y1, x2, y2)) { \259OOMERR; \260break; \261} \262PDBOXPOINT(pd, x1, y1); \263PDBOXPOINT(pd, x2, y2); \264pd->curx = x2; \265pd->cury = y2; \266} while (0)267268#define HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, OOMERR) \269do { \270ADJUST3(pd, x1, y1, x2, y2, x3, y3); \271if (!subdivideCubic(pd, 0, \272pd->curx, pd->cury, \273x1, y1, x2, y2, x3, y3)) { \274OOMERR; \275break; \276} \277PDBOXPOINT(pd, x1, y1); \278PDBOXPOINT(pd, x2, y2); \279PDBOXPOINT(pd, x3, y3); \280pd->curx = x3; \281pd->cury = y3; \282} while (0)283284#define HANDLECLOSE(pd, OOMERR) \285do { \286if (pd->curx != pd->movx || pd->cury != pd->movy) { \287if (!subdivideLine(pd, 0, \288pd->curx, pd->cury, \289pd->movx, pd->movy)) { \290OOMERR; \291break; \292} \293pd->curx = pd->movx; \294pd->cury = pd->movy; \295} \296} while (0)297298#define HANDLEENDPATH(pd, OOMERR) \299do { \300HANDLECLOSE(pd, OOMERR); \301pd->state = STATE_PATH_DONE; \302} while (0)303304static pathData *305GetSpanData(JNIEnv *env, jobject sr, int minState, int maxState)306{307pathData *pd = (pathData *) JNU_GetLongFieldAsPtr(env, sr, pSpanDataID);308309if (pd == NULL) {310JNU_ThrowNullPointerException(env, "private data");311} else if (pd->state < minState || pd->state > maxState) {312JNU_ThrowInternalError(env, "bad path delivery sequence");313pd = NULL;314}315316return pd;317}318319static pathData *320MakeSpanData(JNIEnv *env, jobject sr)321{322pathData *pd = (pathData *) JNU_GetLongFieldAsPtr(env, sr, pSpanDataID);323324if (pd != NULL) {325JNU_ThrowInternalError(env, "private data already initialized");326return NULL;327}328329pd = calloc(1, sizeof(pathData));330331if (pd == NULL) {332JNU_ThrowOutOfMemoryError(env, "private data");333} else {334/* Initialize PathConsumer2D struct header */335pd->funcs.moveTo = PCMoveTo;336pd->funcs.lineTo = PCLineTo;337pd->funcs.quadTo = PCQuadTo;338pd->funcs.cubicTo = PCCubicTo;339pd->funcs.closePath = PCClosePath;340pd->funcs.pathDone = PCPathDone;341342/* Initialize ShapeSpanIterator fields */343pd->first = 1;344345(*env)->SetLongField(env, sr, pSpanDataID, ptr_to_jlong(pd));346}347348return pd;349}350351JNIEXPORT void JNICALL352Java_sun_java2d_pipe_ShapeSpanIterator_initIDs353(JNIEnv *env, jclass src)354{355pSpanDataID = (*env)->GetFieldID(env, src, "pData", "J");356}357358/*359* Class: sun_java2d_pipe_ShapeSpanIterator360* Method: setNormalize361* Signature: (Z)V362*/363JNIEXPORT void JNICALL364Java_sun_java2d_pipe_ShapeSpanIterator_setNormalize365(JNIEnv *env, jobject sr, jboolean adjust)366{367pathData *pd;368369pd = MakeSpanData(env, sr);370if (pd == NULL) {371return;372}373374pd->adjust = adjust;375}376377JNIEXPORT void JNICALL378Java_sun_java2d_pipe_ShapeSpanIterator_setOutputAreaXYXY379(JNIEnv *env, jobject sr, jint lox, jint loy, jint hix, jint hiy)380{381pathData *pd;382383pd = GetSpanData(env, sr, STATE_INIT, STATE_INIT);384if (pd == NULL) {385return;386}387388pd->lox = lox;389pd->loy = loy;390pd->hix = hix;391pd->hiy = hiy;392pd->state = STATE_HAVE_CLIP;393}394395JNIEXPORT void JNICALL396Java_sun_java2d_pipe_ShapeSpanIterator_setRule397(JNIEnv *env, jobject sr, jint rule)398{399pathData *pd;400401pd = GetSpanData(env, sr, STATE_HAVE_CLIP, STATE_HAVE_CLIP);402if (pd == NULL) {403return;404}405406pd->evenodd = (rule == java_awt_geom_PathIterator_WIND_EVEN_ODD);407pd->state = STATE_HAVE_RULE;408}409410JNIEXPORT void JNICALL411Java_sun_java2d_pipe_ShapeSpanIterator_addSegment412(JNIEnv *env, jobject sr, jint type, jfloatArray coordObj)413{414jfloat coords[6];415jfloat x1, y1, x2, y2, x3, y3;416jboolean oom = JNI_FALSE;417pathData *pd;418int numpts = 0;419420pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);421if (pd == NULL) {422return;423}424425(*env)->GetFloatArrayRegion(env, coordObj, 0, 6, coords);426if ((*env)->ExceptionCheck(env)) {427return;428}429430switch (type) {431case java_awt_geom_PathIterator_SEG_MOVETO:432x1 = coords[0]; y1 = coords[1];433HANDLEMOVETO(pd, x1, y1, {oom = JNI_TRUE;});434break;435case java_awt_geom_PathIterator_SEG_LINETO:436x1 = coords[0]; y1 = coords[1];437HANDLELINETO(pd, x1, y1, {oom = JNI_TRUE;});438break;439case java_awt_geom_PathIterator_SEG_QUADTO:440x1 = coords[0]; y1 = coords[1];441x2 = coords[2]; y2 = coords[3];442HANDLEQUADTO(pd, x1, y1, x2, y2, {oom = JNI_TRUE;});443break;444case java_awt_geom_PathIterator_SEG_CUBICTO:445x1 = coords[0]; y1 = coords[1];446x2 = coords[2]; y2 = coords[3];447x3 = coords[4]; y3 = coords[5];448HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, {oom = JNI_TRUE;});449break;450case java_awt_geom_PathIterator_SEG_CLOSE:451HANDLECLOSE(pd, {oom = JNI_TRUE;});452break;453default:454JNU_ThrowInternalError(env, "bad path segment type");455return;456}457458if (oom) {459JNU_ThrowOutOfMemoryError(env, "path segment data");460return;461}462}463464JNIEXPORT void JNICALL465Java_sun_java2d_pipe_ShapeSpanIterator_getPathBox466(JNIEnv *env, jobject sr, jintArray spanbox)467{468pathData *pd;469jint coords[4];470471pd = GetSpanData(env, sr, STATE_PATH_DONE, STATE_PATH_DONE);472if (pd == NULL) {473return;474}475476ShapeSIGetPathBox(env, pd, coords);477478(*env)->SetIntArrayRegion(env, spanbox, 0, 4, coords);479}480481JNIEXPORT void JNICALL482Java_sun_java2d_pipe_ShapeSpanIterator_intersectClipBox483(JNIEnv *env, jobject ri, jint clox, jint cloy, jint chix, jint chiy)484{485pathData *pd;486487pd = GetSpanData(env, ri, STATE_PATH_DONE, STATE_PATH_DONE);488if (pd == NULL) {489return;490}491492ShapeSIIntersectClipBox(env, pd, clox, cloy, chix, chiy);493}494495JNIEXPORT jboolean JNICALL496Java_sun_java2d_pipe_ShapeSpanIterator_nextSpan497(JNIEnv *env, jobject sr, jintArray spanbox)498{499pathData *pd;500jboolean ret;501jint coords[4];502503pd = GetSpanData(env, sr, STATE_PATH_DONE, STATE_SPAN_STARTED);504if (pd == NULL) {505return JNI_FALSE;506}507508ret = ShapeSINextSpan(pd, coords);509if (ret) {510(*env)->SetIntArrayRegion(env, spanbox, 0, 4, coords);511}512513return ret;514}515516JNIEXPORT void JNICALL517Java_sun_java2d_pipe_ShapeSpanIterator_skipDownTo518(JNIEnv *env, jobject sr, jint y)519{520pathData *pd;521522pd = GetSpanData(env, sr, STATE_PATH_DONE, STATE_SPAN_STARTED);523if (pd == NULL) {524return;525}526527ShapeSISkipDownTo(pd, y);528}529530JNIEXPORT jlong JNICALL531Java_sun_java2d_pipe_ShapeSpanIterator_getNativeIterator532(JNIEnv *env, jobject sr)533{534return ptr_to_jlong(&ShapeSIFuncs);535}536537JNIEXPORT void JNICALL538Java_sun_java2d_pipe_ShapeSpanIterator_dispose539(JNIEnv *env, jobject sr)540{541pathData *pd = (pathData *) JNU_GetLongFieldAsPtr(env, sr, pSpanDataID);542543if (pd == NULL) {544return;545}546547if (pd->segments != NULL) {548free(pd->segments);549}550if (pd->segmentTable != NULL) {551free(pd->segmentTable);552}553free(pd);554555(*env)->SetLongField(env, sr, pSpanDataID, jlong_zero);556}557558#define OUT_XLO 1559#define OUT_XHI 2560#define OUT_YLO 4561#define OUT_YHI 8562563#define CALCULATE_OUTCODES(pd, outc, x, y) \564do { \565if (y <= pd->loy) outc = OUT_YLO; \566else if (y >= pd->hiy) outc = OUT_YHI; \567else outc = 0; \568if (x <= pd->lox) outc |= OUT_XLO; \569else if (x >= pd->hix) outc |= OUT_XHI; \570} while (0)571572JNIEXPORT void JNICALL573Java_sun_java2d_pipe_ShapeSpanIterator_appendPoly574(JNIEnv *env, jobject sr,575jintArray xArray, jintArray yArray, jint nPoints,576jint ixoff, jint iyoff)577{578pathData *pd;579int i;580jint *xPoints, *yPoints;581jboolean oom = JNI_FALSE;582jfloat xoff = (jfloat) ixoff, yoff = (jfloat) iyoff;583584pd = GetSpanData(env, sr, STATE_HAVE_CLIP, STATE_HAVE_CLIP);585if (pd == NULL) {586return;587}588589pd->evenodd = JNI_TRUE;590pd->state = STATE_HAVE_RULE;591if (pd->adjust) {592xoff += 0.25f;593yoff += 0.25f;594}595596if (xArray == NULL || yArray == NULL) {597JNU_ThrowNullPointerException(env, "polygon data arrays");598return;599}600if ((*env)->GetArrayLength(env, xArray) < nPoints ||601(*env)->GetArrayLength(env, yArray) < nPoints)602{603JNU_ThrowArrayIndexOutOfBoundsException(env, "polygon data arrays");604return;605}606607if (nPoints > 0) {608xPoints = (*env)->GetPrimitiveArrayCritical(env, xArray, NULL);609if (xPoints != NULL) {610yPoints = (*env)->GetPrimitiveArrayCritical(env, yArray, NULL);611if (yPoints != NULL) {612jint outc0;613jfloat x, y;614615x = xPoints[0] + xoff;616y = yPoints[0] + yoff;617CALCULATE_OUTCODES(pd, outc0, x, y);618pd->movx = pd->curx = x;619pd->movy = pd->cury = y;620pd->pathlox = pd->pathhix = x;621pd->pathloy = pd->pathhiy = y;622pd->first = 0;623for (i = 1; !oom && i < nPoints; i++) {624jint outc1;625626x = xPoints[i] + xoff;627y = yPoints[i] + yoff;628if (y == pd->cury) {629/* Horizontal segment - do not append */630if (x != pd->curx) {631/* Not empty segment - track change in X */632CALCULATE_OUTCODES(pd, outc0, x, y);633pd->curx = x;634if (pd->pathlox > x) pd->pathlox = x;635if (pd->pathhix < x) pd->pathhix = x;636}637continue;638}639CALCULATE_OUTCODES(pd, outc1, x, y);640outc0 &= outc1;641if (outc0 == 0) {642oom = !appendSegment(pd, pd->curx, pd->cury, x, y);643} else if (outc0 == OUT_XLO) {644oom = !appendSegment(pd, (jfloat) pd->lox, pd->cury,645(jfloat) pd->lox, y);646}647if (pd->pathlox > x) pd->pathlox = x;648if (pd->pathloy > y) pd->pathloy = y;649if (pd->pathhix < x) pd->pathhix = x;650if (pd->pathhiy < y) pd->pathhiy = y;651outc0 = outc1;652pd->curx = x;653pd->cury = y;654}655(*env)->ReleasePrimitiveArrayCritical(env, yArray,656yPoints, JNI_ABORT);657}658(*env)->ReleasePrimitiveArrayCritical(env, xArray,659xPoints, JNI_ABORT);660}661if (xPoints == NULL || yPoints == NULL) {662return;663}664}665if (!oom) {666HANDLEENDPATH(pd, {oom = JNI_TRUE;});667}668if (oom) {669JNU_ThrowOutOfMemoryError(env, "path segment data");670}671}672673JNIEXPORT void JNICALL674Java_sun_java2d_pipe_ShapeSpanIterator_moveTo675(JNIEnv *env, jobject sr, jfloat x0, jfloat y0)676{677pathData *pd;678679pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);680if (pd == NULL) {681return;682}683684HANDLEMOVETO(pd, x0, y0,685{JNU_ThrowOutOfMemoryError(env, "path segment data");});686}687688JNIEXPORT void JNICALL689Java_sun_java2d_pipe_ShapeSpanIterator_lineTo690(JNIEnv *env, jobject sr, jfloat x1, jfloat y1)691{692pathData *pd;693694pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);695if (pd == NULL) {696return;697}698699HANDLELINETO(pd, x1, y1,700{JNU_ThrowOutOfMemoryError(env, "path segment data");});701}702703JNIEXPORT void JNICALL704Java_sun_java2d_pipe_ShapeSpanIterator_quadTo705(JNIEnv *env, jobject sr,706jfloat xm, jfloat ym, jfloat x1, jfloat y1)707{708pathData *pd;709710pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);711if (pd == NULL) {712return;713}714715HANDLEQUADTO(pd, xm, ym, x1, y1,716{JNU_ThrowOutOfMemoryError(env, "path segment data");});717}718719JNIEXPORT void JNICALL720Java_sun_java2d_pipe_ShapeSpanIterator_curveTo721(JNIEnv *env, jobject sr,722jfloat xm, jfloat ym,723jfloat xn, jfloat yn,724jfloat x1, jfloat y1)725{726pathData *pd;727728pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);729if (pd == NULL) {730return;731}732733HANDLECUBICTO(pd, xm, ym, xn, yn, x1, y1,734{JNU_ThrowOutOfMemoryError(env, "path segment data");});735}736737JNIEXPORT void JNICALL738Java_sun_java2d_pipe_ShapeSpanIterator_closePath739(JNIEnv *env, jobject sr)740{741pathData *pd;742743pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);744if (pd == NULL) {745return;746}747748HANDLECLOSE(pd, {JNU_ThrowOutOfMemoryError(env, "path segment data");});749}750751JNIEXPORT void JNICALL752Java_sun_java2d_pipe_ShapeSpanIterator_pathDone753(JNIEnv *env, jobject sr)754{755pathData *pd;756757pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);758if (pd == NULL) {759return;760}761762HANDLEENDPATH(pd, {JNU_ThrowOutOfMemoryError(env, "path segment data");});763}764765JNIEXPORT jlong JNICALL766Java_sun_java2d_pipe_ShapeSpanIterator_getNativeConsumer767(JNIEnv *env, jobject sr)768{769pathData *pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);770771if (pd == NULL) {772return jlong_zero;773}774775return ptr_to_jlong(&(pd->funcs));776}777778static jboolean779PCMoveTo(PathConsumerVec *consumer,780jfloat x0, jfloat y0)781{782pathData *pd = (pathData *) consumer;783jboolean oom = JNI_FALSE;784785HANDLEMOVETO(pd, x0, y0, {oom = JNI_TRUE;});786787return oom;788}789790static jboolean791PCLineTo(PathConsumerVec *consumer,792jfloat x1, jfloat y1)793{794pathData *pd = (pathData *) consumer;795jboolean oom = JNI_FALSE;796797HANDLELINETO(pd, x1, y1, {oom = JNI_TRUE;});798799return oom;800}801802static jboolean803PCQuadTo(PathConsumerVec *consumer,804jfloat x1, jfloat y1,805jfloat x2, jfloat y2)806{807pathData *pd = (pathData *) consumer;808jboolean oom = JNI_FALSE;809810HANDLEQUADTO(pd, x1, y1, x2, y2, {oom = JNI_TRUE;});811812return oom;813}814815static jboolean816PCCubicTo(PathConsumerVec *consumer,817jfloat x1, jfloat y1,818jfloat x2, jfloat y2,819jfloat x3, jfloat y3)820{821pathData *pd = (pathData *) consumer;822jboolean oom = JNI_FALSE;823824HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, {oom = JNI_TRUE;});825826return oom;827}828829static jboolean830PCClosePath(PathConsumerVec *consumer)831{832pathData *pd = (pathData *) consumer;833jboolean oom = JNI_FALSE;834835HANDLECLOSE(pd, {oom = JNI_TRUE;});836837return oom;838}839840static jboolean841PCPathDone(PathConsumerVec *consumer)842{843pathData *pd = (pathData *) consumer;844jboolean oom = JNI_FALSE;845846HANDLEENDPATH(pd, {oom = JNI_TRUE;});847848return oom;849}850851/*852* REMIND: CDECL needed for WIN32 "qsort"853*/854855#ifdef _WIN32856#define CDECL __cdecl857#else858#define CDECL859#endif860861#define SUBDIVIDE_MAX 10862#define MAX_FLAT_SQ (1.0 * 1.0)863#define GROW_SIZE 20864#define ERRSTEP_MAX (0x7fffffff)865#define FRACTTOJINT(f) ((jint) ((f) * (double) ERRSTEP_MAX))866867#define minmax2(v1, v2, min, max) \868do { \869if (v1 < v2) { \870min = v1; \871max = v2; \872} else { \873min = v2; \874max = v1; \875} \876} while(0)877878#define minmax3(v1, v2, v3, min, max) \879do { \880if (v1 < v2) { \881if (v1 < v3) { \882min = v1; \883max = (v2 < v3) ? v3 : v2; \884} else { \885max = v2; \886min = v3; \887} \888} else { \889if (v1 < v3) { \890max = v3; \891min = v2; \892} else { \893max = v1; \894min = (v2 < v3) ? v2 : v3; \895} \896} \897} while (0)898899#define minmax4(v1, v2, v3, v4, min, max) \900do { \901if (v1 < v2) { \902if (v3 < v4) { \903max = (v2 < v4) ? v4 : v2; \904min = (v1 < v3) ? v1 : v3; \905} else { \906max = (v2 < v3) ? v3 : v2; \907min = (v1 < v4) ? v1 : v4; \908} \909} else { \910if (v3 < v4) { \911max = (v1 < v4) ? v4 : v1; \912min = (v2 < v3) ? v2 : v3; \913} else { \914max = (v1 < v3) ? v3 : v1; \915min = (v2 < v4) ? v2 : v4; \916} \917} \918} while(0)919920static jfloat921ptSegDistSq(jfloat x0, jfloat y0,922jfloat x1, jfloat y1,923jfloat px, jfloat py)924{925jfloat dotprod, projlenSq;926927/* Adjust vectors relative to x0,y0 */928/* x1,y1 becomes relative vector from x0,y0 to end of segment */929x1 -= x0;930y1 -= y0;931/* px,py becomes relative vector from x0,y0 to test point */932px -= x0;933py -= y0;934dotprod = px * x1 + py * y1;935if (dotprod <= 0.0) {936/* px,py is on the side of x0,y0 away from x1,y1 */937/* distance to segment is length of px,py vector */938/* "length of its (clipped) projection" is now 0.0 */939projlenSq = 0.0;940} else {941/* switch to backwards vectors relative to x1,y1 */942/* x1,y1 are already the negative of x0,y0=>x1,y1 */943/* to get px,py to be the negative of px,py=>x1,y1 */944/* the dot product of two negated vectors is the same */945/* as the dot product of the two normal vectors */946px = x1 - px;947py = y1 - py;948dotprod = px * x1 + py * y1;949if (dotprod <= 0.0) {950/* px,py is on the side of x1,y1 away from x0,y0 */951/* distance to segment is length of (backwards) px,py vector */952/* "length of its (clipped) projection" is now 0.0 */953projlenSq = 0.0;954} else {955/* px,py is between x0,y0 and x1,y1 */956/* dotprod is the length of the px,py vector */957/* projected on the x1,y1=>x0,y0 vector times the */958/* length of the x1,y1=>x0,y0 vector */959projlenSq = dotprod * dotprod / (x1 * x1 + y1 * y1);960}961}962/* Distance to line is now the length of the relative point */963/* vector minus the length of its projection onto the line */964/* (which is zero if the projection falls outside the range */965/* of the line segment). */966return px * px + py * py - projlenSq;967}968969static jboolean970appendSegment(pathData *pd,971jfloat x0, jfloat y0,972jfloat x1, jfloat y1)973{974jbyte windDir;975jint istartx, istarty, ilasty;976jfloat dx, dy, slope;977jfloat ystartbump;978jint bumpx, bumperr, error;979segmentData *seg;980981if (y0 > y1) {982jfloat t;983t = x0; x0 = x1; x1 = t;984t = y0; y0 = y1; y1 = t;985windDir = -1;986} else {987windDir = 1;988}989/* We want to iterate at every horizontal pixel center (HPC) crossing. */990/* First calculate next highest HPC we will cross at the start. */991istarty = (jint) ceil(y0 - 0.5f);992/* Then calculate next highest HPC we would cross at the end. */993ilasty = (jint) ceil(y1 - 0.5f);994/* Ignore if we start and end outside clip, or on the same scanline. */995if (istarty >= ilasty || istarty >= pd->hiy || ilasty <= pd->loy) {996return JNI_TRUE;997}998999/* We will need to insert this segment, check for room. */1000if (pd->numSegments >= pd->segmentsSize) {1001segmentData *newSegs;1002int newSize = pd->segmentsSize + GROW_SIZE;1003newSegs = (segmentData *) calloc(newSize, sizeof(segmentData));1004if (newSegs == NULL) {1005return JNI_FALSE;1006}1007if (pd->segments != NULL) {1008memcpy(newSegs, pd->segments,1009sizeof(segmentData) * pd->segmentsSize);1010free(pd->segments);1011}1012pd->segments = newSegs;1013pd->segmentsSize = newSize;1014}10151016dx = x1 - x0;1017dy = y1 - y0;1018slope = dx / dy;10191020/*1021* The Y coordinate of the first HPC was calculated as istarty. We1022* now need to calculate the corresponding X coordinate (both integer1023* version for span start coordinate and float version for sub-pixel1024* error calculation).1025*/1026/* First, how far does y bump to get to next HPC? */1027ystartbump = istarty + 0.5f - y0;1028/* Now, bump the float x coordinate to get X sample at that HPC. */1029x0 += ystartbump * dx / dy;1030/* Now calculate the integer coordinate that such a span starts at. */1031/* NOTE: Span inclusion is based on vertical pixel centers (VPC). */1032istartx = (jint) ceil(x0 - 0.5f);1033/* What is the lower bound of the per-scanline change in the X coord? */1034bumpx = (jint) floor(slope);1035/* What is the subpixel amount by which the bumpx is off? */1036bumperr = FRACTTOJINT(slope - floor(slope));1037/* Finally, find out how far the x coordinate can go before next VPC. */1038error = FRACTTOJINT(x0 - (istartx - 0.5f));10391040seg = &pd->segments[pd->numSegments++];1041seg->curx = istartx;1042seg->cury = istarty;1043seg->lasty = ilasty;1044seg->error = error;1045seg->bumpx = bumpx;1046seg->bumperr = bumperr;1047seg->windDir = windDir;1048return JNI_TRUE;1049}10501051/*1052* Lines don't really need to be subdivided, but this function performs1053* the same trivial rejections and reductions that the curve subdivision1054* functions perform before it hands the coordinates off to the appendSegment1055* function.1056*/1057static jboolean1058subdivideLine(pathData *pd, int level,1059jfloat x0, jfloat y0,1060jfloat x1, jfloat y1)1061{1062jfloat miny, maxy;1063jfloat minx, maxx;10641065minmax2(x0, x1, minx, maxx);1066minmax2(y0, y1, miny, maxy);10671068if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) {1069return JNI_TRUE;1070}1071if (maxx <= pd->lox) {1072return appendSegment(pd, maxx, y0, maxx, y1);1073}10741075return appendSegment(pd, x0, y0, x1, y1);1076}10771078static jboolean1079subdivideQuad(pathData *pd, int level,1080jfloat x0, jfloat y0,1081jfloat x1, jfloat y1,1082jfloat x2, jfloat y2)1083{1084jfloat miny, maxy;1085jfloat minx, maxx;10861087minmax3(x0, x1, x2, minx, maxx);1088minmax3(y0, y1, y2, miny, maxy);10891090if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) {1091return JNI_TRUE;1092}1093if (maxx <= pd->lox) {1094return appendSegment(pd, maxx, y0, maxx, y2);1095}10961097if (level < SUBDIVIDE_MAX) {1098/* Test if the curve is flat enough for insertion. */1099if (ptSegDistSq(x0, y0, x2, y2, x1, y1) > MAX_FLAT_SQ) {1100jfloat cx1, cx2;1101jfloat cy1, cy2;11021103cx1 = (x0 + x1) / 2.0f;1104cx2 = (x1 + x2) / 2.0f;1105x1 = (cx1 + cx2) / 2.0f;11061107cy1 = (y0 + y1) / 2.0f;1108cy2 = (y1 + y2) / 2.0f;1109y1 = (cy1 + cy2) / 2.0f;11101111level++;1112return (subdivideQuad(pd, level, x0, y0, cx1, cy1, x1, y1) &&1113subdivideQuad(pd, level, x1, y1, cx2, cy2, x2, y2));1114}1115}11161117return appendSegment(pd, x0, y0, x2, y2);1118}11191120static jboolean1121subdivideCubic(pathData *pd, int level,1122jfloat x0, jfloat y0,1123jfloat x1, jfloat y1,1124jfloat x2, jfloat y2,1125jfloat x3, jfloat y3)1126{1127jfloat miny, maxy;1128jfloat minx, maxx;11291130minmax4(x0, x1, x2, x3, minx, maxx);1131minmax4(y0, y1, y2, y3, miny, maxy);11321133if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) {1134return JNI_TRUE;1135}1136if (maxx <= pd->lox) {1137return appendSegment(pd, maxx, y0, maxx, y3);1138}11391140if (level < SUBDIVIDE_MAX) {1141/* Test if the curve is flat enough for insertion. */1142if (ptSegDistSq(x0, y0, x3, y3, x1, y1) > MAX_FLAT_SQ ||1143ptSegDistSq(x0, y0, x3, y3, x2, y2) > MAX_FLAT_SQ)1144{1145jfloat ctrx, cx12, cx21;1146jfloat ctry, cy12, cy21;11471148ctrx = (x1 + x2) / 2.0f;1149x1 = (x0 + x1) / 2.0f;1150x2 = (x2 + x3) / 2.0f;1151cx12 = (x1 + ctrx) / 2.0f;1152cx21 = (ctrx + x2) / 2.0f;1153ctrx = (cx12 + cx21) / 2.0f;11541155ctry = (y1 + y2) / 2.0f;1156y1 = (y0 + y1) / 2.0f;1157y2 = (y2 + y3) / 2.0f;1158cy12 = (y1 + ctry) / 2.0f;1159cy21 = (ctry + y2) / 2.0f;1160ctry = (cy12 + cy21) / 2.0f;11611162level++;1163return (subdivideCubic(pd, level, x0, y0, x1, y1,1164cx12, cy12, ctrx, ctry) &&1165subdivideCubic(pd, level, ctrx, ctry, cx21, cy21,1166x2, y2, x3, y3));1167}1168}11691170return appendSegment(pd, x0, y0, x3, y3);1171}11721173static int CDECL1174sortSegmentsByLeadingY(const void *elem1, const void *elem2)1175{1176segmentData *seg1 = *(segmentData **)elem1;1177segmentData *seg2 = *(segmentData **)elem2;11781179if (seg1->cury < seg2->cury) {1180return -1;1181}1182if (seg1->cury > seg2->cury) {1183return 1;1184}1185if (seg1->curx < seg2->curx) {1186return -1;1187}1188if (seg1->curx > seg2->curx) {1189return 1;1190}1191if (seg1->lasty < seg2->lasty) {1192return -1;1193}1194if (seg1->lasty > seg2->lasty) {1195return 1;1196}1197return 0;1198}11991200static void *1201ShapeSIOpen(JNIEnv *env, jobject iterator)1202{1203return GetSpanData(env, iterator, STATE_PATH_DONE, STATE_PATH_DONE);1204}12051206static void1207ShapeSIClose(JNIEnv *env, void *private)1208{1209}12101211static void1212ShapeSIGetPathBox(JNIEnv *env, void *private, jint pathbox[])1213{1214pathData *pd = (pathData *)private;12151216pathbox[0] = (jint) floor(pd->pathlox);1217pathbox[1] = (jint) floor(pd->pathloy);1218pathbox[2] = (jint) ceil(pd->pathhix);1219pathbox[3] = (jint) ceil(pd->pathhiy);1220}12211222/* Adjust the clip box from the given bounds. Used to constrain1223the output to a device clip1224*/1225static void1226ShapeSIIntersectClipBox(JNIEnv *env, void *private,1227jint clox, jint cloy, jint chix, jint chiy)1228{1229pathData *pd = (pathData *)private;12301231if (clox > pd->lox) {1232pd->lox = clox;1233}1234if (cloy > pd->loy) {1235pd->loy = cloy;1236}1237if (chix < pd->hix) {1238pd->hix = chix;1239}1240if (chiy < pd->hiy) {1241pd->hiy = chiy;1242}1243}12441245static jboolean1246ShapeSINextSpan(void *state, jint spanbox[])1247{1248pathData *pd = (pathData *)state;1249int lo, cur, new, hi;1250int num = pd->numSegments;1251jint x0, x1, y0, err;1252jint loy;1253int ret = JNI_FALSE;1254segmentData **segmentTable;1255segmentData *seg;12561257if (pd->state != STATE_SPAN_STARTED) {1258if (!initSegmentTable(pd)) {1259/* REMIND: - throw exception? */1260pd->lowSegment = num;1261return JNI_FALSE;1262}1263}12641265lo = pd->lowSegment;1266cur = pd->curSegment;1267hi = pd->hiSegment;1268num = pd->numSegments;1269loy = pd->loy;1270segmentTable = pd->segmentTable;12711272while (lo < num) {1273if (cur < hi) {1274seg = segmentTable[cur];1275x0 = seg->curx;1276if (x0 >= pd->hix) {1277cur = hi;1278continue;1279}1280if (x0 < pd->lox) {1281x0 = pd->lox;1282}12831284if (pd->evenodd) {1285cur += 2;1286if (cur <= hi) {1287x1 = segmentTable[cur - 1]->curx;1288} else {1289x1 = pd->hix;1290}1291} else {1292int wind = seg->windDir;1293cur++;12941295while (JNI_TRUE) {1296if (cur >= hi) {1297x1 = pd->hix;1298break;1299}1300seg = segmentTable[cur++];1301wind += seg->windDir;1302if (wind == 0) {1303x1 = seg->curx;1304break;1305}1306}1307}13081309if (x1 > pd->hix) {1310x1 = pd->hix;1311}1312if (x1 <= x0) {1313continue;1314}1315spanbox[0] = x0;1316spanbox[1] = loy;1317spanbox[2] = x1;1318spanbox[3] = loy + 1;1319ret = JNI_TRUE;1320break;1321}13221323if (++loy >= pd->hiy) {1324lo = cur = hi = num;1325break;1326}13271328/* Go through active segments and toss which end "above" loy */1329cur = new = hi;1330while (--cur >= lo) {1331seg = segmentTable[cur];1332if (seg->lasty > loy) {1333segmentTable[--new] = seg;1334}1335}13361337lo = new;1338if (lo == hi && lo < num) {1339/* The current list of segments is empty so we need to1340* jump to the beginning of the next set of segments.1341* Since the segments are not clipped to the output1342* area we need to make sure we don't jump "backwards"1343*/1344seg = segmentTable[lo];1345if (loy < seg->cury) {1346loy = seg->cury;1347}1348}13491350/* Go through new segments and accept any which start "above" loy */1351while (hi < num && segmentTable[hi]->cury <= loy) {1352hi++;1353}13541355/* Update and sort the active segments by x0 */1356for (cur = lo; cur < hi; cur++) {1357seg = segmentTable[cur];13581359/* First update the x0, y0 of the segment */1360x0 = seg->curx;1361y0 = seg->cury;1362err = seg->error;1363if (++y0 == loy) {1364x0 += seg->bumpx;1365err += seg->bumperr;1366x0 -= (err >> 31);1367err &= ERRSTEP_MAX;1368} else {1369jlong steps = loy;1370steps -= y0 - 1;1371y0 = loy;1372x0 += (jint) (steps * seg->bumpx);1373steps = err + (steps * seg->bumperr);1374x0 += (jint) (steps >> 31);1375err = ((jint) steps) & ERRSTEP_MAX;1376}1377seg->curx = x0;1378seg->cury = y0;1379seg->error = err;13801381/* Then make sure the segment is sorted by x0 */1382for (new = cur; new > lo; new--) {1383segmentData *seg2 = segmentTable[new - 1];1384if (seg2->curx <= x0) {1385break;1386}1387segmentTable[new] = seg2;1388}1389segmentTable[new] = seg;1390}1391cur = lo;1392}13931394pd->lowSegment = lo;1395pd->hiSegment = hi;1396pd->curSegment = cur;1397pd->loy = loy;1398return ret;1399}14001401static void1402ShapeSISkipDownTo(void *private, jint y)1403{1404pathData *pd = (pathData *)private;14051406if (pd->state != STATE_SPAN_STARTED) {1407if (!initSegmentTable(pd)) {1408/* REMIND: - throw exception? */1409pd->lowSegment = pd->numSegments;1410return;1411}1412}14131414/* Make sure we are jumping forward */1415if (pd->loy < y) {1416/* Pretend like we just finished with the span line y-1... */1417pd->loy = y - 1;1418pd->curSegment = pd->hiSegment; /* no more segments on that line */1419}1420}14211422static jboolean1423initSegmentTable(pathData *pd)1424{1425int i, cur, num, loy;1426segmentData **segmentTable;1427segmentTable = malloc(pd->numSegments * sizeof(segmentData *));1428if (segmentTable == NULL) {1429return JNI_FALSE;1430}1431pd->state = STATE_SPAN_STARTED;1432for (i = 0; i < pd->numSegments; i++) {1433segmentTable[i] = &pd->segments[i];1434}1435qsort(segmentTable, pd->numSegments, sizeof(segmentData *),1436sortSegmentsByLeadingY);14371438pd->segmentTable = segmentTable;14391440/* Skip to the first segment that ends below the top clip edge */1441cur = 0;1442num = pd->numSegments;1443loy = pd->loy;1444while (cur < num && segmentTable[cur]->lasty <= loy) {1445cur++;1446}1447pd->lowSegment = pd->curSegment = pd->hiSegment = cur;14481449/* Prepare for next action to increment loy and prepare new segments */1450pd->loy--;14511452return JNI_TRUE;1453}145414551456