blob: 61bde7216b08fdfdd04c766412cc3032fd4a663d [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 */
46typedef 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 */
63typedef 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
106static jboolean subdivideLine(pathData *pd, int level,
107 jfloat x0, jfloat y0,
108 jfloat x1, jfloat y1);
109static jboolean subdivideQuad(pathData *pd, int level,
110 jfloat x0, jfloat y0,
111 jfloat x1, jfloat y1,
112 jfloat x2, jfloat y2);
113static 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);
118static jboolean appendSegment(pathData *pd,
119 jfloat x0, jfloat y0,
120 jfloat x1, jfloat y1);
121static jboolean initSegmentTable(pathData *pd);
122
123static void *ShapeSIOpen(JNIEnv *env, jobject iterator);
124static void ShapeSIClose(JNIEnv *env, void *private);
125static void ShapeSIGetPathBox(JNIEnv *env, void *private, jint pathbox[]);
126static void ShapeSIIntersectClipBox(JNIEnv *env, void *private,
127 jint lox, jint loy, jint hix, jint hiy);
128static jboolean ShapeSINextSpan(void *state, jint spanbox[]);
129static void ShapeSISkipDownTo(void *private, jint y);
130
131static jfieldID pSpanDataID;
132
133static SpanIteratorFuncs ShapeSIFuncs = {
134 ShapeSIOpen,
135 ShapeSIClose,
136 ShapeSIGetPathBox,
137 ShapeSIIntersectClipBox,
138 ShapeSINextSpan,
139 ShapeSISkipDownTo
140};
141
142static LineToFunc PCLineTo;
143static MoveToFunc PCMoveTo;
144static QuadToFunc PCQuadTo;
145static CubicToFunc PCCubicTo;
146static ClosePathFunc PCClosePath;
147static 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
305static pathData *
306GetSpanData(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
320static pathData *
321MakeSpanData(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
352JNIEXPORT void JNICALL
353Java_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 */
364JNIEXPORT void JNICALL
365Java_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
378JNIEXPORT void JNICALL
379Java_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
396JNIEXPORT void JNICALL
397Java_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
411JNIEXPORT void JNICALL
412Java_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
465JNIEXPORT void JNICALL
466Java_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
482JNIEXPORT void JNICALL
483Java_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
496JNIEXPORT jboolean JNICALL
497Java_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
517JNIEXPORT void JNICALL
518Java_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
531JNIEXPORT jlong JNICALL
532Java_sun_java2d_pipe_ShapeSpanIterator_getNativeIterator
533 (JNIEnv *env, jobject sr)
534{
535 return ptr_to_jlong(&ShapeSIFuncs);
536}
537
538JNIEXPORT void JNICALL
539Java_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
573JNIEXPORT void JNICALL
574Java_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 }
657 (*env)->ReleasePrimitiveArrayCritical(env, yArray,
658 yPoints, JNI_ABORT);
659 }
660 (*env)->ReleasePrimitiveArrayCritical(env, xArray,
661 xPoints, JNI_ABORT);
662 }
663 if (!oom) {
664 HANDLEENDPATH(pd, {oom = JNI_TRUE;});
665 }
666 if (oom) {
667 JNU_ThrowOutOfMemoryError(env, "path segment data");
668 }
669}
670
671JNIEXPORT void JNICALL
672Java_sun_java2d_pipe_ShapeSpanIterator_moveTo
673 (JNIEnv *env, jobject sr, jfloat x0, jfloat y0)
674{
675 pathData *pd;
676
677 pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
678 if (pd == NULL) {
679 return;
680 }
681
682 HANDLEMOVETO(pd, x0, y0,
683 {JNU_ThrowOutOfMemoryError(env, "path segment data");});
684}
685
686JNIEXPORT void JNICALL
687Java_sun_java2d_pipe_ShapeSpanIterator_lineTo
688 (JNIEnv *env, jobject sr, jfloat x1, jfloat y1)
689{
690 pathData *pd;
691
692 pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
693 if (pd == NULL) {
694 return;
695 }
696
697 HANDLELINETO(pd, x1, y1,
698 {JNU_ThrowOutOfMemoryError(env, "path segment data");});
699}
700
701JNIEXPORT void JNICALL
702Java_sun_java2d_pipe_ShapeSpanIterator_quadTo
703 (JNIEnv *env, jobject sr,
704 jfloat xm, jfloat ym, jfloat x1, jfloat y1)
705{
706 pathData *pd;
707
708 pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
709 if (pd == NULL) {
710 return;
711 }
712
713 HANDLEQUADTO(pd, xm, ym, x1, y1,
714 {JNU_ThrowOutOfMemoryError(env, "path segment data");});
715}
716
717JNIEXPORT void JNICALL
718Java_sun_java2d_pipe_ShapeSpanIterator_curveTo
719 (JNIEnv *env, jobject sr,
720 jfloat xm, jfloat ym,
721 jfloat xn, jfloat yn,
722 jfloat x1, jfloat y1)
723{
724 pathData *pd;
725
726 pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
727 if (pd == NULL) {
728 return;
729 }
730
731 HANDLECUBICTO(pd, xm, ym, xn, yn, x1, y1,
732 {JNU_ThrowOutOfMemoryError(env, "path segment data");});
733}
734
735JNIEXPORT void JNICALL
736Java_sun_java2d_pipe_ShapeSpanIterator_closePath
737 (JNIEnv *env, jobject sr)
738{
739 pathData *pd;
740
741 pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
742 if (pd == NULL) {
743 return;
744 }
745
746 HANDLECLOSE(pd, {JNU_ThrowOutOfMemoryError(env, "path segment data");});
747}
748
749JNIEXPORT void JNICALL
750Java_sun_java2d_pipe_ShapeSpanIterator_pathDone
751 (JNIEnv *env, jobject sr)
752{
753 pathData *pd;
754
755 pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
756 if (pd == NULL) {
757 return;
758 }
759
760 HANDLEENDPATH(pd, {JNU_ThrowOutOfMemoryError(env, "path segment data");});
761}
762
763JNIEXPORT jlong JNICALL
764Java_sun_java2d_pipe_ShapeSpanIterator_getNativeConsumer
765 (JNIEnv *env, jobject sr)
766{
767 pathData *pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
768
769 if (pd == NULL) {
770 return jlong_zero;
771 }
772
773 return ptr_to_jlong(&(pd->funcs));
774}
775
776static jboolean
777PCMoveTo(PathConsumerVec *consumer,
778 jfloat x0, jfloat y0)
779{
780 pathData *pd = (pathData *) consumer;
781 jboolean oom = JNI_FALSE;
782
783 HANDLEMOVETO(pd, x0, y0, {oom = JNI_TRUE;});
784
785 return oom;
786}
787
788static jboolean
789PCLineTo(PathConsumerVec *consumer,
790 jfloat x1, jfloat y1)
791{
792 pathData *pd = (pathData *) consumer;
793 jboolean oom = JNI_FALSE;
794
795 HANDLELINETO(pd, x1, y1, {oom = JNI_TRUE;});
796
797 return oom;
798}
799
800static jboolean
801PCQuadTo(PathConsumerVec *consumer,
802 jfloat x1, jfloat y1,
803 jfloat x2, jfloat y2)
804{
805 pathData *pd = (pathData *) consumer;
806 jboolean oom = JNI_FALSE;
807
808 HANDLEQUADTO(pd, x1, y1, x2, y2, {oom = JNI_TRUE;});
809
810 return oom;
811}
812
813static jboolean
814PCCubicTo(PathConsumerVec *consumer,
815 jfloat x1, jfloat y1,
816 jfloat x2, jfloat y2,
817 jfloat x3, jfloat y3)
818{
819 pathData *pd = (pathData *) consumer;
820 jboolean oom = JNI_FALSE;
821
822 HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, {oom = JNI_TRUE;});
823
824 return oom;
825}
826
827static jboolean
828PCClosePath(PathConsumerVec *consumer)
829{
830 pathData *pd = (pathData *) consumer;
831 jboolean oom = JNI_FALSE;
832
833 HANDLECLOSE(pd, {oom = JNI_TRUE;});
834
835 return oom;
836}
837
838static jboolean
839PCPathDone(PathConsumerVec *consumer)
840{
841 pathData *pd = (pathData *) consumer;
842 jboolean oom = JNI_FALSE;
843
844 HANDLEENDPATH(pd, {oom = JNI_TRUE;});
845
846 return oom;
847}
848
849/*
850 * REMIND: CDECL needed for WIN32 "qsort"
851 */
852
853#ifdef _WIN32
854#define CDECL __cdecl
855#else
856#define CDECL
857#endif
858
859#define SUBDIVIDE_MAX 10
860#define MAX_FLAT_SQ (1.0 * 1.0)
861#define GROW_SIZE 20
862#define ERRSTEP_MAX (0x7fffffff)
863#define FRACTTOJINT(f) ((jint) ((f) * (double) ERRSTEP_MAX))
864
865#define minmax2(v1, v2, min, max) \
866do { \
867 if (v1 < v2) { \
868 min = v1; \
869 max = v2; \
870 } else { \
871 min = v2; \
872 max = v1; \
873 } \
874} while(0)
875
876#define minmax3(v1, v2, v3, min, max) \
877do { \
878 if (v1 < v2) { \
879 if (v1 < v3) { \
880 min = v1; \
881 max = (v2 < v3) ? v3 : v2; \
882 } else { \
883 max = v2; \
884 min = v3; \
885 } \
886 } else { \
887 if (v1 < v3) { \
888 max = v3; \
889 min = v2; \
890 } else { \
891 max = v1; \
892 min = (v2 < v3) ? v2 : v3; \
893 } \
894 } \
895} while (0)
896
897#define minmax4(v1, v2, v3, v4, min, max) \
898do { \
899 if (v1 < v2) { \
900 if (v3 < v4) { \
901 max = (v2 < v4) ? v4 : v2; \
902 min = (v1 < v3) ? v1 : v3; \
903 } else { \
904 max = (v2 < v3) ? v3 : v2; \
905 min = (v1 < v4) ? v1 : v4; \
906 } \
907 } else { \
908 if (v3 < v4) { \
909 max = (v1 < v4) ? v4 : v1; \
910 min = (v2 < v3) ? v2 : v3; \
911 } else { \
912 max = (v1 < v3) ? v3 : v1; \
913 min = (v2 < v4) ? v2 : v4; \
914 } \
915 } \
916} while(0)
917
918static jfloat
919ptSegDistSq(jfloat x0, jfloat y0,
920 jfloat x1, jfloat y1,
921 jfloat px, jfloat py)
922{
923 jfloat dotprod, projlenSq;
924
925 /* Adjust vectors relative to x0,y0 */
926 /* x1,y1 becomes relative vector from x0,y0 to end of segment */
927 x1 -= x0;
928 y1 -= y0;
929 /* px,py becomes relative vector from x0,y0 to test point */
930 px -= x0;
931 py -= y0;
932 dotprod = px * x1 + py * y1;
933 if (dotprod <= 0.0) {
934 /* px,py is on the side of x0,y0 away from x1,y1 */
935 /* distance to segment is length of px,py vector */
936 /* "length of its (clipped) projection" is now 0.0 */
937 projlenSq = 0.0;
938 } else {
939 /* switch to backwards vectors relative to x1,y1 */
940 /* x1,y1 are already the negative of x0,y0=>x1,y1 */
941 /* to get px,py to be the negative of px,py=>x1,y1 */
942 /* the dot product of two negated vectors is the same */
943 /* as the dot product of the two normal vectors */
944 px = x1 - px;
945 py = y1 - py;
946 dotprod = px * x1 + py * y1;
947 if (dotprod <= 0.0) {
948 /* px,py is on the side of x1,y1 away from x0,y0 */
949 /* distance to segment is length of (backwards) px,py vector */
950 /* "length of its (clipped) projection" is now 0.0 */
951 projlenSq = 0.0;
952 } else {
953 /* px,py is between x0,y0 and x1,y1 */
954 /* dotprod is the length of the px,py vector */
955 /* projected on the x1,y1=>x0,y0 vector times the */
956 /* length of the x1,y1=>x0,y0 vector */
957 projlenSq = dotprod * dotprod / (x1 * x1 + y1 * y1);
958 }
959 }
960 /* Distance to line is now the length of the relative point */
961 /* vector minus the length of its projection onto the line */
962 /* (which is zero if the projection falls outside the range */
963 /* of the line segment). */
964 return px * px + py * py - projlenSq;
965}
966
967static jboolean
968appendSegment(pathData *pd,
969 jfloat x0, jfloat y0,
970 jfloat x1, jfloat y1)
971{
972 jbyte windDir;
973 jint istartx, istarty, ilasty;
974 jfloat dx, dy, slope;
975 jfloat ystartbump;
976 jint bumpx, bumperr, error;
977 segmentData *seg;
978
979 if (y0 > y1) {
980 jfloat t;
981 t = x0; x0 = x1; x1 = t;
982 t = y0; y0 = y1; y1 = t;
983 windDir = -1;
984 } else {
985 windDir = 1;
986 }
987 /* We want to iterate at every horizontal pixel center (HPC) crossing. */
988 /* First calculate next highest HPC we will cross at the start. */
989 istarty = (jint) ceil(y0 - 0.5f);
990 /* Then calculate next highest HPC we would cross at the end. */
991 ilasty = (jint) ceil(y1 - 0.5f);
992 /* Ignore if we start and end outside clip, or on the same scanline. */
993 if (istarty >= ilasty || istarty >= pd->hiy || ilasty <= pd->loy) {
994 return JNI_TRUE;
995 }
996
997 /* We will need to insert this segment, check for room. */
998 if (pd->numSegments >= pd->segmentsSize) {
999 segmentData *newSegs;
1000 int newSize = pd->segmentsSize + GROW_SIZE;
1001 newSegs = (segmentData *) calloc(newSize, sizeof(segmentData));
1002 if (newSegs == NULL) {
1003 return JNI_FALSE;
1004 }
1005 if (pd->segments != NULL) {
1006 memcpy(newSegs, pd->segments,
1007 sizeof(segmentData) * pd->segmentsSize);
1008 free(pd->segments);
1009 }
1010 pd->segments = newSegs;
1011 pd->segmentsSize = newSize;
1012 }
1013
1014 dx = x1 - x0;
1015 dy = y1 - y0;
1016 slope = dx / dy;
1017
1018 /*
1019 * The Y coordinate of the first HPC was calculated as istarty. We
1020 * now need to calculate the corresponding X coordinate (both integer
1021 * version for span start coordinate and float version for sub-pixel
1022 * error calculation).
1023 */
1024 /* First, how far does y bump to get to next HPC? */
1025 ystartbump = istarty + 0.5f - y0;
1026 /* Now, bump the float x coordinate to get X sample at that HPC. */
1027 x0 += ystartbump * dx / dy;
1028 /* Now calculate the integer coordinate that such a span starts at. */
1029 /* NOTE: Span inclusion is based on vertical pixel centers (VPC). */
1030 istartx = (jint) ceil(x0 - 0.5f);
1031 /* What is the lower bound of the per-scanline change in the X coord? */
1032 bumpx = (jint) floor(slope);
1033 /* What is the subpixel amount by which the bumpx is off? */
1034 bumperr = FRACTTOJINT(slope - floor(slope));
1035 /* Finally, find out how far the x coordinate can go before next VPC. */
1036 error = FRACTTOJINT(x0 - (istartx - 0.5f));
1037
1038 seg = &pd->segments[pd->numSegments++];
1039 seg->curx = istartx;
1040 seg->cury = istarty;
1041 seg->lasty = ilasty;
1042 seg->error = error;
1043 seg->bumpx = bumpx;
1044 seg->bumperr = bumperr;
1045 seg->windDir = windDir;
1046 return JNI_TRUE;
1047}
1048
1049/*
1050 * Lines don't really need to be subdivided, but this function performs
1051 * the same trivial rejections and reductions that the curve subdivision
1052 * functions perform before it hands the coordinates off to the appendSegment
1053 * function.
1054 */
1055static jboolean
1056subdivideLine(pathData *pd, int level,
1057 jfloat x0, jfloat y0,
1058 jfloat x1, jfloat y1)
1059{
1060 jfloat miny, maxy;
1061 jfloat minx, maxx;
1062
1063 minmax2(x0, x1, minx, maxx);
1064 minmax2(y0, y1, miny, maxy);
1065
1066 if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) {
1067 return JNI_TRUE;
1068 }
1069 if (maxx <= pd->lox) {
1070 return appendSegment(pd, maxx, y0, maxx, y1);
1071 }
1072
1073 return appendSegment(pd, x0, y0, x1, y1);
1074}
1075
1076static jboolean
1077subdivideQuad(pathData *pd, int level,
1078 jfloat x0, jfloat y0,
1079 jfloat x1, jfloat y1,
1080 jfloat x2, jfloat y2)
1081{
1082 jfloat miny, maxy;
1083 jfloat minx, maxx;
1084
1085 minmax3(x0, x1, x2, minx, maxx);
1086 minmax3(y0, y1, y2, miny, maxy);
1087
1088 if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) {
1089 return JNI_TRUE;
1090 }
1091 if (maxx <= pd->lox) {
1092 return appendSegment(pd, maxx, y0, maxx, y2);
1093 }
1094
1095 if (level < SUBDIVIDE_MAX) {
1096 /* Test if the curve is flat enough for insertion. */
1097 if (ptSegDistSq(x0, y0, x2, y2, x1, y1) > MAX_FLAT_SQ) {
1098 jfloat cx1, cx2;
1099 jfloat cy1, cy2;
1100
1101 cx1 = (x0 + x1) / 2.0f;
1102 cx2 = (x1 + x2) / 2.0f;
1103 x1 = (cx1 + cx2) / 2.0f;
1104
1105 cy1 = (y0 + y1) / 2.0f;
1106 cy2 = (y1 + y2) / 2.0f;
1107 y1 = (cy1 + cy2) / 2.0f;
1108
1109 level++;
1110 return (subdivideQuad(pd, level, x0, y0, cx1, cy1, x1, y1) &&
1111 subdivideQuad(pd, level, x1, y1, cx2, cy2, x2, y2));
1112 }
1113 }
1114
1115 return appendSegment(pd, x0, y0, x2, y2);
1116}
1117
1118static jboolean
1119subdivideCubic(pathData *pd, int level,
1120 jfloat x0, jfloat y0,
1121 jfloat x1, jfloat y1,
1122 jfloat x2, jfloat y2,
1123 jfloat x3, jfloat y3)
1124{
1125 jfloat miny, maxy;
1126 jfloat minx, maxx;
1127
1128 minmax4(x0, x1, x2, x3, minx, maxx);
1129 minmax4(y0, y1, y2, y3, miny, maxy);
1130
1131 if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) {
1132 return JNI_TRUE;
1133 }
1134 if (maxx <= pd->lox) {
1135 return appendSegment(pd, maxx, y0, maxx, y3);
1136 }
1137
1138 if (level < SUBDIVIDE_MAX) {
1139 /* Test if the curve is flat enough for insertion. */
1140 if (ptSegDistSq(x0, y0, x3, y3, x1, y1) > MAX_FLAT_SQ ||
1141 ptSegDistSq(x0, y0, x3, y3, x2, y2) > MAX_FLAT_SQ)
1142 {
1143 jfloat ctrx, cx12, cx21;
1144 jfloat ctry, cy12, cy21;
1145
1146 ctrx = (x1 + x2) / 2.0f;
1147 x1 = (x0 + x1) / 2.0f;
1148 x2 = (x2 + x3) / 2.0f;
1149 cx12 = (x1 + ctrx) / 2.0f;
1150 cx21 = (ctrx + x2) / 2.0f;
1151 ctrx = (cx12 + cx21) / 2.0f;
1152
1153 ctry = (y1 + y2) / 2.0f;
1154 y1 = (y0 + y1) / 2.0f;
1155 y2 = (y2 + y3) / 2.0f;
1156 cy12 = (y1 + ctry) / 2.0f;
1157 cy21 = (ctry + y2) / 2.0f;
1158 ctry = (cy12 + cy21) / 2.0f;
1159
1160 level++;
1161 return (subdivideCubic(pd, level, x0, y0, x1, y1,
1162 cx12, cy12, ctrx, ctry) &&
1163 subdivideCubic(pd, level, ctrx, ctry, cx21, cy21,
1164 x2, y2, x3, y3));
1165 }
1166 }
1167
1168 return appendSegment(pd, x0, y0, x3, y3);
1169}
1170
1171static int CDECL
1172sortSegmentsByLeadingY(const void *elem1, const void *elem2)
1173{
1174 segmentData *seg1 = *(segmentData **)elem1;
1175 segmentData *seg2 = *(segmentData **)elem2;
1176
1177 if (seg1->cury < seg2->cury) {
1178 return -1;
1179 }
1180 if (seg1->cury > seg2->cury) {
1181 return 1;
1182 }
1183 if (seg1->curx < seg2->curx) {
1184 return -1;
1185 }
1186 if (seg1->curx > seg2->curx) {
1187 return 1;
1188 }
1189 if (seg1->lasty < seg2->lasty) {
1190 return -1;
1191 }
1192 if (seg1->lasty > seg2->lasty) {
1193 return 1;
1194 }
1195 return 0;
1196}
1197
1198static void *
1199ShapeSIOpen(JNIEnv *env, jobject iterator)
1200{
1201 return GetSpanData(env, iterator, STATE_PATH_DONE, STATE_PATH_DONE);
1202}
1203
1204static void
1205ShapeSIClose(JNIEnv *env, void *private)
1206{
1207}
1208
1209static void
1210ShapeSIGetPathBox(JNIEnv *env, void *private, jint pathbox[])
1211{
1212 pathData *pd = (pathData *)private;
1213
1214 pathbox[0] = (jint) floor(pd->pathlox);
1215 pathbox[1] = (jint) floor(pd->pathloy);
1216 pathbox[2] = (jint) ceil(pd->pathhix);
1217 pathbox[3] = (jint) ceil(pd->pathhiy);
1218}
1219
1220/* Adjust the clip box from the given bounds. Used to constrain
1221 the output to a device clip
1222*/
1223static void
1224ShapeSIIntersectClipBox(JNIEnv *env, void *private,
1225 jint clox, jint cloy, jint chix, jint chiy)
1226{
1227 pathData *pd = (pathData *)private;
1228
1229 if (clox > pd->lox) {
1230 pd->lox = clox;
1231 }
1232 if (cloy > pd->loy) {
1233 pd->loy = cloy;
1234 }
1235 if (chix < pd->hix) {
1236 pd->hix = chix;
1237 }
1238 if (chiy < pd->hiy) {
1239 pd->hiy = chiy;
1240 }
1241}
1242
1243static jboolean
1244ShapeSINextSpan(void *state, jint spanbox[])
1245{
1246 pathData *pd = (pathData *)state;
1247 int lo, cur, new, hi;
1248 int num = pd->numSegments;
1249 jint x0, x1, y0, err;
1250 jint loy;
1251 int ret = JNI_FALSE;
1252 segmentData **segmentTable;
1253 segmentData *seg;
1254
1255 if (pd->state != STATE_SPAN_STARTED) {
1256 if (!initSegmentTable(pd)) {
1257 /* REMIND: - throw exception? */
1258 pd->lowSegment = num;
1259 return JNI_FALSE;
1260 }
1261 }
1262
1263 lo = pd->lowSegment;
1264 cur = pd->curSegment;
1265 hi = pd->hiSegment;
1266 num = pd->numSegments;
1267 loy = pd->loy;
1268 segmentTable = pd->segmentTable;
1269
1270 while (lo < num) {
1271 if (cur < hi) {
1272 seg = segmentTable[cur];
1273 x0 = seg->curx;
1274 if (x0 >= pd->hix) {
1275 cur = hi;
1276 continue;
1277 }
1278 if (x0 < pd->lox) {
1279 x0 = pd->lox;
1280 }
1281
1282 if (pd->evenodd) {
1283 cur += 2;
1284 if (cur <= hi) {
1285 x1 = segmentTable[cur - 1]->curx;
1286 } else {
1287 x1 = pd->hix;
1288 }
1289 } else {
1290 int wind = seg->windDir;
1291 cur++;
1292
1293 while (JNI_TRUE) {
1294 if (cur >= hi) {
1295 x1 = pd->hix;
1296 break;
1297 }
1298 seg = segmentTable[cur++];
1299 wind += seg->windDir;
1300 if (wind == 0) {
1301 x1 = seg->curx;
1302 break;
1303 }
1304 }
1305 }
1306
1307 if (x1 > pd->hix) {
1308 x1 = pd->hix;
1309 }
1310 if (x1 <= x0) {
1311 continue;
1312 }
1313 spanbox[0] = x0;
1314 spanbox[1] = loy;
1315 spanbox[2] = x1;
1316 spanbox[3] = loy + 1;
1317 ret = JNI_TRUE;
1318 break;
1319 }
1320
1321 if (++loy >= pd->hiy) {
1322 lo = cur = hi = num;
1323 break;
1324 }
1325
1326 /* Go through active segments and toss which end "above" loy */
1327 cur = new = hi;
1328 while (--cur >= lo) {
1329 seg = segmentTable[cur];
1330 if (seg->lasty > loy) {
1331 segmentTable[--new] = seg;
1332 }
1333 }
1334
1335 lo = new;
1336 if (lo == hi && lo < num) {
1337 /* The current list of segments is empty so we need to
1338 * jump to the beginning of the next set of segments.
1339 * Since the segments are not clipped to the output
1340 * area we need to make sure we don't jump "backwards"
1341 */
1342 seg = segmentTable[lo];
1343 if (loy < seg->cury) {
1344 loy = seg->cury;
1345 }
1346 }
1347
1348 /* Go through new segments and accept any which start "above" loy */
1349 while (hi < num && segmentTable[hi]->cury <= loy) {
1350 hi++;
1351 }
1352
1353 /* Update and sort the active segments by x0 */
1354 for (cur = lo; cur < hi; cur++) {
1355 seg = segmentTable[cur];
1356
1357 /* First update the x0, y0 of the segment */
1358 x0 = seg->curx;
1359 y0 = seg->cury;
1360 err = seg->error;
1361 if (++y0 == loy) {
1362 x0 += seg->bumpx;
1363 err += seg->bumperr;
1364 x0 -= (err >> 31);
1365 err &= ERRSTEP_MAX;
1366 } else {
1367 jlong steps = loy;
1368 steps -= y0 - 1;
1369 y0 = loy;
1370 x0 += (jint) (steps * seg->bumpx);
1371 steps = err + (steps * seg->bumperr);
1372 x0 += (jint) (steps >> 31);
1373 err = ((jint) steps) & ERRSTEP_MAX;
1374 }
1375 seg->curx = x0;
1376 seg->cury = y0;
1377 seg->error = err;
1378
1379 /* Then make sure the segment is sorted by x0 */
1380 for (new = cur; new > lo; new--) {
1381 segmentData *seg2 = segmentTable[new - 1];
1382 if (seg2->curx <= x0) {
1383 break;
1384 }
1385 segmentTable[new] = seg2;
1386 }
1387 segmentTable[new] = seg;
1388 }
1389 cur = lo;
1390 }
1391
1392 pd->lowSegment = lo;
1393 pd->hiSegment = hi;
1394 pd->curSegment = cur;
1395 pd->loy = loy;
1396 return ret;
1397}
1398
1399static void
1400ShapeSISkipDownTo(void *private, jint y)
1401{
1402 pathData *pd = (pathData *)private;
1403
1404 if (pd->state != STATE_SPAN_STARTED) {
1405 if (!initSegmentTable(pd)) {
1406 /* REMIND: - throw exception? */
1407 pd->lowSegment = pd->numSegments;
1408 return;
1409 }
1410 }
1411
1412 /* Make sure we are jumping forward */
1413 if (pd->loy < y) {
1414 /* Pretend like we just finished with the span line y-1... */
1415 pd->loy = y - 1;
1416 pd->curSegment = pd->hiSegment; /* no more segments on that line */
1417 }
1418}
1419
1420static jboolean
1421initSegmentTable(pathData *pd)
1422{
1423 int i, cur, num, loy;
1424 segmentData **segmentTable;
1425 segmentTable = malloc(pd->numSegments * sizeof(segmentData *));
1426 if (segmentTable == NULL) {
1427 return JNI_FALSE;
1428 }
1429 pd->state = STATE_SPAN_STARTED;
1430 for (i = 0; i < pd->numSegments; i++) {
1431 segmentTable[i] = &pd->segments[i];
1432 }
1433 qsort(segmentTable, pd->numSegments, sizeof(segmentData *),
1434 sortSegmentsByLeadingY);
1435
1436 pd->segmentTable = segmentTable;
1437
1438 /* Skip to the first segment that ends below the top clip edge */
1439 cur = 0;
1440 num = pd->numSegments;
1441 loy = pd->loy;
1442 while (cur < num && segmentTable[cur]->lasty <= loy) {
1443 cur++;
1444 }
1445 pd->lowSegment = pd->curSegment = pd->hiSegment = cur;
1446
1447 /* Prepare for next action to increment loy and prepare new segments */
1448 pd->loy--;
1449
1450 return JNI_TRUE;
1451}