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 */
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 (*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
674JNIEXPORT void JNICALL
675Java_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
689JNIEXPORT void JNICALL
690Java_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
704JNIEXPORT void JNICALL
705Java_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
720JNIEXPORT void JNICALL
721Java_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
738JNIEXPORT void JNICALL
739Java_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
752JNIEXPORT void JNICALL
753Java_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
766JNIEXPORT jlong JNICALL
767Java_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
779static jboolean
780PCMoveTo(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
791static jboolean
792PCLineTo(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
803static jboolean
804PCQuadTo(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
816static jboolean
817PCCubicTo(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
830static jboolean
831PCClosePath(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
841static jboolean
842PCPathDone(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) \
869do { \
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) \
880do { \
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) \
901do { \
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
921static jfloat
922ptSegDistSq(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
970static jboolean
971appendSegment(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 */
1058static jboolean
1059subdivideLine(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
1079static jboolean
1080subdivideQuad(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
1121static jboolean
1122subdivideCubic(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
1174static int CDECL
1175sortSegmentsByLeadingY(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
1201static void *
1202ShapeSIOpen(JNIEnv *env, jobject iterator)
1203{
1204 return GetSpanData(env, iterator, STATE_PATH_DONE, STATE_PATH_DONE);
1205}
1206
1207static void
1208ShapeSIClose(JNIEnv *env, void *private)
1209{
1210}
1211
1212static void
1213ShapeSIGetPathBox(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*/
1226static void
1227ShapeSIIntersectClipBox(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
1246static jboolean
1247ShapeSINextSpan(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
1402static void
1403ShapeSISkipDownTo(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
1423static jboolean
1424initSegmentTable(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