1 /*
2  * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #include <stdlib.h>
27 #include <string.h>
28 #include <math.h>
29 
30 #include "jni.h"
31 #include "jni_util.h"
32 #include <jlong.h>
33 
34 #include "j2d_md.h"
35 
36 #include "PathConsumer2D.h"
37 #include "SpanIterator.h"
38 
39 #include "sun_java2d_pipe_ShapeSpanIterator.h"
40 #include "java_awt_geom_PathIterator.h"
41 
42 /*
43  * This structure holds all of the information needed to trace and
44  * manage a single line segment of the shape's outline.
45  */
46 typedef struct {
47     jint curx;
48     jint cury;
49     jint lasty;
50     jint error;
51     jint bumpx;
52     jint bumperr;
53     jbyte windDir;
54     jbyte pad0;
55     jbyte pad1;
56     jbyte pad2;
57 } segmentData;
58 
59 /*
60  * This structure holds all of the information needed to trace out
61  * the entire span list of a single Shape object.
62  */
63 typedef struct {
64     PathConsumerVec funcs;      /* Native PathConsumer function vector */
65 
66     char state;                 /* Path delivery sequence state */
67     char evenodd;               /* non-zero if path has EvenOdd winding rule */
68     char first;                 /* non-zero if first path segment */
69     char adjust;                /* normalize to nearest (0.25, 0.25) */
70 
71     jint lox;                   /* clip bbox low X */
72     jint loy;                   /* clip bbox low Y */
73     jint hix;                   /* clip bbox high X */
74     jint hiy;                   /* clip bbox high Y */
75 
76     jfloat curx;                /* current path point X coordinate */
77     jfloat cury;                /* current path point Y coordinate */
78     jfloat movx;                /* last moveto X coordinate */
79     jfloat movy;                /* last moveto Y coordinate */
80 
81     jfloat adjx;                /* last X coordinate adjustment */
82     jfloat adjy;                /* last Y coordinate adjustment */
83 
84     jfloat pathlox;             /* lowest X coordinate in path */
85     jfloat pathloy;             /* lowest Y coordinate in path */
86     jfloat pathhix;             /* highest X coordinate in path */
87     jfloat pathhiy;             /* highest Y coordinate in path */
88 
89     segmentData *segments;      /* pointer to array of path segments */
90     int numSegments;            /* number of segments entries in array */
91     int segmentsSize;           /* size of array of path segments */
92 
93     int lowSegment;             /* lower limit of segments in active range */
94     int curSegment;             /* index of next active segment to return */
95     int hiSegment;              /* upper limit of segments in active range */
96 
97     segmentData **segmentTable; /* pointers to segments being stepped */
98 } pathData;
99 
100 #define STATE_INIT              0
101 #define STATE_HAVE_CLIP         1
102 #define STATE_HAVE_RULE         2
103 #define STATE_PATH_DONE         3
104 #define STATE_SPAN_STARTED      4
105 
106 static jboolean subdivideLine(pathData *pd, int level,
107                               jfloat x0, jfloat y0,
108                               jfloat x1, jfloat y1);
109 static jboolean subdivideQuad(pathData *pd, int level,
110                               jfloat x0, jfloat y0,
111                               jfloat x1, jfloat y1,
112                               jfloat x2, jfloat y2);
113 static jboolean subdivideCubic(pathData *pd, int level,
114                                jfloat x0, jfloat y0,
115                                jfloat x1, jfloat y1,
116                                jfloat x2, jfloat y2,
117                                jfloat x3, jfloat y3);
118 static jboolean appendSegment(pathData *pd,
119                               jfloat x0, jfloat y0,
120                               jfloat x1, jfloat y1);
121 static jboolean initSegmentTable(pathData *pd);
122 
123 static void *ShapeSIOpen(JNIEnv *env, jobject iterator);
124 static void ShapeSIClose(JNIEnv *env, void *private);
125 static void ShapeSIGetPathBox(JNIEnv *env, void *private, jint pathbox[]);
126 static void ShapeSIIntersectClipBox(JNIEnv *env, void *private,
127                                         jint lox, jint loy, jint hix, jint hiy);
128 static jboolean ShapeSINextSpan(void *state, jint spanbox[]);
129 static void ShapeSISkipDownTo(void *private, jint y);
130 
131 static jfieldID pSpanDataID;
132 
133 static SpanIteratorFuncs ShapeSIFuncs = {
134     ShapeSIOpen,
135     ShapeSIClose,
136     ShapeSIGetPathBox,
137     ShapeSIIntersectClipBox,
138     ShapeSINextSpan,
139     ShapeSISkipDownTo
140 };
141 
142 static LineToFunc PCLineTo;
143 static MoveToFunc PCMoveTo;
144 static QuadToFunc PCQuadTo;
145 static CubicToFunc PCCubicTo;
146 static ClosePathFunc PCClosePath;
147 static PathDoneFunc PCPathDone;
148 
149 #define PDBOXPOINT(pd, x, y)                                    \
150     do {                                                        \
151         if (pd->first) {                                        \
152             pd->pathlox = pd->pathhix = x;                      \
153             pd->pathloy = pd->pathhiy = y;                      \
154             pd->first = 0;                                      \
155         } else {                                                \
156             if (pd->pathlox > x) pd->pathlox = x;               \
157             if (pd->pathloy > y) pd->pathloy = y;               \
158             if (pd->pathhix < x) pd->pathhix = x;               \
159             if (pd->pathhiy < y) pd->pathhiy = y;               \
160         }                                                       \
161     } while (0)
162 
163 /*
164  * _ADJUST is the internal macro used to adjust a new endpoint
165  * and then invoke the "additional" code from the callers below
166  * which will adjust the control points as needed to match.
167  *
168  * When the "additional" code is executed, newadj[xy] will
169  * contain the adjustment applied to the new endpoint and
170  * pd->adj[xy] will still contain the previous adjustment
171  * that was applied to the old endpoint.
172  */
173 #define _ADJUST(pd, x, y, additional)                           \
174     do {                                                        \
175         if (pd->adjust) {                                       \
176             jfloat newx = (jfloat) floor(x + 0.25f) + 0.25f;    \
177             jfloat newy = (jfloat) floor(y + 0.25f) + 0.25f;    \
178             jfloat newadjx = newx - x;                          \
179             jfloat newadjy = newy - y;                          \
180             x = newx;                                           \
181             y = newy;                                           \
182             additional;                                         \
183             pd->adjx = newadjx;                                 \
184             pd->adjy = newadjy;                                 \
185         }                                                       \
186     } while (0)
187 
188 /*
189  * Adjust a single endpoint with no control points.
190  * "additional" code is a null statement.
191  */
192 #define ADJUST1(pd, x1, y1)                                     \
193     _ADJUST(pd, x1, y1,                                         \
194             do {                                                \
195             } while (0))
196 
197 /*
198  * Adjust a quadratic curve.  The _ADJUST macro takes care
199  * of the new endpoint and the "additional" code adjusts
200  * the single quadratic control point by the averge of
201  * the prior and the new adjustment amounts.
202  */
203 #define ADJUST2(pd, x1, y1, x2, y2)                             \
204     _ADJUST(pd, x2, y2,                                         \
205             do {                                                \
206                 x1 += (pd->adjx + newadjy) / 2;                 \
207                 y1 += (pd->adjy + newadjy) / 2;                 \
208             } while (0))
209 
210 /*
211  * Adjust a cubic curve.  The _ADJUST macro takes care
212  * of the new endpoint and the "additional" code adjusts
213  * the first of the two cubic control points by the same
214  * amount that the prior endpoint was adjusted and then
215  * adjusts the second of the two control points by the
216  * same amount as the new endpoint was adjusted.  This
217  * keeps the tangent lines from xy0 to xy1 and xy3 to xy2
218  * parallel before and after the adjustment.
219  */
220 #define ADJUST3(pd, x1, y1, x2, y2, x3, y3)                     \
221     _ADJUST(pd, x3, y3,                                         \
222             do {                                                \
223                 x1 += pd->adjx;                                 \
224                 y1 += pd->adjy;                                 \
225                 x2 += newadjx;                                  \
226                 y2 += newadjy;                                  \
227             } while (0))
228 
229 #define HANDLEMOVETO(pd, x0, y0, OOMERR)                        \
230     do {                                                        \
231         HANDLECLOSE(pd, OOMERR);                                \
232         ADJUST1(pd, x0, y0);                                    \
233         pd->movx = x0;                                          \
234         pd->movy = y0;                                          \
235         PDBOXPOINT(pd, x0, y0);                                 \
236         pd->curx = x0;                                          \
237         pd->cury = y0;                                          \
238     } while (0)
239 
240 #define HANDLELINETO(pd, x1, y1, OOMERR)                        \
241     do {                                                        \
242         ADJUST1(pd, x1, y1);                                    \
243         if (!subdivideLine(pd, 0,                               \
244                            pd->curx, pd->cury,                  \
245                            x1, y1)) {                           \
246             OOMERR;                                             \
247             break;                                              \
248         }                                                       \
249         PDBOXPOINT(pd, x1, y1);                                 \
250         pd->curx = x1;                                          \
251         pd->cury = y1;                                          \
252     } while (0)
253 
254 #define HANDLEQUADTO(pd, x1, y1, x2, y2, OOMERR)                \
255     do {                                                        \
256         ADJUST2(pd, x1, y1, x2, y2);                            \
257         if (!subdivideQuad(pd, 0,                               \
258                            pd->curx, pd->cury,                  \
259                            x1, y1, x2, y2)) {                   \
260             OOMERR;                                             \
261             break;                                              \
262         }                                                       \
263         PDBOXPOINT(pd, x1, y1);                                 \
264         PDBOXPOINT(pd, x2, y2);                                 \
265         pd->curx = x2;                                          \
266         pd->cury = y2;                                          \
267     } while (0)
268 
269 #define HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, OOMERR)       \
270     do {                                                        \
271         ADJUST3(pd, x1, y1, x2, y2, x3, y3);                    \
272         if (!subdivideCubic(pd, 0,                              \
273                             pd->curx, pd->cury,                 \
274                             x1, y1, x2, y2, x3, y3)) {          \
275             OOMERR;                                             \
276             break;                                              \
277         }                                                       \
278         PDBOXPOINT(pd, x1, y1);                                 \
279         PDBOXPOINT(pd, x2, y2);                                 \
280         PDBOXPOINT(pd, x3, y3);                                 \
281         pd->curx = x3;                                          \
282         pd->cury = y3;                                          \
283     } while (0)
284 
285 #define HANDLECLOSE(pd, OOMERR)                                 \
286     do {                                                        \
287         if (pd->curx != pd->movx || pd->cury != pd->movy) {     \
288             if (!subdivideLine(pd, 0,                           \
289                                pd->curx, pd->cury,              \
290                                pd->movx, pd->movy)) {           \
291                 OOMERR;                                         \
292                 break;                                          \
293             }                                                   \
294             pd->curx = pd->movx;                                \
295             pd->cury = pd->movy;                                \
296         }                                                       \
297     } while (0)
298 
299 #define HANDLEENDPATH(pd, OOMERR)                               \
300     do {                                                        \
301         HANDLECLOSE(pd, OOMERR);                                \
302         pd->state = STATE_PATH_DONE;                            \
303     } while (0)
304 
305 static pathData *
GetSpanData(JNIEnv * env,jobject sr,int minState,int maxState)306 GetSpanData(JNIEnv *env, jobject sr, int minState, int maxState)
307 {
308     pathData *pd = (pathData *) JNU_GetLongFieldAsPtr(env, sr, pSpanDataID);
309 
310     if (pd == NULL) {
311         JNU_ThrowNullPointerException(env, "private data");
312     } else if (pd->state < minState || pd->state > maxState) {
313         JNU_ThrowInternalError(env, "bad path delivery sequence");
314         pd = NULL;
315     }
316 
317     return pd;
318 }
319 
320 static pathData *
MakeSpanData(JNIEnv * env,jobject sr)321 MakeSpanData(JNIEnv *env, jobject sr)
322 {
323     pathData *pd = (pathData *) JNU_GetLongFieldAsPtr(env, sr, pSpanDataID);
324 
325     if (pd != NULL) {
326         JNU_ThrowInternalError(env, "private data already initialized");
327         return NULL;
328     }
329 
330     pd = calloc(1, sizeof(pathData));
331 
332     if (pd == NULL) {
333         JNU_ThrowOutOfMemoryError(env, "private data");
334     } else {
335         /* Initialize PathConsumer2D struct header */
336         pd->funcs.moveTo = PCMoveTo;
337         pd->funcs.lineTo = PCLineTo;
338         pd->funcs.quadTo = PCQuadTo;
339         pd->funcs.cubicTo = PCCubicTo;
340         pd->funcs.closePath = PCClosePath;
341         pd->funcs.pathDone = PCPathDone;
342 
343         /* Initialize ShapeSpanIterator fields */
344         pd->first = 1;
345 
346         (*env)->SetLongField(env, sr, pSpanDataID, ptr_to_jlong(pd));
347     }
348 
349     return pd;
350 }
351 
352 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_initIDs(JNIEnv * env,jclass src)353 Java_sun_java2d_pipe_ShapeSpanIterator_initIDs
354     (JNIEnv *env, jclass src)
355 {
356     pSpanDataID = (*env)->GetFieldID(env, src, "pData", "J");
357 }
358 
359 /*
360  * Class:     sun_java2d_pipe_ShapeSpanIterator
361  * Method:    setNormalize
362  * Signature: (Z)V
363  */
364 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_setNormalize(JNIEnv * env,jobject sr,jboolean adjust)365 Java_sun_java2d_pipe_ShapeSpanIterator_setNormalize
366     (JNIEnv *env, jobject sr, jboolean adjust)
367 {
368     pathData *pd;
369 
370     pd = MakeSpanData(env, sr);
371     if (pd == NULL) {
372         return;
373     }
374 
375     pd->adjust = adjust;
376 }
377 
378 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_setOutputAreaXYXY(JNIEnv * env,jobject sr,jint lox,jint loy,jint hix,jint hiy)379 Java_sun_java2d_pipe_ShapeSpanIterator_setOutputAreaXYXY
380     (JNIEnv *env, jobject sr, jint lox, jint loy, jint hix, jint hiy)
381 {
382     pathData *pd;
383 
384     pd = GetSpanData(env, sr, STATE_INIT, STATE_INIT);
385     if (pd == NULL) {
386         return;
387     }
388 
389     pd->lox = lox;
390     pd->loy = loy;
391     pd->hix = hix;
392     pd->hiy = hiy;
393     pd->state = STATE_HAVE_CLIP;
394 }
395 
396 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_setRule(JNIEnv * env,jobject sr,jint rule)397 Java_sun_java2d_pipe_ShapeSpanIterator_setRule
398     (JNIEnv *env, jobject sr, jint rule)
399 {
400     pathData *pd;
401 
402     pd = GetSpanData(env, sr, STATE_HAVE_CLIP, STATE_HAVE_CLIP);
403     if (pd == NULL) {
404         return;
405     }
406 
407     pd->evenodd = (rule == java_awt_geom_PathIterator_WIND_EVEN_ODD);
408     pd->state = STATE_HAVE_RULE;
409 }
410 
411 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_addSegment(JNIEnv * env,jobject sr,jint type,jfloatArray coordObj)412 Java_sun_java2d_pipe_ShapeSpanIterator_addSegment
413     (JNIEnv *env, jobject sr, jint type, jfloatArray coordObj)
414 {
415     jfloat coords[6];
416     jfloat x1, y1, x2, y2, x3, y3;
417     jboolean oom = JNI_FALSE;
418     pathData *pd;
419     int numpts = 0;
420 
421     pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
422     if (pd == NULL) {
423         return;
424     }
425 
426     (*env)->GetFloatArrayRegion(env, coordObj, 0, 6, coords);
427     if ((*env)->ExceptionCheck(env)) {
428         return;
429     }
430 
431     switch (type) {
432     case java_awt_geom_PathIterator_SEG_MOVETO:
433         x1 = coords[0]; y1 = coords[1];
434         HANDLEMOVETO(pd, x1, y1, {oom = JNI_TRUE;});
435         break;
436     case java_awt_geom_PathIterator_SEG_LINETO:
437         x1 = coords[0]; y1 = coords[1];
438         HANDLELINETO(pd, x1, y1, {oom = JNI_TRUE;});
439         break;
440     case java_awt_geom_PathIterator_SEG_QUADTO:
441         x1 = coords[0]; y1 = coords[1];
442         x2 = coords[2]; y2 = coords[3];
443         HANDLEQUADTO(pd, x1, y1, x2, y2, {oom = JNI_TRUE;});
444         break;
445     case java_awt_geom_PathIterator_SEG_CUBICTO:
446         x1 = coords[0]; y1 = coords[1];
447         x2 = coords[2]; y2 = coords[3];
448         x3 = coords[4]; y3 = coords[5];
449         HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, {oom = JNI_TRUE;});
450         break;
451     case java_awt_geom_PathIterator_SEG_CLOSE:
452         HANDLECLOSE(pd, {oom = JNI_TRUE;});
453         break;
454     default:
455         JNU_ThrowInternalError(env, "bad path segment type");
456         return;
457     }
458 
459     if (oom) {
460         JNU_ThrowOutOfMemoryError(env, "path segment data");
461         return;
462     }
463 }
464 
465 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_getPathBox(JNIEnv * env,jobject sr,jintArray spanbox)466 Java_sun_java2d_pipe_ShapeSpanIterator_getPathBox
467     (JNIEnv *env, jobject sr, jintArray spanbox)
468 {
469     pathData *pd;
470     jint coords[4];
471 
472     pd = GetSpanData(env, sr, STATE_PATH_DONE, STATE_PATH_DONE);
473     if (pd == NULL) {
474         return;
475     }
476 
477     ShapeSIGetPathBox(env, pd, coords);
478 
479     (*env)->SetIntArrayRegion(env, spanbox, 0, 4, coords);
480 }
481 
482 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_intersectClipBox(JNIEnv * env,jobject ri,jint clox,jint cloy,jint chix,jint chiy)483 Java_sun_java2d_pipe_ShapeSpanIterator_intersectClipBox
484     (JNIEnv *env, jobject ri, jint clox, jint cloy, jint chix, jint chiy)
485 {
486     pathData *pd;
487 
488     pd = GetSpanData(env, ri, STATE_PATH_DONE, STATE_PATH_DONE);
489     if (pd == NULL) {
490         return;
491     }
492 
493     ShapeSIIntersectClipBox(env, pd, clox, cloy, chix, chiy);
494 }
495 
496 JNIEXPORT jboolean JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_nextSpan(JNIEnv * env,jobject sr,jintArray spanbox)497 Java_sun_java2d_pipe_ShapeSpanIterator_nextSpan
498     (JNIEnv *env, jobject sr, jintArray spanbox)
499 {
500     pathData *pd;
501     jboolean ret;
502     jint coords[4];
503 
504     pd = GetSpanData(env, sr, STATE_PATH_DONE, STATE_SPAN_STARTED);
505     if (pd == NULL) {
506         return JNI_FALSE;
507     }
508 
509     ret = ShapeSINextSpan(pd, coords);
510     if (ret) {
511         (*env)->SetIntArrayRegion(env, spanbox, 0, 4, coords);
512     }
513 
514     return ret;
515 }
516 
517 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_skipDownTo(JNIEnv * env,jobject sr,jint y)518 Java_sun_java2d_pipe_ShapeSpanIterator_skipDownTo
519     (JNIEnv *env, jobject sr, jint y)
520 {
521     pathData *pd;
522 
523     pd = GetSpanData(env, sr, STATE_PATH_DONE, STATE_SPAN_STARTED);
524     if (pd == NULL) {
525         return;
526     }
527 
528     ShapeSISkipDownTo(pd, y);
529 }
530 
531 JNIEXPORT jlong JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_getNativeIterator(JNIEnv * env,jobject sr)532 Java_sun_java2d_pipe_ShapeSpanIterator_getNativeIterator
533     (JNIEnv *env, jobject sr)
534 {
535     return ptr_to_jlong(&ShapeSIFuncs);
536 }
537 
538 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_dispose(JNIEnv * env,jobject sr)539 Java_sun_java2d_pipe_ShapeSpanIterator_dispose
540     (JNIEnv *env, jobject sr)
541 {
542     pathData *pd = (pathData *) JNU_GetLongFieldAsPtr(env, sr, pSpanDataID);
543 
544     if (pd == NULL) {
545         return;
546     }
547 
548     if (pd->segments != NULL) {
549         free(pd->segments);
550     }
551     if (pd->segmentTable != NULL) {
552         free(pd->segmentTable);
553     }
554     free(pd);
555 
556     (*env)->SetLongField(env, sr, pSpanDataID, jlong_zero);
557 }
558 
559 #define OUT_XLO 1
560 #define OUT_XHI 2
561 #define OUT_YLO 4
562 #define OUT_YHI 8
563 
564 #define CALCULATE_OUTCODES(pd, outc, x, y) \
565     do { \
566         if (y <= pd->loy) outc = OUT_YLO; \
567         else if (y >= pd->hiy) outc = OUT_YHI; \
568         else outc = 0; \
569         if (x <= pd->lox) outc |= OUT_XLO; \
570         else if (x >= pd->hix) outc |= OUT_XHI; \
571     } while (0)
572 
573 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_appendPoly(JNIEnv * env,jobject sr,jintArray xArray,jintArray yArray,jint nPoints,jint ixoff,jint iyoff)574 Java_sun_java2d_pipe_ShapeSpanIterator_appendPoly
575     (JNIEnv *env, jobject sr,
576      jintArray xArray, jintArray yArray, jint nPoints,
577      jint ixoff, jint iyoff)
578 {
579     pathData *pd;
580     int i;
581     jint *xPoints, *yPoints;
582     jboolean oom = JNI_FALSE;
583     jfloat xoff = (jfloat) ixoff, yoff = (jfloat) iyoff;
584 
585     pd = GetSpanData(env, sr, STATE_HAVE_CLIP, STATE_HAVE_CLIP);
586     if (pd == NULL) {
587         return;
588     }
589 
590     pd->evenodd = JNI_TRUE;
591     pd->state = STATE_HAVE_RULE;
592     if (pd->adjust) {
593         xoff += 0.25f;
594         yoff += 0.25f;
595     }
596 
597     if (xArray == NULL || yArray == NULL) {
598         JNU_ThrowNullPointerException(env, "polygon data arrays");
599         return;
600     }
601     if ((*env)->GetArrayLength(env, xArray) < nPoints ||
602         (*env)->GetArrayLength(env, yArray) < nPoints)
603     {
604         JNU_ThrowArrayIndexOutOfBoundsException(env, "polygon data arrays");
605         return;
606     }
607 
608     if (nPoints > 0) {
609         xPoints = (*env)->GetPrimitiveArrayCritical(env, xArray, NULL);
610         if (xPoints != NULL) {
611             yPoints = (*env)->GetPrimitiveArrayCritical(env, yArray, NULL);
612             if (yPoints != NULL) {
613                 jint outc0;
614                 jfloat x, y;
615 
616                 x = xPoints[0] + xoff;
617                 y = yPoints[0] + yoff;
618                 CALCULATE_OUTCODES(pd, outc0, x, y);
619                 pd->movx = pd->curx = x;
620                 pd->movy = pd->cury = y;
621                 pd->pathlox = pd->pathhix = x;
622                 pd->pathloy = pd->pathhiy = y;
623                 pd->first = 0;
624                 for (i = 1; !oom && i < nPoints; i++) {
625                     jint outc1;
626 
627                     x = xPoints[i] + xoff;
628                     y = yPoints[i] + yoff;
629                     if (y == pd->cury) {
630                         /* Horizontal segment - do not append */
631                         if (x != pd->curx) {
632                             /* Not empty segment - track change in X */
633                             CALCULATE_OUTCODES(pd, outc0, x, y);
634                             pd->curx = x;
635                             if (pd->pathlox > x) pd->pathlox = x;
636                             if (pd->pathhix < x) pd->pathhix = x;
637                         }
638                         continue;
639                     }
640                     CALCULATE_OUTCODES(pd, outc1, x, y);
641                     outc0 &= outc1;
642                     if (outc0 == 0) {
643                         oom = !appendSegment(pd, pd->curx, pd->cury, x, y);
644                     } else if (outc0 == OUT_XLO) {
645                         oom = !appendSegment(pd, (jfloat) pd->lox, pd->cury,
646                                              (jfloat) pd->lox, y);
647                     }
648                     if (pd->pathlox > x) pd->pathlox = x;
649                     if (pd->pathloy > y) pd->pathloy = y;
650                     if (pd->pathhix < x) pd->pathhix = x;
651                     if (pd->pathhiy < y) pd->pathhiy = y;
652                     outc0 = outc1;
653                     pd->curx = x;
654                     pd->cury = y;
655                 }
656                 (*env)->ReleasePrimitiveArrayCritical(env, yArray,
657                                                       yPoints, JNI_ABORT);
658             }
659             (*env)->ReleasePrimitiveArrayCritical(env, xArray,
660                                                   xPoints, JNI_ABORT);
661         }
662         if (xPoints == NULL || yPoints == NULL) {
663             return;
664         }
665     }
666     if (!oom) {
667         HANDLEENDPATH(pd, {oom = JNI_TRUE;});
668     }
669     if (oom) {
670         JNU_ThrowOutOfMemoryError(env, "path segment data");
671     }
672 }
673 
674 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_moveTo(JNIEnv * env,jobject sr,jfloat x0,jfloat y0)675 Java_sun_java2d_pipe_ShapeSpanIterator_moveTo
676     (JNIEnv *env, jobject sr, jfloat x0, jfloat y0)
677 {
678     pathData *pd;
679 
680     pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
681     if (pd == NULL) {
682         return;
683     }
684 
685     HANDLEMOVETO(pd, x0, y0,
686                  {JNU_ThrowOutOfMemoryError(env, "path segment data");});
687 }
688 
689 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_lineTo(JNIEnv * env,jobject sr,jfloat x1,jfloat y1)690 Java_sun_java2d_pipe_ShapeSpanIterator_lineTo
691     (JNIEnv *env, jobject sr, jfloat x1, jfloat y1)
692 {
693     pathData *pd;
694 
695     pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
696     if (pd == NULL) {
697         return;
698     }
699 
700     HANDLELINETO(pd, x1, y1,
701                  {JNU_ThrowOutOfMemoryError(env, "path segment data");});
702 }
703 
704 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_quadTo(JNIEnv * env,jobject sr,jfloat xm,jfloat ym,jfloat x1,jfloat y1)705 Java_sun_java2d_pipe_ShapeSpanIterator_quadTo
706     (JNIEnv *env, jobject sr,
707      jfloat xm, jfloat ym, jfloat x1, jfloat y1)
708 {
709     pathData *pd;
710 
711     pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
712     if (pd == NULL) {
713         return;
714     }
715 
716     HANDLEQUADTO(pd, xm, ym, x1, y1,
717                  {JNU_ThrowOutOfMemoryError(env, "path segment data");});
718 }
719 
720 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)721 Java_sun_java2d_pipe_ShapeSpanIterator_curveTo
722     (JNIEnv *env, jobject sr,
723      jfloat xm, jfloat ym,
724      jfloat xn, jfloat yn,
725      jfloat x1, jfloat y1)
726 {
727     pathData *pd;
728 
729     pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
730     if (pd == NULL) {
731         return;
732     }
733 
734     HANDLECUBICTO(pd, xm, ym, xn, yn, x1, y1,
735                   {JNU_ThrowOutOfMemoryError(env, "path segment data");});
736 }
737 
738 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_closePath(JNIEnv * env,jobject sr)739 Java_sun_java2d_pipe_ShapeSpanIterator_closePath
740     (JNIEnv *env, jobject sr)
741 {
742     pathData *pd;
743 
744     pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
745     if (pd == NULL) {
746         return;
747     }
748 
749     HANDLECLOSE(pd, {JNU_ThrowOutOfMemoryError(env, "path segment data");});
750 }
751 
752 JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_pathDone(JNIEnv * env,jobject sr)753 Java_sun_java2d_pipe_ShapeSpanIterator_pathDone
754     (JNIEnv *env, jobject sr)
755 {
756     pathData *pd;
757 
758     pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
759     if (pd == NULL) {
760         return;
761     }
762 
763     HANDLEENDPATH(pd, {JNU_ThrowOutOfMemoryError(env, "path segment data");});
764 }
765 
766 JNIEXPORT jlong JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_getNativeConsumer(JNIEnv * env,jobject sr)767 Java_sun_java2d_pipe_ShapeSpanIterator_getNativeConsumer
768     (JNIEnv *env, jobject sr)
769 {
770     pathData *pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
771 
772     if (pd == NULL) {
773         return jlong_zero;
774     }
775 
776     return ptr_to_jlong(&(pd->funcs));
777 }
778 
779 static jboolean
PCMoveTo(PathConsumerVec * consumer,jfloat x0,jfloat y0)780 PCMoveTo(PathConsumerVec *consumer,
781          jfloat x0, jfloat y0)
782 {
783     pathData *pd = (pathData *) consumer;
784     jboolean oom = JNI_FALSE;
785 
786     HANDLEMOVETO(pd, x0, y0, {oom = JNI_TRUE;});
787 
788     return oom;
789 }
790 
791 static jboolean
PCLineTo(PathConsumerVec * consumer,jfloat x1,jfloat y1)792 PCLineTo(PathConsumerVec *consumer,
793          jfloat x1, jfloat y1)
794 {
795     pathData *pd = (pathData *) consumer;
796     jboolean oom = JNI_FALSE;
797 
798     HANDLELINETO(pd, x1, y1, {oom = JNI_TRUE;});
799 
800     return oom;
801 }
802 
803 static jboolean
PCQuadTo(PathConsumerVec * consumer,jfloat x1,jfloat y1,jfloat x2,jfloat y2)804 PCQuadTo(PathConsumerVec *consumer,
805          jfloat x1, jfloat y1,
806          jfloat x2, jfloat y2)
807 {
808     pathData *pd = (pathData *) consumer;
809     jboolean oom = JNI_FALSE;
810 
811     HANDLEQUADTO(pd, x1, y1, x2, y2, {oom = JNI_TRUE;});
812 
813     return oom;
814 }
815 
816 static jboolean
PCCubicTo(PathConsumerVec * consumer,jfloat x1,jfloat y1,jfloat x2,jfloat y2,jfloat x3,jfloat y3)817 PCCubicTo(PathConsumerVec *consumer,
818           jfloat x1, jfloat y1,
819           jfloat x2, jfloat y2,
820           jfloat x3, jfloat y3)
821 {
822     pathData *pd = (pathData *) consumer;
823     jboolean oom = JNI_FALSE;
824 
825     HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, {oom = JNI_TRUE;});
826 
827     return oom;
828 }
829 
830 static jboolean
PCClosePath(PathConsumerVec * consumer)831 PCClosePath(PathConsumerVec *consumer)
832 {
833     pathData *pd = (pathData *) consumer;
834     jboolean oom = JNI_FALSE;
835 
836     HANDLECLOSE(pd, {oom = JNI_TRUE;});
837 
838     return oom;
839 }
840 
841 static jboolean
PCPathDone(PathConsumerVec * consumer)842 PCPathDone(PathConsumerVec *consumer)
843 {
844     pathData *pd = (pathData *) consumer;
845     jboolean oom = JNI_FALSE;
846 
847     HANDLEENDPATH(pd, {oom = JNI_TRUE;});
848 
849     return oom;
850 }
851 
852 /*
853  * REMIND: CDECL needed for WIN32 "qsort"
854  */
855 
856 #ifdef _WIN32
857 #define CDECL __cdecl
858 #else
859 #define CDECL
860 #endif
861 
862 #define SUBDIVIDE_MAX   10
863 #define MAX_FLAT_SQ     (1.0 * 1.0)
864 #define GROW_SIZE       20
865 #define ERRSTEP_MAX     (0x7fffffff)
866 #define FRACTTOJINT(f)  ((jint) ((f) * (double) ERRSTEP_MAX))
867 
868 #define minmax2(v1, v2, min, max)       \
869 do {                                    \
870     if (v1 < v2) {                      \
871         min = v1;                       \
872         max = v2;                       \
873     } else {                            \
874         min = v2;                       \
875         max = v1;                       \
876     }                                   \
877 } while(0)
878 
879 #define minmax3(v1, v2, v3, min, max)   \
880 do {                                    \
881     if (v1 < v2) {                      \
882         if (v1 < v3) {                  \
883             min = v1;                   \
884             max = (v2 < v3) ? v3 : v2;  \
885         } else {                        \
886             max = v2;                   \
887             min = v3;                   \
888         }                               \
889     } else {                            \
890         if (v1 < v3) {                  \
891             max = v3;                   \
892             min = v2;                   \
893         } else {                        \
894             max = v1;                   \
895             min = (v2 < v3) ? v2 : v3;  \
896         }                               \
897     }                                   \
898 } while (0)
899 
900 #define minmax4(v1, v2, v3, v4, min, max)       \
901 do {                                            \
902     if (v1 < v2) {                              \
903         if (v3 < v4) {                          \
904             max = (v2 < v4) ? v4 : v2;          \
905             min = (v1 < v3) ? v1 : v3;          \
906         } else {                                \
907             max = (v2 < v3) ? v3 : v2;          \
908             min = (v1 < v4) ? v1 : v4;          \
909         }                                       \
910     } else {                                    \
911         if (v3 < v4) {                          \
912             max = (v1 < v4) ? v4 : v1;          \
913             min = (v2 < v3) ? v2 : v3;          \
914         } else {                                \
915             max = (v1 < v3) ? v3 : v1;          \
916             min = (v2 < v4) ? v2 : v4;          \
917         }                                       \
918     }                                           \
919 } while(0)
920 
921 static jfloat
ptSegDistSq(jfloat x0,jfloat y0,jfloat x1,jfloat y1,jfloat px,jfloat py)922 ptSegDistSq(jfloat x0, jfloat y0,
923             jfloat x1, jfloat y1,
924             jfloat px, jfloat py)
925 {
926     jfloat dotprod, projlenSq;
927 
928     /* Adjust vectors relative to x0,y0 */
929     /* x1,y1 becomes relative vector from x0,y0 to end of segment */
930     x1 -= x0;
931     y1 -= y0;
932     /* px,py becomes relative vector from x0,y0 to test point */
933     px -= x0;
934     py -= y0;
935     dotprod = px * x1 + py * y1;
936     if (dotprod <= 0.0) {
937         /* px,py is on the side of x0,y0 away from x1,y1 */
938         /* distance to segment is length of px,py vector */
939         /* "length of its (clipped) projection" is now 0.0 */
940         projlenSq = 0.0;
941     } else {
942         /* switch to backwards vectors relative to x1,y1 */
943         /* x1,y1 are already the negative of x0,y0=>x1,y1 */
944         /* to get px,py to be the negative of px,py=>x1,y1 */
945         /* the dot product of two negated vectors is the same */
946         /* as the dot product of the two normal vectors */
947         px = x1 - px;
948         py = y1 - py;
949         dotprod = px * x1 + py * y1;
950         if (dotprod <= 0.0) {
951             /* px,py is on the side of x1,y1 away from x0,y0 */
952             /* distance to segment is length of (backwards) px,py vector */
953             /* "length of its (clipped) projection" is now 0.0 */
954             projlenSq = 0.0;
955         } else {
956             /* px,py is between x0,y0 and x1,y1 */
957             /* dotprod is the length of the px,py vector */
958             /* projected on the x1,y1=>x0,y0 vector times the */
959             /* length of the x1,y1=>x0,y0 vector */
960             projlenSq = dotprod * dotprod / (x1 * x1 + y1 * y1);
961         }
962     }
963     /* Distance to line is now the length of the relative point */
964     /* vector minus the length of its projection onto the line */
965     /* (which is zero if the projection falls outside the range */
966     /*  of the line segment). */
967     return px * px + py * py - projlenSq;
968 }
969 
970 static jboolean
appendSegment(pathData * pd,jfloat x0,jfloat y0,jfloat x1,jfloat y1)971 appendSegment(pathData *pd,
972               jfloat x0, jfloat y0,
973               jfloat x1, jfloat y1)
974 {
975     jbyte windDir;
976     jint istartx, istarty, ilasty;
977     jfloat dx, dy, slope;
978     jfloat ystartbump;
979     jint bumpx, bumperr, error;
980     segmentData *seg;
981 
982     if (y0 > y1) {
983         jfloat t;
984         t = x0; x0 = x1; x1 = t;
985         t = y0; y0 = y1; y1 = t;
986         windDir = -1;
987     } else {
988         windDir = 1;
989     }
990     /* We want to iterate at every horizontal pixel center (HPC) crossing. */
991     /* First calculate next highest HPC we will cross at the start. */
992     istarty = (jint) ceil(y0 - 0.5f);
993     /* Then calculate next highest HPC we would cross at the end. */
994     ilasty  = (jint) ceil(y1 - 0.5f);
995     /* Ignore if we start and end outside clip, or on the same scanline. */
996     if (istarty >= ilasty || istarty >= pd->hiy || ilasty <= pd->loy) {
997         return JNI_TRUE;
998     }
999 
1000     /* We will need to insert this segment, check for room. */
1001     if (pd->numSegments >= pd->segmentsSize) {
1002         segmentData *newSegs;
1003         int newSize = pd->segmentsSize + GROW_SIZE;
1004         newSegs = (segmentData *) calloc(newSize, sizeof(segmentData));
1005         if (newSegs == NULL) {
1006             return JNI_FALSE;
1007         }
1008         if (pd->segments != NULL) {
1009             memcpy(newSegs, pd->segments,
1010                    sizeof(segmentData) * pd->segmentsSize);
1011             free(pd->segments);
1012         }
1013         pd->segments = newSegs;
1014         pd->segmentsSize = newSize;
1015     }
1016 
1017     dx = x1 - x0;
1018     dy = y1 - y0;
1019     slope = dx / dy;
1020 
1021     /*
1022      * The Y coordinate of the first HPC was calculated as istarty.  We
1023      * now need to calculate the corresponding X coordinate (both integer
1024      * version for span start coordinate and float version for sub-pixel
1025      * error calculation).
1026      */
1027     /* First, how far does y bump to get to next HPC? */
1028     ystartbump = istarty + 0.5f - y0;
1029     /* Now, bump the float x coordinate to get X sample at that HPC. */
1030     x0 += ystartbump * dx / dy;
1031     /* Now calculate the integer coordinate that such a span starts at. */
1032     /* NOTE: Span inclusion is based on vertical pixel centers (VPC). */
1033     istartx = (jint) ceil(x0 - 0.5f);
1034     /* What is the lower bound of the per-scanline change in the X coord? */
1035     bumpx = (jint) floor(slope);
1036     /* What is the subpixel amount by which the bumpx is off? */
1037     bumperr = FRACTTOJINT(slope - floor(slope));
1038     /* Finally, find out how far the x coordinate can go before next VPC. */
1039     error = FRACTTOJINT(x0 - (istartx - 0.5f));
1040 
1041     seg = &pd->segments[pd->numSegments++];
1042     seg->curx = istartx;
1043     seg->cury = istarty;
1044     seg->lasty = ilasty;
1045     seg->error = error;
1046     seg->bumpx = bumpx;
1047     seg->bumperr = bumperr;
1048     seg->windDir = windDir;
1049     return JNI_TRUE;
1050 }
1051 
1052 /*
1053  * Lines don't really need to be subdivided, but this function performs
1054  * the same trivial rejections and reductions that the curve subdivision
1055  * functions perform before it hands the coordinates off to the appendSegment
1056  * function.
1057  */
1058 static jboolean
subdivideLine(pathData * pd,int level,jfloat x0,jfloat y0,jfloat x1,jfloat y1)1059 subdivideLine(pathData *pd, int level,
1060               jfloat x0, jfloat y0,
1061               jfloat x1, jfloat y1)
1062 {
1063     jfloat miny, maxy;
1064     jfloat minx, maxx;
1065 
1066     minmax2(x0, x1, minx, maxx);
1067     minmax2(y0, y1, miny, maxy);
1068 
1069     if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) {
1070         return JNI_TRUE;
1071     }
1072     if (maxx <= pd->lox) {
1073         return appendSegment(pd, maxx, y0, maxx, y1);
1074     }
1075 
1076     return appendSegment(pd, x0, y0, x1, y1);
1077 }
1078 
1079 static jboolean
subdivideQuad(pathData * pd,int level,jfloat x0,jfloat y0,jfloat x1,jfloat y1,jfloat x2,jfloat y2)1080 subdivideQuad(pathData *pd, int level,
1081               jfloat x0, jfloat y0,
1082               jfloat x1, jfloat y1,
1083               jfloat x2, jfloat y2)
1084 {
1085     jfloat miny, maxy;
1086     jfloat minx, maxx;
1087 
1088     minmax3(x0, x1, x2, minx, maxx);
1089     minmax3(y0, y1, y2, miny, maxy);
1090 
1091     if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) {
1092         return JNI_TRUE;
1093     }
1094     if (maxx <= pd->lox) {
1095         return appendSegment(pd, maxx, y0, maxx, y2);
1096     }
1097 
1098     if (level < SUBDIVIDE_MAX) {
1099         /* Test if the curve is flat enough for insertion. */
1100         if (ptSegDistSq(x0, y0, x2, y2, x1, y1) > MAX_FLAT_SQ) {
1101             jfloat cx1, cx2;
1102             jfloat cy1, cy2;
1103 
1104             cx1 = (x0 + x1) / 2.0f;
1105             cx2 = (x1 + x2) / 2.0f;
1106             x1 = (cx1 + cx2) / 2.0f;
1107 
1108             cy1 = (y0 + y1) / 2.0f;
1109             cy2 = (y1 + y2) / 2.0f;
1110             y1 = (cy1 + cy2) / 2.0f;
1111 
1112             level++;
1113             return (subdivideQuad(pd, level, x0, y0, cx1, cy1, x1, y1) &&
1114                     subdivideQuad(pd, level, x1, y1, cx2, cy2, x2, y2));
1115         }
1116     }
1117 
1118     return appendSegment(pd, x0, y0, x2, y2);
1119 }
1120 
1121 static jboolean
subdivideCubic(pathData * pd,int level,jfloat x0,jfloat y0,jfloat x1,jfloat y1,jfloat x2,jfloat y2,jfloat x3,jfloat y3)1122 subdivideCubic(pathData *pd, int level,
1123                jfloat x0, jfloat y0,
1124                jfloat x1, jfloat y1,
1125                jfloat x2, jfloat y2,
1126                jfloat x3, jfloat y3)
1127 {
1128     jfloat miny, maxy;
1129     jfloat minx, maxx;
1130 
1131     minmax4(x0, x1, x2, x3, minx, maxx);
1132     minmax4(y0, y1, y2, y3, miny, maxy);
1133 
1134     if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) {
1135         return JNI_TRUE;
1136     }
1137     if (maxx <= pd->lox) {
1138         return appendSegment(pd, maxx, y0, maxx, y3);
1139     }
1140 
1141     if (level < SUBDIVIDE_MAX) {
1142         /* Test if the curve is flat enough for insertion. */
1143         if (ptSegDistSq(x0, y0, x3, y3, x1, y1) > MAX_FLAT_SQ ||
1144             ptSegDistSq(x0, y0, x3, y3, x2, y2) > MAX_FLAT_SQ)
1145         {
1146             jfloat ctrx, cx12, cx21;
1147             jfloat ctry, cy12, cy21;
1148 
1149             ctrx = (x1 + x2) / 2.0f;
1150             x1 = (x0 + x1) / 2.0f;
1151             x2 = (x2 + x3) / 2.0f;
1152             cx12 = (x1 + ctrx) / 2.0f;
1153             cx21 = (ctrx + x2) / 2.0f;
1154             ctrx = (cx12 + cx21) / 2.0f;
1155 
1156             ctry = (y1 + y2) / 2.0f;
1157             y1 = (y0 + y1) / 2.0f;
1158             y2 = (y2 + y3) / 2.0f;
1159             cy12 = (y1 + ctry) / 2.0f;
1160             cy21 = (ctry + y2) / 2.0f;
1161             ctry = (cy12 + cy21) / 2.0f;
1162 
1163             level++;
1164             return (subdivideCubic(pd, level, x0, y0, x1, y1,
1165                                    cx12, cy12, ctrx, ctry) &&
1166                     subdivideCubic(pd, level, ctrx, ctry, cx21, cy21,
1167                                    x2, y2, x3, y3));
1168         }
1169     }
1170 
1171     return appendSegment(pd, x0, y0, x3, y3);
1172 }
1173 
1174 static int CDECL
sortSegmentsByLeadingY(const void * elem1,const void * elem2)1175 sortSegmentsByLeadingY(const void *elem1, const void *elem2)
1176 {
1177     segmentData *seg1 = *(segmentData **)elem1;
1178     segmentData *seg2 = *(segmentData **)elem2;
1179 
1180     if (seg1->cury < seg2->cury) {
1181         return -1;
1182     }
1183     if (seg1->cury > seg2->cury) {
1184         return 1;
1185     }
1186     if (seg1->curx < seg2->curx) {
1187         return -1;
1188     }
1189     if (seg1->curx > seg2->curx) {
1190         return 1;
1191     }
1192     if (seg1->lasty < seg2->lasty) {
1193         return -1;
1194     }
1195     if (seg1->lasty > seg2->lasty) {
1196         return 1;
1197     }
1198     return 0;
1199 }
1200 
1201 static void *
ShapeSIOpen(JNIEnv * env,jobject iterator)1202 ShapeSIOpen(JNIEnv *env, jobject iterator)
1203 {
1204     return GetSpanData(env, iterator, STATE_PATH_DONE, STATE_PATH_DONE);
1205 }
1206 
1207 static void
ShapeSIClose(JNIEnv * env,void * private)1208 ShapeSIClose(JNIEnv *env, void *private)
1209 {
1210 }
1211 
1212 static void
ShapeSIGetPathBox(JNIEnv * env,void * private,jint pathbox[])1213 ShapeSIGetPathBox(JNIEnv *env, void *private, jint pathbox[])
1214 {
1215     pathData *pd = (pathData *)private;
1216 
1217     pathbox[0] = (jint) floor(pd->pathlox);
1218     pathbox[1] = (jint) floor(pd->pathloy);
1219     pathbox[2] = (jint) ceil(pd->pathhix);
1220     pathbox[3] = (jint) ceil(pd->pathhiy);
1221 }
1222 
1223 /*  Adjust the clip box from the given bounds. Used to constrain
1224     the output to a device clip
1225 */
1226 static void
ShapeSIIntersectClipBox(JNIEnv * env,void * private,jint clox,jint cloy,jint chix,jint chiy)1227 ShapeSIIntersectClipBox(JNIEnv *env, void *private,
1228                             jint clox, jint cloy, jint chix, jint chiy)
1229 {
1230     pathData *pd = (pathData *)private;
1231 
1232     if (clox > pd->lox) {
1233         pd->lox = clox;
1234     }
1235     if (cloy > pd->loy) {
1236         pd->loy = cloy;
1237     }
1238     if (chix < pd->hix) {
1239         pd->hix = chix;
1240     }
1241     if (chiy < pd->hiy) {
1242         pd->hiy = chiy;
1243     }
1244 }
1245 
1246 static jboolean
ShapeSINextSpan(void * state,jint spanbox[])1247 ShapeSINextSpan(void *state, jint spanbox[])
1248 {
1249     pathData *pd = (pathData *)state;
1250     int lo, cur, new, hi;
1251     int num = pd->numSegments;
1252     jint x0, x1, y0, err;
1253     jint loy;
1254     int ret = JNI_FALSE;
1255     segmentData **segmentTable;
1256     segmentData *seg;
1257 
1258     if (pd->state != STATE_SPAN_STARTED) {
1259         if (!initSegmentTable(pd)) {
1260             /* REMIND: - throw exception? */
1261             pd->lowSegment = num;
1262             return JNI_FALSE;
1263         }
1264     }
1265 
1266     lo = pd->lowSegment;
1267     cur = pd->curSegment;
1268     hi = pd->hiSegment;
1269     num = pd->numSegments;
1270     loy = pd->loy;
1271     segmentTable = pd->segmentTable;
1272 
1273     while (lo < num) {
1274         if (cur < hi) {
1275             seg = segmentTable[cur];
1276             x0 = seg->curx;
1277             if (x0 >= pd->hix) {
1278                 cur = hi;
1279                 continue;
1280             }
1281             if (x0 < pd->lox) {
1282                 x0 = pd->lox;
1283             }
1284 
1285             if (pd->evenodd) {
1286                 cur += 2;
1287                 if (cur <= hi) {
1288                     x1 = segmentTable[cur - 1]->curx;
1289                 } else {
1290                     x1 = pd->hix;
1291                 }
1292             } else {
1293                 int wind = seg->windDir;
1294                 cur++;
1295 
1296                 while (JNI_TRUE) {
1297                     if (cur >= hi) {
1298                         x1 = pd->hix;
1299                         break;
1300                     }
1301                     seg = segmentTable[cur++];
1302                     wind += seg->windDir;
1303                     if (wind == 0) {
1304                         x1 = seg->curx;
1305                         break;
1306                     }
1307                 }
1308             }
1309 
1310             if (x1 > pd->hix) {
1311                 x1 = pd->hix;
1312             }
1313             if (x1 <= x0) {
1314                 continue;
1315             }
1316             spanbox[0] = x0;
1317             spanbox[1] = loy;
1318             spanbox[2] = x1;
1319             spanbox[3] = loy + 1;
1320             ret = JNI_TRUE;
1321             break;
1322         }
1323 
1324         if (++loy >= pd->hiy) {
1325             lo = cur = hi = num;
1326             break;
1327         }
1328 
1329         /* Go through active segments and toss which end "above" loy */
1330         cur = new = hi;
1331         while (--cur >= lo) {
1332             seg = segmentTable[cur];
1333             if (seg->lasty > loy) {
1334                 segmentTable[--new] = seg;
1335             }
1336         }
1337 
1338         lo = new;
1339         if (lo == hi && lo < num) {
1340             /* The current list of segments is empty so we need to
1341              * jump to the beginning of the next set of segments.
1342              * Since the segments are not clipped to the output
1343              * area we need to make sure we don't jump "backwards"
1344              */
1345             seg = segmentTable[lo];
1346             if (loy < seg->cury) {
1347                 loy = seg->cury;
1348             }
1349         }
1350 
1351         /* Go through new segments and accept any which start "above" loy */
1352         while (hi < num && segmentTable[hi]->cury <= loy) {
1353             hi++;
1354         }
1355 
1356         /* Update and sort the active segments by x0 */
1357         for (cur = lo; cur < hi; cur++) {
1358             seg = segmentTable[cur];
1359 
1360             /* First update the x0, y0 of the segment */
1361             x0 = seg->curx;
1362             y0 = seg->cury;
1363             err = seg->error;
1364             if (++y0 == loy) {
1365                 x0 += seg->bumpx;
1366                 err += seg->bumperr;
1367                 x0 -= (err >> 31);
1368                 err &= ERRSTEP_MAX;
1369             } else {
1370                 jlong steps = loy;
1371                 steps -= y0 - 1;
1372                 y0 = loy;
1373                 x0 += (jint) (steps * seg->bumpx);
1374                 steps = err + (steps * seg->bumperr);
1375                 x0 += (jint) (steps >> 31);
1376                 err = ((jint) steps) & ERRSTEP_MAX;
1377             }
1378             seg->curx = x0;
1379             seg->cury = y0;
1380             seg->error = err;
1381 
1382             /* Then make sure the segment is sorted by x0 */
1383             for (new = cur; new > lo; new--) {
1384                 segmentData *seg2 = segmentTable[new - 1];
1385                 if (seg2->curx <= x0) {
1386                     break;
1387                 }
1388                 segmentTable[new] = seg2;
1389             }
1390             segmentTable[new] = seg;
1391         }
1392         cur = lo;
1393     }
1394 
1395     pd->lowSegment = lo;
1396     pd->hiSegment = hi;
1397     pd->curSegment = cur;
1398     pd->loy = loy;
1399     return ret;
1400 }
1401 
1402 static void
ShapeSISkipDownTo(void * private,jint y)1403 ShapeSISkipDownTo(void *private, jint y)
1404 {
1405     pathData *pd = (pathData *)private;
1406 
1407     if (pd->state != STATE_SPAN_STARTED) {
1408         if (!initSegmentTable(pd)) {
1409             /* REMIND: - throw exception? */
1410             pd->lowSegment = pd->numSegments;
1411             return;
1412         }
1413     }
1414 
1415     /* Make sure we are jumping forward */
1416     if (pd->loy < y) {
1417         /* Pretend like we just finished with the span line y-1... */
1418         pd->loy = y - 1;
1419         pd->curSegment = pd->hiSegment; /* no more segments on that line */
1420     }
1421 }
1422 
1423 static jboolean
initSegmentTable(pathData * pd)1424 initSegmentTable(pathData *pd)
1425 {
1426     int i, cur, num, loy;
1427     segmentData **segmentTable;
1428     segmentTable = malloc(pd->numSegments * sizeof(segmentData *));
1429     if (segmentTable == NULL) {
1430         return JNI_FALSE;
1431     }
1432     pd->state = STATE_SPAN_STARTED;
1433     for (i = 0; i < pd->numSegments; i++) {
1434         segmentTable[i] = &pd->segments[i];
1435     }
1436     qsort(segmentTable, pd->numSegments, sizeof(segmentData *),
1437           sortSegmentsByLeadingY);
1438 
1439     pd->segmentTable = segmentTable;
1440 
1441     /* Skip to the first segment that ends below the top clip edge */
1442     cur = 0;
1443     num = pd->numSegments;
1444     loy = pd->loy;
1445     while (cur < num && segmentTable[cur]->lasty <= loy) {
1446         cur++;
1447     }
1448     pd->lowSegment = pd->curSegment = pd->hiSegment = cur;
1449 
1450     /* Prepare for next action to increment loy and prepare new segments */
1451     pd->loy--;
1452 
1453     return JNI_TRUE;
1454 }
1455