blob: d17525910884296d79532b4f786fb23b71778d96 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2006 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
26package sun.java2d.loops;
27
28import java.awt.geom.Path2D;
29import java.awt.geom.PathIterator;
30import java.awt.geom.QuadCurve2D;
31import sun.awt.SunHints;
32import java.util.*;
33
34/* This is the java implementation of the native code from
35 * src/share/native/sun/java2d/loops/ProcessPath.[c,h]
36 * This code is written to be as much similar to the native
37 * as it possible. So, it sometimes does not follow java naming conventions.
38 *
39 * It's important to keep this code synchronized with native one. See more
40 * comments, description and high level scheme of the rendering process in the
41 * ProcessPath.c
42 */
43
44public class ProcessPath {
45
46 /* Public interfaces and methods for drawing and filling general paths */
47
48 public static abstract class DrawHandler {
49 public int xMin;
50 public int yMin;
51 public int xMax;
52 public int yMax;
53 public float xMinf;
54 public float yMinf;
55 public float xMaxf;
56 public float yMaxf;
57
58 public int strokeControl;
59
60 public DrawHandler(int xMin, int yMin, int xMax, int yMax,
61 int strokeControl)
62 {
63 setBounds(xMin, yMin, xMax, yMax, strokeControl);
64 }
65
66 public void setBounds(int xMin, int yMin, int xMax, int yMax)
67 {
68 this.xMin = xMin;
69 this.yMin = yMin;
70 this.xMax = xMax;
71 this.yMax = yMax;
72
73 /* Setting up fractional clipping box
74 *
75 * We are using following float -> int mapping:
76 *
77 * xi = floor(xf + 0.5)
78 *
79 * So, fractional values that hit the [xmin, xmax) integer interval
80 * will be situated inside the [xmin-0.5, xmax - 0.5) fractional
81 * interval. We are using EPSF constant to provide that upper
82 * boundary is not included.
83 */
84 xMinf = xMin - 0.5f;
85 yMinf = yMin - 0.5f;
86 xMaxf = xMax - 0.5f - EPSF;
87 yMaxf = yMax - 0.5f - EPSF;
88 }
89
90 public void setBounds(int xMin, int yMin, int xMax, int yMax,
91 int strokeControl)
92 {
93 this.strokeControl = strokeControl;
94 setBounds(xMin, yMin, xMax, yMax);
95 }
96
97 public void adjustBounds(int bxMin, int byMin, int bxMax, int byMax)
98 {
99 if (xMin > bxMin) bxMin = xMin;
100 if (xMax < bxMax) bxMax = xMax;
101 if (yMin > byMin) byMin = yMin;
102 if (yMax < byMax) byMax = yMax;
103 setBounds(bxMin, byMin, bxMax, byMax);
104 }
105
106 public DrawHandler(int xMin, int yMin, int xMax, int yMax) {
107 this(xMin, yMin, xMax, yMax, SunHints.INTVAL_STROKE_DEFAULT);
108 }
109
110 public abstract void drawLine(int x0, int y0, int x1, int y1);
111
112 public abstract void drawPixel(int x0, int y0);
113
114 public abstract void drawScanline(int x0, int x1, int y0);
115 }
116
117 public interface EndSubPathHandler {
118 public void processEndSubPath();
119 }
120
121 public static final int PH_MODE_DRAW_CLIP = 0;
122 public static final int PH_MODE_FILL_CLIP = 1;
123
124 public static abstract class ProcessHandler implements EndSubPathHandler {
125 DrawHandler dhnd;
126 int clipMode;
127
128 public ProcessHandler(DrawHandler dhnd,
129 int clipMode) {
130 this.dhnd = dhnd;
131 this.clipMode = clipMode;
132 }
133
134 public abstract void processFixedLine(int x1, int y1,
135 int x2, int y2, int [] pixelInfo,
136 boolean checkBounds,
137 boolean endSubPath);
138 }
139
140 public static EndSubPathHandler noopEndSubPathHandler =
141 new EndSubPathHandler() {
142 public void processEndSubPath() { }
143 };
144
145 public static boolean fillPath(DrawHandler dhnd, Path2D.Float p2df,
146 int transX, int transY)
147 {
148 FillProcessHandler fhnd = new FillProcessHandler(dhnd);
149 if (!doProcessPath(fhnd, p2df, transX, transY)) {
150 return false;
151 }
152 FillPolygon(fhnd, p2df.getWindingRule());
153 return true;
154 }
155
156 public static boolean drawPath(DrawHandler dhnd,
157 EndSubPathHandler endSubPath,
158 Path2D.Float p2df,
159 int transX, int transY)
160 {
161 return doProcessPath(new DrawProcessHandler(dhnd, endSubPath),
162 p2df, transX, transY);
163 }
164
165 public static boolean drawPath(DrawHandler dhnd,
166 Path2D.Float p2df,
167 int transX, int transY)
168 {
169 return doProcessPath(new DrawProcessHandler(dhnd,
170 noopEndSubPathHandler),
171 p2df, transX, transY);
172 }
173
174 /* Private implementation of the rendering process */
175
176 /* Boundaries used for skipping huge path segments */
177 private static final float UPPER_BND = Float.MAX_VALUE/4.0f;
178 private static final float LOWER_BND = -UPPER_BND;
179
180 /* Precision (in bits) used in forward differencing */
181 private static final int FWD_PREC = 7;
182
183 /* Precision (in bits) used for the rounding in the midpoint test */
184 private static final int MDP_PREC = 10;
185
186 private static final int MDP_MULT = 1 << MDP_PREC;
187 private static final int MDP_HALF_MULT = MDP_MULT >> 1;
188
189 /* Boundaries used for clipping large path segments (those are inside
190 * [UPPER/LOWER]_BND boundaries)
191 */
192 private static final int UPPER_OUT_BND = 1 << (30 - MDP_PREC);
193 private static final int LOWER_OUT_BND = -UPPER_OUT_BND;
194
195
196 /* Calculation boundaries. They are used for switching to the more slow but
197 * allowing larger input values method of calculation of the initial values
198 * of the scan converted line segments inside the FillPolygon
199 */
200 private static final float CALC_UBND = 1 << (30 - MDP_PREC);
201 private static final float CALC_LBND = -CALC_UBND;
202
203
204 /* Following constants are used for providing open boundaries of the
205 * intervals
206 */
207 public static final int EPSFX = 1;
208 public static final float EPSF = ((float)EPSFX)/MDP_MULT;
209
210 /* Bit mask used to separate whole part from the fraction part of the
211 * number
212 */
213 private static final int MDP_W_MASK = -MDP_MULT;
214
215 /* Bit mask used to separate fractional part from the whole part of the
216 * number
217 */
218 private static final int MDP_F_MASK = MDP_MULT - 1;
219
220 /*
221 * Constants for the forward differencing
222 * of the cubic and quad curves
223 */
224
225 /* Maximum size of the cubic curve (calculated as the size of the bounding
226 * box of the control points) which could be rendered without splitting
227 */
228 private static final int MAX_CUB_SIZE = 256;
229
230 /* Maximum size of the quad curve (calculated as the size of the bounding
231 * box of the control points) which could be rendered without splitting
232 */
233 private static final int MAX_QUAD_SIZE = 1024;
234
235 /* Default power of 2 steps used in the forward differencing. Here DF prefix
236 * stands for DeFault. Constants below are used as initial values for the
237 * adaptive forward differencing algorithm.
238 */
239 private static final int DF_CUB_STEPS = 3;
240 private static final int DF_QUAD_STEPS = 2;
241
242 /* Shift of the current point of the curve for preparing to the midpoint
243 * rounding
244 */
245 private static final int DF_CUB_SHIFT = FWD_PREC + DF_CUB_STEPS*3 -
246 MDP_PREC;
247 private static final int DF_QUAD_SHIFT = FWD_PREC + DF_QUAD_STEPS*2 -
248 MDP_PREC;
249
250 /* Default amount of steps of the forward differencing */
251 private static final int DF_CUB_COUNT = (1<<DF_CUB_STEPS);
252 private static final int DF_QUAD_COUNT = (1<<DF_QUAD_STEPS);
253
254 /* Default boundary constants used to check the necessity of the restepping
255 */
256 private static final int DF_CUB_DEC_BND = 1<<DF_CUB_STEPS*3 + FWD_PREC + 2;
257 private static final int DF_CUB_INC_BND = 1<<DF_CUB_STEPS*3 + FWD_PREC - 1;
258 private static final int DF_QUAD_DEC_BND = 1<<DF_QUAD_STEPS*2 +
259 FWD_PREC + 2;
260 private static final int DF_QUAD_INC_BND = 1<<DF_QUAD_STEPS*2 +
261 FWD_PREC - 1;
262
263 /* Multipliers for the coefficients of the polynomial form of the cubic and
264 * quad curves representation
265 */
266 private static final int CUB_A_SHIFT = FWD_PREC;
267 private static final int CUB_B_SHIFT = (DF_CUB_STEPS + FWD_PREC + 1);
268 private static final int CUB_C_SHIFT = (DF_CUB_STEPS*2 + FWD_PREC);
269
270 private static final int CUB_A_MDP_MULT = (1<<CUB_A_SHIFT);
271 private static final int CUB_B_MDP_MULT = (1<<CUB_B_SHIFT);
272 private static final int CUB_C_MDP_MULT = (1<<CUB_C_SHIFT);
273
274 private static final int QUAD_A_SHIFT = FWD_PREC;
275 private static final int QUAD_B_SHIFT = (DF_QUAD_STEPS + FWD_PREC);
276
277 private static final int QUAD_A_MDP_MULT = (1<<QUAD_A_SHIFT);
278 private static final int QUAD_B_MDP_MULT = (1<<QUAD_B_SHIFT);
279
280 /* Clipping macros for drawing and filling algorithms */
281 private static float CLIP(float a1, float b1, float a2, float b2,
282 double t) {
283 return (float)(b1 + (double)(t - a1)*(b2 - b1) / (a2 - a1));
284 }
285
286 private static int CLIP(int a1, int b1, int a2, int b2, double t) {
287 return (int)(b1 + (double)(t - a1)*(b2 - b1) / (a2 - a1));
288 }
289
290
291 private static final int CRES_MIN_CLIPPED = 0;
292 private static final int CRES_MAX_CLIPPED = 1;
293 private static final int CRES_NOT_CLIPPED = 3;
294 private static final int CRES_INVISIBLE = 4;
295
296 private static boolean IS_CLIPPED(int res) {
297 return res == CRES_MIN_CLIPPED || res == CRES_MAX_CLIPPED;
298 }
299
300 /* This is java implementation of the macro from ProcessGeneralPath.c.
301 * To keep the logic of the java code similar to the native one
302 * array and set of indexes are used to point out the data.
303 */
304 private static int TESTANDCLIP(float LINE_MIN, float LINE_MAX, float[] c,
305 int a1, int b1, int a2, int b2) {
306 double t;
307 int res = CRES_NOT_CLIPPED;
308 if (c[a1] < (LINE_MIN) || c[a1] > (LINE_MAX)) {
309 if (c[a1] < (LINE_MIN)) {
310 if (c[a2] < (LINE_MIN)) {
311 return CRES_INVISIBLE;
312 };
313 res = CRES_MIN_CLIPPED;
314 t = (LINE_MIN);
315 } else {
316 if (c[a2] > (LINE_MAX)) {
317 return CRES_INVISIBLE;
318 };
319 res = CRES_MAX_CLIPPED;
320 t = (LINE_MAX);
321 }
322 c[b1] = CLIP(c[a1], c[b1], c[a2], c[b2], t);
323 c[a1] = (float)t;
324 }
325 return res;
326 }
327
328 /* Integer version of the method above */
329 private static int TESTANDCLIP(int LINE_MIN, int LINE_MAX, int[] c,
330 int a1, int b1, int a2, int b2) {
331 double t;
332 int res = CRES_NOT_CLIPPED;
333 if (c[a1] < (LINE_MIN) || c[a1] > (LINE_MAX)) {
334 if (c[a1] < (LINE_MIN)) {
335 if (c[a2] < (LINE_MIN)) {
336 return CRES_INVISIBLE;
337 };
338 res = CRES_MIN_CLIPPED;
339 t = (LINE_MIN);
340 } else {
341 if (c[a2] > (LINE_MAX)) {
342 return CRES_INVISIBLE;
343 };
344 res = CRES_MAX_CLIPPED;
345 t = (LINE_MAX);
346 }
347 c[b1] = CLIP(c[a1], c[b1], c[a2], c[b2], t);
348 c[a1] = (int)t;
349 }
350 return res;
351 }
352
353
354
355 /* Following method is used for clipping and clumping filled shapes.
356 * An example of this process is shown on the picture below:
357 * ----+ ----+
358 * |/ | |/ |
359 * + | + |
360 * /| | I |
361 * / | | I |
362 * | | | ===> I |
363 * \ | | I |
364 * \| | I |
365 * + | + |
366 * |\ | |\ |
367 * | ----+ | ----+
368 * boundary boundary
369 *
370 * We can only perform clipping in case of right side of the output area
371 * because all segments passed out the right boundary don't influence on the
372 * result of scan conversion algorithm (it correctly handles half open
373 * contours).
374 *
375 * This is java implementation of the macro from ProcessGeneralPath.c.
376 * To keep the logic of the java code similar to the native one
377 * array and set of indexes are used to point out the data.
378 *
379 */
380 private static int CLIPCLAMP(float LINE_MIN, float LINE_MAX, float[] c,
381 int a1, int b1, int a2, int b2,
382 int a3, int b3) {
383 c[a3] = c[a1];
384 c[b3] = c[b1];
385 int res = TESTANDCLIP(LINE_MIN, LINE_MAX, c, a1, b1, a2, b2);
386 if (res == CRES_MIN_CLIPPED) {
387 c[a3] = c[a1];
388 } else if (res == CRES_MAX_CLIPPED) {
389 c[a3] = c[a1];
390 res = CRES_MAX_CLIPPED;
391 } else if (res == CRES_INVISIBLE) {
392 if (c[a1] > LINE_MAX) {
393 res = CRES_INVISIBLE;
394 } else {
395 c[a1] = LINE_MIN;
396 c[a2] = LINE_MIN;
397 res = CRES_NOT_CLIPPED;
398 }
399 }
400 return res;
401 }
402
403 /* Integer version of the method above */
404 private static int CLIPCLAMP(int LINE_MIN, int LINE_MAX, int[] c,
405 int a1, int b1, int a2, int b2,
406 int a3, int b3) {
407 c[a3] = c[a1];
408 c[b3] = c[b1];
409 int res = TESTANDCLIP(LINE_MIN, LINE_MAX, c, a1, b1, a2, b2);
410 if (res == CRES_MIN_CLIPPED) {
411 c[a3] = c[a1];
412 } else if (res == CRES_MAX_CLIPPED) {
413 c[a3] = c[a1];
414 res = CRES_MAX_CLIPPED;
415 } else if (res == CRES_INVISIBLE) {
416 if (c[a1] > LINE_MAX) {
417 res = CRES_INVISIBLE;
418 } else {
419 c[a1] = LINE_MIN;
420 c[a2] = LINE_MIN;
421 res = CRES_NOT_CLIPPED;
422 }
423 }
424 return res;
425 }
426
427 private static class DrawProcessHandler extends ProcessHandler {
428
429 EndSubPathHandler processESP;
430
431 public DrawProcessHandler(DrawHandler dhnd,
432 EndSubPathHandler processESP) {
433 super(dhnd, PH_MODE_DRAW_CLIP);
434 this.dhnd = dhnd;
435 this.processESP = processESP;
436 }
437
438 public void processEndSubPath() {
439 processESP.processEndSubPath();
440 }
441
442 void PROCESS_LINE(int fX0, int fY0, int fX1, int fY1,
443 boolean checkBounds, int[] pixelInfo) {
444 int X0 = fX0 >> MDP_PREC;
445 int Y0 = fY0 >> MDP_PREC;
446 int X1 = fX1 >> MDP_PREC;
447 int Y1 = fY1 >> MDP_PREC;
448
449 /* Handling lines having just one pixel */
450 if (((X0^X1) | (Y0^Y1)) == 0) {
451 if (checkBounds &&
452 (dhnd.yMin > Y0 ||
453 dhnd.yMax <= Y0 ||
454 dhnd.xMin > X0 ||
455 dhnd.xMax <= X0)) return;
456
457 if (pixelInfo[0] == 0) {
458 pixelInfo[0] = 1;
459 pixelInfo[1] = X0;
460 pixelInfo[2] = Y0;
461 pixelInfo[3] = X0;
462 pixelInfo[4] = Y0;
463 dhnd.drawPixel(X0, Y0);
464 } else if ((X0 != pixelInfo[3] || Y0 != pixelInfo[4]) &&
465 (X0 != pixelInfo[1] || Y0 != pixelInfo[2])) {
466 dhnd.drawPixel(X0, Y0);
467 pixelInfo[3] = X0;
468 pixelInfo[4] = Y0;
469 }
470 return;
471 }
472
473 if (!checkBounds ||
474 (dhnd.yMin <= Y0 &&
475 dhnd.yMax > Y0 &&
476 dhnd.xMin <= X0 &&
477 dhnd.xMax > X0))
478 {
479 /* Switch off first pixel of the line before drawing */
480 if (pixelInfo[0] == 1 &&
481 ((pixelInfo[1] == X0 && pixelInfo[2] == Y0) ||
482 (pixelInfo[3] == X0 && pixelInfo[4] == Y0)))
483 {
484 dhnd.drawPixel(X0, Y0);
485 }
486 }
487
488 dhnd.drawLine(X0, Y0, X1, Y1);
489
490 if (pixelInfo[0] == 0) {
491 pixelInfo[0] = 1;
492 pixelInfo[1] = X0;
493 pixelInfo[2] = Y0;
494 pixelInfo[3] = X0;
495 pixelInfo[4] = Y0;
496 }
497
498 /* Switch on last pixel of the line if it was already
499 * drawn during rendering of the previous segments
500 */
501 if ((pixelInfo[1] == X1 && pixelInfo[2] == Y1) ||
502 (pixelInfo[3] == X1 && pixelInfo[4] == Y1))
503 {
504 if (checkBounds &&
505 (dhnd.yMin > Y1 ||
506 dhnd.yMax <= Y1 ||
507 dhnd.xMin > X1 ||
508 dhnd.xMax <= X1)) {
509 return;
510 }
511
512 dhnd.drawPixel(X1, Y1);
513 }
514 pixelInfo[3] = X1;
515 pixelInfo[4] = Y1;
516 }
517
518 void PROCESS_POINT(int fX, int fY, boolean checkBounds,
519 int[] pixelInfo) {
520 int _X = fX>> MDP_PREC;
521 int _Y = fY>> MDP_PREC;
522 if (checkBounds &&
523 (dhnd.yMin > _Y ||
524 dhnd.yMax <= _Y ||
525 dhnd.xMin > _X ||
526 dhnd.xMax <= _X)) return;
527 /*
528 * (_X,_Y) should be inside boundaries
529 *
530 * assert(dhnd.yMin <= _Y &&
531 * dhnd.yMax > _Y &&
532 * dhnd.xMin <= _X &&
533 * dhnd.xMax > _X);
534 *
535 */
536 if (pixelInfo[0] == 0) {
537 pixelInfo[0] = 1;
538 pixelInfo[1] = _X;
539 pixelInfo[2] = _Y;
540 pixelInfo[3] = _X;
541 pixelInfo[4] = _Y;
542 dhnd.drawPixel(_X, _Y);
543 } else if ((_X != pixelInfo[3] || _Y != pixelInfo[4]) &&
544 (_X != pixelInfo[1] || _Y != pixelInfo[2])) {
545 dhnd.drawPixel(_X, _Y);
546 pixelInfo[3] = _X;
547 pixelInfo[4] = _Y;
548 }
549 }
550
551 /* Drawing line with subpixel endpoints
552 *
553 * (x1, y1), (x2, y2) - fixed point coordinates of the endpoints
554 * with MDP_PREC bits for the fractional part
555 *
556 * pixelInfo - structure which keeps drawing info for avoiding
557 * multiple drawing at the same position on the
558 * screen (required for the XOR mode of drawing)
559 *
560 * pixelInfo[0] - state of the drawing
561 * 0 - no pixel drawn between
562 * moveTo/close of the path
563 * 1 - there are drawn pixels
564 *
565 * pixelInfo[1,2] - first pixel of the path
566 * between moveTo/close of the
567 * path
568 *
569 * pixelInfo[3,4] - last drawn pixel between
570 * moveTo/close of the path
571 *
572 * checkBounds - flag showing necessity of checking the clip
573 *
574 */
575 public void processFixedLine(int x1, int y1, int x2, int y2,
576 int[] pixelInfo, boolean checkBounds,
577 boolean endSubPath) {
578
579 /* Checking if line is inside a (X,Y),(X+MDP_MULT,Y+MDP_MULT) box */
580 int c = ((x1 ^ x2) | (y1 ^ y2));
581 int rx1, ry1, rx2, ry2;
582 if ((c & MDP_W_MASK) == 0) {
583 /* Checking for the segments with integer coordinates having
584 * the same start and end points
585 */
586 if (c == 0) {
587 PROCESS_POINT(x1 + MDP_HALF_MULT, y1 + MDP_HALF_MULT,
588 checkBounds, pixelInfo);
589 }
590 return;
591 }
592
593 if (x1 == x2 || y1 == y2) {
594 rx1 = x1 + MDP_HALF_MULT;
595 rx2 = x2 + MDP_HALF_MULT;
596 ry1 = y1 + MDP_HALF_MULT;
597 ry2 = y2 + MDP_HALF_MULT;
598 } else {
599 /* Neither dx nor dy can be zero because of the check above */
600 int dx = x2 - x1;
601 int dy = y2 - y1;
602
603 /* Floor of x1, y1, x2, y2 */
604 int fx1 = x1 & MDP_W_MASK;
605 int fy1 = y1 & MDP_W_MASK;
606 int fx2 = x2 & MDP_W_MASK;
607 int fy2 = y2 & MDP_W_MASK;
608
609 /* Processing first endpoint */
610 if (fx1 == x1 || fy1 == y1) {
611 /* Adding MDP_HALF_MULT to the [xy]1 if f[xy]1 == [xy]1
612 * will not affect the result
613 */
614 rx1 = x1 + MDP_HALF_MULT;
615 ry1 = y1 + MDP_HALF_MULT;
616 } else {
617 /* Boundary at the direction from (x1,y1) to (x2,y2) */
618 int bx1 = (x1 < x2) ? fx1 + MDP_MULT : fx1;
619 int by1 = (y1 < y2) ? fy1 + MDP_MULT : fy1;
620
621 /* intersection with column bx1 */
622 int cross = y1 + ((bx1 - x1)*dy)/dx;
623 if (cross >= fy1 && cross <= fy1 + MDP_MULT) {
624 rx1 = bx1;
625 ry1 = cross + MDP_HALF_MULT;
626 } else {
627 /* intersection with row by1 */
628 cross = x1 + ((by1 - y1)*dx)/dy;
629 rx1 = cross + MDP_HALF_MULT;
630 ry1 = by1;
631 }
632 }
633
634 /* Processing second endpoint */
635 if (fx2 == x2 || fy2 == y2) {
636 /* Adding MDP_HALF_MULT to the [xy]2 if f[xy]2 == [xy]2
637 * will not affect the result
638 */
639 rx2 = x2 + MDP_HALF_MULT;
640 ry2 = y2 + MDP_HALF_MULT;
641 } else {
642 /* Boundary at the direction from (x2,y2) to (x1,y1) */
643 int bx2 = (x1 > x2) ? fx2 + MDP_MULT : fx2;
644 int by2 = (y1 > y2) ? fy2 + MDP_MULT : fy2;
645
646 /* intersection with column bx2 */
647 int cross = y2 + ((bx2 - x2)*dy)/dx;
648 if (cross >= fy2 && cross <= fy2 + MDP_MULT) {
649 rx2 = bx2;
650 ry2 = cross + MDP_HALF_MULT;
651 } else {
652 /* intersection with row by2 */
653 cross = x2 + ((by2 - y2)*dx)/dy;
654 rx2 = cross + MDP_HALF_MULT;
655 ry2 = by2;
656 }
657 }
658 }
659 PROCESS_LINE(rx1, ry1, rx2, ry2, checkBounds, pixelInfo);
660 }
661 }
662
663 /* Performing drawing of the monotonic in X and Y quadratic curves with
664 * sizes less than MAX_QUAD_SIZE by using forward differencing method of
665 * calculation. See comments to the DrawMonotonicQuad in the
666 * ProcessGeneralPath.c
667 */
668 private static void DrawMonotonicQuad(ProcessHandler hnd,
669 float[] coords,
670 boolean checkBounds,
671 int[] pixelInfo) {
672
673 int x0 = (int)(coords[0]*MDP_MULT);
674 int y0 = (int)(coords[1]*MDP_MULT);
675
676 int xe = (int)(coords[4]*MDP_MULT);
677 int ye = (int)(coords[5]*MDP_MULT);
678
679 /* Extracting fractional part of coordinates of first control point */
680 int px = (x0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT;
681 int py = (y0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT;
682
683 /* Setting default amount of steps */
684 int count = DF_QUAD_COUNT;
685
686 /* Setting default shift for preparing to the midpoint rounding */
687 int shift = DF_QUAD_SHIFT;
688
689 int ax = (int)((coords[0] - 2*coords[2] +
690 coords[4])*QUAD_A_MDP_MULT);
691 int ay = (int)((coords[1] - 2*coords[3] +
692 coords[5])*QUAD_A_MDP_MULT);
693
694 int bx = (int)((-2*coords[0] + 2*coords[2])*QUAD_B_MDP_MULT);
695 int by = (int)((-2*coords[1] + 2*coords[3])*QUAD_B_MDP_MULT);
696
697 int ddpx = 2*ax;
698 int ddpy = 2*ay;
699
700 int dpx = ax + bx;
701 int dpy = ay + by;
702
703 int x1, y1;
704
705 int x2 = x0;
706 int y2 = y0;
707
708 int maxDD = Math.max(Math.abs(ddpx),Math.abs(ddpy));
709
710 int dx = xe - x0;
711 int dy = ye - y0;
712
713 int x0w = x0 & MDP_W_MASK;
714 int y0w = y0 & MDP_W_MASK;
715
716 /* Perform decreasing step in 2 times if slope of the first forward
717 * difference changes too quickly (more than a pixel per step in X or Y
718 * direction). We can perform adjusting of the step size before the
719 * rendering loop because the curvature of the quad curve remains the
720 * same along all the curve
721 */
722 while (maxDD > DF_QUAD_DEC_BND) {
723 dpx = (dpx<<1) - ax;
724 dpy = (dpy<<1) - ay;
725 count <<= 1;
726 maxDD >>= 2;
727 px <<=2;
728 py <<=2;
729 shift += 2;
730 }
731
732 while(count-- > 1) {
733 px += dpx;
734 py += dpy;
735
736 dpx += ddpx;
737 dpy += ddpy;
738
739 x1 = x2;
740 y1 = y2;
741
742 x2 = x0w + (px >> shift);
743 y2 = y0w + (py >> shift);
744
745 /* Checking that we are not running out of the endpoint and bounding
746 * violating coordinate. The check is pretty simple because the
747 * curve passed to the DrawCubic already splitted into the
748 * monotonic in X and Y pieces
749 */
750
751 /* Bounding x2 by xe */
752 if (((xe-x2)^dx) < 0) {
753 x2 = xe;
754 }
755
756 /* Bounding y2 by ye */
757 if (((ye-y2)^dy) < 0) {
758 y2 = ye;
759 }
760
761 hnd.processFixedLine(x1, y1, x2, y2, pixelInfo, checkBounds, false);
762 }
763
764 /* We are performing one step less than necessary and use actual
765 * (xe,ye) endpoint of the curve instead of calculated. This prevent us
766 * from running above the curve endpoint due to the accumulated errors
767 * during the iterations.
768 */
769
770 hnd.processFixedLine(x2, y2, xe, ye, pixelInfo, checkBounds, false);
771 }
772
773 /*
774 * Checking size of the quad curves and split them if necessary.
775 * Calling DrawMonotonicQuad for the curves of the appropriate size.
776 * Note: coords array could be changed
777 */
778 private static void ProcessMonotonicQuad(ProcessHandler hnd,
779 float[] coords,
780 int[] pixelInfo) {
781
782 float[] coords1 = new float[6];
783 float tx, ty;
784 float xMin, yMin, xMax, yMax;
785
786 xMin = xMax = coords[0];
787 yMin = yMax = coords[1];
788 for (int i = 2; i < 6; i += 2) {
789 xMin = (xMin > coords[i])? coords[i] : xMin;
790 xMax = (xMax < coords[i])? coords[i] : xMax;
791 yMin = (yMin > coords[i + 1])? coords[i + 1] : yMin;
792 yMax = (yMax < coords[i + 1])? coords[i + 1] : yMax;
793 }
794
795 if (hnd.clipMode == PH_MODE_DRAW_CLIP) {
796
797 /* In case of drawing we could just skip curves which are
798 * completely out of bounds
799 */
800 if (hnd.dhnd.xMaxf < xMin || hnd.dhnd.xMinf > xMax ||
801 hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax) {
802 return;
803 }
804 } else {
805
806 /* In case of filling we could skip curves which are above,
807 * below and behind the right boundary of the visible area
808 */
809
810 if (hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax ||
811 hnd.dhnd.xMaxf < xMin)
812 {
813 return;
814 }
815
816 /* We could clamp x coordinates to the corresponding boundary
817 * if the curve is completely behind the left one
818 */
819
820 if (hnd.dhnd.xMinf > xMax) {
821 coords[0] = coords[2] = coords[4] = hnd.dhnd.xMinf;
822 }
823 }
824
825 if (xMax - xMin > MAX_QUAD_SIZE || yMax - yMin > MAX_QUAD_SIZE) {
826 coords1[4] = coords[4];
827 coords1[5] = coords[5];
828 coords1[2] = (coords[2] + coords[4])/2.0f;
829 coords1[3] = (coords[3] + coords[5])/2.0f;
830 coords[2] = (coords[0] + coords[2])/2.0f;
831 coords[3] = (coords[1] + coords[3])/2.0f;
832 coords[4] = coords1[0] = (coords[2] + coords1[2])/2.0f;
833 coords[5] = coords1[1] = (coords[3] + coords1[3])/2.0f;
834
835 ProcessMonotonicQuad(hnd, coords, pixelInfo);
836
837 ProcessMonotonicQuad(hnd, coords1, pixelInfo);
838 } else {
839 DrawMonotonicQuad(hnd, coords,
840 /* Set checkBounds parameter if curve intersects
841 * boundary of the visible area. We know that the
842 * curve is visible, so the check is pretty
843 * simple
844 */
845 hnd.dhnd.xMinf >= xMin ||
846 hnd.dhnd.xMaxf <= xMax ||
847 hnd.dhnd.yMinf >= yMin ||
848 hnd.dhnd.yMaxf <= yMax,
849 pixelInfo);
850 }
851 }
852
853 /*
854 * Split quadratic curve into monotonic in X and Y parts. Calling
855 * ProcessMonotonicQuad for each monotonic piece of the curve.
856 * Note: coords array could be changed
857 */
858 private static void ProcessQuad(ProcessHandler hnd, float[] coords,
859 int[] pixelInfo) {
860 /* Temporary array for holding parameters corresponding to the extreme
861 * in X and Y points
862 */
863 double params[] = new double[2];
864 int cnt = 0;
865 double param;
866
867 /* Simple check for monotonicity in X before searching for the extreme
868 * points of the X(t) function. We first check if the curve is
869 * monotonic in X by seeing if all of the X coordinates are strongly
870 * ordered.
871 */
872 if ((coords[0] > coords[2] || coords[2] > coords[4]) &&
873 (coords[0] < coords[2] || coords[2] < coords[4]))
874 {
875 /* Searching for extreme points of the X(t) function by solving
876 * dX(t)
877 * ---- = 0 equation
878 * dt
879 */
880 double ax = coords[0] - 2*coords[2] + coords[4];
881 if (ax != 0) {
882 /* Calculating root of the following equation
883 * ax*t + bx = 0
884 */
885 double bx = coords[0] - coords[2];
886
887 param = bx/ax;
888 if (param < 1.0 && param > 0.0) {
889 params[cnt++] = param;
890 }
891 }
892 }
893
894 /* Simple check for monotonicity in Y before searching for the extreme
895 * points of the Y(t) function. We first check if the curve is
896 * monotonic in Y by seeing if all of the Y coordinates are strongly
897 * ordered.
898 */
899 if ((coords[1] > coords[3] || coords[3] > coords[5]) &&
900 (coords[1] < coords[3] || coords[3] < coords[5]))
901 {
902 /* Searching for extreme points of the Y(t) function by solving
903 * dY(t)
904 * ----- = 0 equation
905 * dt
906 */
907 double ay = coords[1] - 2*coords[3] + coords[5];
908
909 if (ay != 0) {
910 /* Calculating root of the following equation
911 * ay*t + by = 0
912 */
913 double by = coords[1] - coords[3];
914
915 param = by/ay;
916 if (param < 1.0 && param > 0.0) {
917 if (cnt > 0) {
918 /* Inserting parameter only if it differs from
919 * already stored
920 */
921 if (params[0] > param) {
922 params[cnt++] = params[0];
923 params[0] = param;
924 } else if (params[0] < param) {
925 params[cnt++] = param;
926 }
927 } else {
928 params[cnt++] = param;
929 }
930 }
931 }
932 }
933
934 /* Processing obtained monotonic parts */
935 switch(cnt) {
936 case 0:
937 break;
938 case 1:
939 ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,
940 (float)params[0]);
941 break;
942 case 2:
943 ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,
944 (float)params[0]);
945 param = params[1] - params[0];
946 if (param > 0) {
947 ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,
948 /* Scale parameter to match with
949 * rest of the curve
950 */
951 (float)(param/(1.0 - params[0])));
952 }
953 break;
954 }
955
956 ProcessMonotonicQuad(hnd,coords,pixelInfo);
957 }
958
959 /*
960 * Bite the piece of the quadratic curve from start point till the point
961 * corresponding to the specified parameter then call ProcessQuad for the
962 * bitten part.
963 * Note: coords array will be changed
964 */
965 private static void ProcessFirstMonotonicPartOfQuad(ProcessHandler hnd,
966 float[] coords,
967 int[] pixelInfo,
968 float t) {
969 float[] coords1 = new float[6];
970
971 coords1[0] = coords[0];
972 coords1[1] = coords[1];
973 coords1[2] = coords[0] + t*(coords[2] - coords[0]);
974 coords1[3] = coords[1] + t*(coords[3] - coords[1]);
975 coords[2] = coords[2] + t*(coords[4] - coords[2]);
976 coords[3] = coords[3] + t*(coords[5] - coords[3]);
977 coords[0] = coords1[4] = coords1[2] + t*(coords[2] - coords1[2]);
978 coords[1] = coords1[5] = coords1[3] + t*(coords[3] - coords1[3]);
979
980 ProcessMonotonicQuad(hnd, coords1, pixelInfo);
981 }
982
983 /* Performing drawing of the monotonic in X and Y cubic curves with sizes
984 * less than MAX_CUB_SIZE by using forward differencing method of
985 * calculation. See comments to the DrawMonotonicCubic in the
986 * ProcessGeneralPath.c
987 */
988 private static void DrawMonotonicCubic(ProcessHandler hnd,
989 float[] coords,
990 boolean checkBounds,
991 int[] pixelInfo) {
992 int x0 = (int)(coords[0]*MDP_MULT);
993 int y0 = (int)(coords[1]*MDP_MULT);
994
995 int xe = (int)(coords[6]*MDP_MULT);
996 int ye = (int)(coords[7]*MDP_MULT);
997
998 /* Extracting fractional part of coordinates of first control point */
999 int px = (x0 & (~MDP_W_MASK)) << DF_CUB_SHIFT;
1000 int py = (y0 & (~MDP_W_MASK)) << DF_CUB_SHIFT;
1001
1002 /* Setting default boundary values for checking first and second forward
1003 * difference for the necessity of the restepping. See comments to the
1004 * boundary values in ProcessQuad for more info.
1005 */
1006 int incStepBnd = DF_CUB_INC_BND;
1007 int decStepBnd = DF_CUB_DEC_BND;
1008
1009 /* Setting default amount of steps */
1010 int count = DF_CUB_COUNT;
1011
1012 /* Setting default shift for preparing to the midpoint rounding */
1013 int shift = DF_CUB_SHIFT;
1014
1015 int ax = (int)((-coords[0] + 3*coords[2] - 3*coords[4] +
1016 coords[6])*CUB_A_MDP_MULT);
1017 int ay = (int)((-coords[1] + 3*coords[3] - 3*coords[5] +
1018 coords[7])*CUB_A_MDP_MULT);
1019
1020 int bx = (int)((3*coords[0] - 6*coords[2] +
1021 3*coords[4])*CUB_B_MDP_MULT);
1022 int by = (int)((3*coords[1] - 6*coords[3] +
1023 3*coords[5])*CUB_B_MDP_MULT);
1024
1025 int cx = (int)((-3*coords[0] + 3*coords[2])*(CUB_C_MDP_MULT));
1026 int cy = (int)((-3*coords[1] + 3*coords[3])*(CUB_C_MDP_MULT));
1027
1028 int dddpx = 6*ax;
1029 int dddpy = 6*ay;
1030
1031 int ddpx = dddpx + bx;
1032 int ddpy = dddpy + by;
1033
1034 int dpx = ax + (bx>>1) + cx;
1035 int dpy = ay + (by>>1) + cy;
1036
1037 int x1, y1;
1038
1039 int x2 = x0;
1040 int y2 = y0;
1041
1042 /* Calculating whole part of the first point of the curve */
1043 int x0w = x0 & MDP_W_MASK;
1044 int y0w = y0 & MDP_W_MASK;
1045
1046 int dx = xe - x0;
1047 int dy = ye - y0;
1048
1049 while (count > 0) {
1050 /* Perform decreasing step in 2 times if necessary */
1051 while (Math.abs(ddpx) > decStepBnd ||
1052 Math.abs(ddpy) > decStepBnd) {
1053 ddpx = (ddpx<<1) - dddpx;
1054 ddpy = (ddpy<<1) - dddpy;
1055 dpx = (dpx<<2) - (ddpx>>1);
1056 dpy = (dpy<<2) - (ddpy>>1);
1057 count <<=1;
1058 decStepBnd <<=3;
1059 incStepBnd <<=3;
1060 px <<=3;
1061 py <<=3;
1062 shift += 3;
1063 }
1064
1065 /* Perform increasing step in 2 times if necessary.
1066 * Note: we could do it only in even steps
1067 */
1068
1069 while ((count & 1) == 0 && shift > DF_CUB_SHIFT &&
1070 Math.abs(dpx) <= incStepBnd &&
1071 Math.abs(dpy) <= incStepBnd) {
1072 dpx = (dpx>>2) + (ddpx>>3);
1073 dpy = (dpy>>2) + (ddpy>>3);
1074 ddpx = (ddpx + dddpx)>>1;
1075 ddpy = (ddpy + dddpy)>>1;
1076 count >>=1;
1077 decStepBnd >>=3;
1078 incStepBnd >>=3;
1079 px >>=3;
1080 py >>=3;
1081 shift -= 3;
1082 }
1083
1084 count--;
1085
1086 /* Performing one step less than necessary and use actual (xe,ye)
1087 * curve's endpoint instead of calculated. This prevent us from
1088 * running above the curve endpoint due to the accumulated errors
1089 * during the iterations.
1090 */
1091 if (count > 0) {
1092 px += dpx;
1093 py += dpy;
1094
1095 dpx += ddpx;
1096 dpy += ddpy;
1097 ddpx += dddpx;
1098 ddpy += dddpy;
1099
1100 x1 = x2;
1101 y1 = y2;
1102
1103 x2 = x0w + (px >> shift);
1104 y2 = y0w + (py >> shift);
1105
1106 /* Checking that we are not running out of the endpoint and
1107 * bounding violating coordinate. The check is pretty simple
1108 * because the curve passed to the DrawCubic already splitted
1109 * into the monotonic in X and Y pieces
1110 */
1111
1112 /* Bounding x2 by xe */
1113 if (((xe-x2)^dx) < 0) {
1114 x2 = xe;
1115 }
1116
1117 /* Bounding y2 by ye */
1118 if (((ye-y2)^dy) < 0) {
1119 y2 = ye;
1120 }
1121
1122 hnd.processFixedLine(x1, y1, x2, y2, pixelInfo, checkBounds,
1123 false);
1124 } else {
1125 hnd.processFixedLine(x2, y2, xe, ye, pixelInfo, checkBounds,
1126 false);
1127 }
1128 }
1129 }
1130
1131 /*
1132 * Checking size of the cubic curves and split them if necessary.
1133 * Calling DrawMonotonicCubic for the curves of the appropriate size.
1134 * Note: coords array could be changed
1135 */
1136 private static void ProcessMonotonicCubic(ProcessHandler hnd,
1137 float[] coords,
1138 int[] pixelInfo) {
1139
1140 float[] coords1 = new float[8];
1141 float tx, ty;
1142 float xMin, xMax;
1143 float yMin, yMax;
1144
1145 xMin = xMax = coords[0];
1146 yMin = yMax = coords[1];
1147
1148 for (int i = 2; i < 8; i += 2) {
1149 xMin = (xMin > coords[i])? coords[i] : xMin;
1150 xMax = (xMax < coords[i])? coords[i] : xMax;
1151 yMin = (yMin > coords[i + 1])? coords[i + 1] : yMin;
1152 yMax = (yMax < coords[i + 1])? coords[i + 1] : yMax;
1153 }
1154
1155 if (hnd.clipMode == PH_MODE_DRAW_CLIP) {
1156 /* In case of drawing we could just skip curves which are
1157 * completely out of bounds
1158 */
1159 if (hnd.dhnd.xMaxf < xMin || hnd.dhnd.xMinf > xMax ||
1160 hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax) {
1161 return;
1162 }
1163 } else {
1164
1165 /* In case of filling we could skip curves which are above,
1166 * below and behind the right boundary of the visible area
1167 */
1168
1169 if (hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax ||
1170 hnd.dhnd.xMaxf < xMin)
1171 {
1172 return;
1173 }
1174
1175 /* We could clamp x coordinates to the corresponding boundary
1176 * if the curve is completely behind the left one
1177 */
1178
1179 if (hnd.dhnd.xMinf > xMax) {
1180 coords[0] = coords[2] = coords[4] = coords[6] =
1181 hnd.dhnd.xMinf;
1182 }
1183 }
1184
1185 if (xMax - xMin > MAX_CUB_SIZE || yMax - yMin > MAX_CUB_SIZE) {
1186 coords1[6] = coords[6];
1187 coords1[7] = coords[7];
1188 coords1[4] = (coords[4] + coords[6])/2.0f;
1189 coords1[5] = (coords[5] + coords[7])/2.0f;
1190 tx = (coords[2] + coords[4])/2.0f;
1191 ty = (coords[3] + coords[5])/2.0f;
1192 coords1[2] = (tx + coords1[4])/2.0f;
1193 coords1[3] = (ty + coords1[5])/2.0f;
1194 coords[2] = (coords[0] + coords[2])/2.0f;
1195 coords[3] = (coords[1] + coords[3])/2.0f;
1196 coords[4] = (coords[2] + tx)/2.0f;
1197 coords[5] = (coords[3] + ty)/2.0f;
1198 coords[6]=coords1[0]=(coords[4] + coords1[2])/2.0f;
1199 coords[7]=coords1[1]=(coords[5] + coords1[3])/2.0f;
1200
1201 ProcessMonotonicCubic(hnd, coords, pixelInfo);
1202
1203 ProcessMonotonicCubic(hnd, coords1, pixelInfo);
1204 } else {
1205 DrawMonotonicCubic(hnd, coords,
1206 /* Set checkBounds parameter if curve intersects
1207 * boundary of the visible area. We know that
1208 * the curve is visible, so the check is pretty
1209 * simple
1210 */
1211 hnd.dhnd.xMinf > xMin ||
1212 hnd.dhnd.xMaxf < xMax ||
1213 hnd.dhnd.yMinf > yMin ||
1214 hnd.dhnd.yMaxf < yMax,
1215 pixelInfo);
1216 }
1217 }
1218
1219 /*
1220 * Split cubic curve into monotonic in X and Y parts. Calling
1221 * ProcessMonotonicCubic for each monotonic piece of the curve.
1222 *
1223 * Note: coords array could be changed
1224 */
1225 private static void ProcessCubic(ProcessHandler hnd,
1226 float[] coords,
1227 int[] pixelInfo) {
1228 /* Temporary array for holding parameters corresponding to the extreme
1229 * in X and Y points
1230 */
1231 double params[] = new double[4];
1232 double eqn[] = new double[3];
1233 double res[] = new double[2];
1234 int cnt = 0;
1235
1236 /* Simple check for monotonicity in X before searching for the extreme
1237 * points of the X(t) function. We first check if the curve is
1238 * monotonic in X by seeing if all of the X coordinates are strongly
1239 * ordered.
1240 */
1241 if ((coords[0] > coords[2] || coords[2] > coords[4] ||
1242 coords[4] > coords[6]) &&
1243 (coords[0] < coords[2] || coords[2] < coords[4] ||
1244 coords[4] < coords[6]))
1245 {
1246 /* Searching for extreme points of the X(t) function by solving
1247 * dX(t)
1248 * ---- = 0 equation
1249 * dt
1250 */
1251 eqn[2] = -coords[0] + 3*coords[2] - 3*coords[4] + coords[6];
1252 eqn[1] = 2*(coords[0] - 2*coords[2] + coords[4]);
1253 eqn[0] = -coords[0] + coords[2];
1254
1255 int nr = QuadCurve2D.solveQuadratic(eqn, res);
1256
1257 /* Following code also correctly works in degenerate case of
1258 * the quadratic equation (nr = -1) because we do not need
1259 * splitting in this case.
1260 */
1261 for (int i = 0; i < nr; i++) {
1262 if (res[i] > 0 && res[i] < 1) {
1263 params[cnt++] = res[i];
1264 }
1265 }
1266 }
1267
1268 /* Simple check for monotonicity in Y before searching for the extreme
1269 * points of the Y(t) function. We first check if the curve is
1270 * monotonic in Y by seeing if all of the Y coordinates are strongly
1271 * ordered.
1272 */
1273 if ((coords[1] > coords[3] || coords[3] > coords[5] ||
1274 coords[5] > coords[7]) &&
1275 (coords[1] < coords[3] || coords[3] < coords[5] ||
1276 coords[5] < coords[7]))
1277 {
1278 /* Searching for extreme points of the Y(t) function by solving
1279 * dY(t)
1280 * ----- = 0 equation
1281 * dt
1282 */
1283 eqn[2] = -coords[1] + 3*coords[3] - 3*coords[5] + coords[7];
1284 eqn[1] = 2*(coords[1] - 2*coords[3] + coords[5]);
1285 eqn[0] = -coords[1] + coords[3];
1286
1287 int nr = QuadCurve2D.solveQuadratic(eqn, res);
1288
1289 /* Following code also correctly works in degenerate case of
1290 * the quadratic equation (nr = -1) because we do not need
1291 * splitting in this case.
1292 */
1293 for (int i = 0; i < nr; i++) {
1294 if (res[i] > 0 && res[i] < 1) {
1295 params[cnt++] = res[i];
1296 }
1297 }
1298 }
1299
1300 if (cnt > 0) {
1301 /* Sorting parameter values corresponding to the extreme points
1302 * of the curve
1303 */
1304 Arrays.sort(params, 0, cnt);
1305
1306 /* Processing obtained monotonic parts */
1307 ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo,
1308 (float)params[0]);
1309 for (int i = 1; i < cnt; i++) {
1310 double param = params[i] - params[i-1];
1311 if (param > 0) {
1312 ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo,
1313 /* Scale parameter to match with rest of the curve */
1314 (float)(param/(1.0 - params[i - 1])));
1315 }
1316 }
1317 }
1318
1319 ProcessMonotonicCubic(hnd,coords,pixelInfo);
1320 }
1321
1322 /*
1323 * Bite the piece of the cubic curve from start point till the point
1324 * corresponding to the specified parameter then call ProcessCubic for the
1325 * bitten part.
1326 * Note: coords array will be changed
1327 */
1328 private static void ProcessFirstMonotonicPartOfCubic(ProcessHandler hnd,
1329 float[] coords,
1330 int[] pixelInfo,
1331 float t)
1332 {
1333 float[] coords1 = new float[8];
1334 float tx, ty;
1335
1336 coords1[0] = coords[0];
1337 coords1[1] = coords[1];
1338 tx = coords[2] + t*(coords[4] - coords[2]);
1339 ty = coords[3] + t*(coords[5] - coords[3]);
1340 coords1[2] = coords[0] + t*(coords[2] - coords[0]);
1341 coords1[3] = coords[1] + t*(coords[3] - coords[1]);
1342 coords1[4] = coords1[2] + t*(tx - coords1[2]);
1343 coords1[5] = coords1[3] + t*(ty - coords1[3]);
1344 coords[4] = coords[4] + t*(coords[6] - coords[4]);
1345 coords[5] = coords[5] + t*(coords[7] - coords[5]);
1346 coords[2] = tx + t*(coords[4] - tx);
1347 coords[3] = ty + t*(coords[5] - ty);
1348 coords[0]=coords1[6]=coords1[4] + t*(coords[2] - coords1[4]);
1349 coords[1]=coords1[7]=coords1[5] + t*(coords[3] - coords1[5]);
1350
1351 ProcessMonotonicCubic(hnd, coords1, pixelInfo);
1352 }
1353
1354 /* Note:
1355 * For more easy reading of the code below each java version of the macros
1356 * from the ProcessPath.c preceded by the commented origin call
1357 * containing verbose names of the parameters
1358 */
1359 private static void ProcessLine(ProcessHandler hnd, float x1, float y1,
1360 float x2, float y2, int[] pixelInfo) {
1361 float xMin, yMin, xMax, yMax;
1362 int X1, Y1, X2, Y2, X3, Y3, res;
1363 boolean clipped = false;
1364 float x3,y3;
1365 float c[] = new float[]{x1, y1, x2, y2, 0, 0};
1366
1367 boolean lastClipped;
1368
1369 xMin = hnd.dhnd.xMinf;
1370 yMin = hnd.dhnd.yMinf;
1371 xMax = hnd.dhnd.xMaxf;
1372 yMax = hnd.dhnd.yMaxf;
1373
1374 //
1375 // TESTANDCLIP(yMin, yMax, y1, x1, y2, x2, res);
1376 //
1377 res = TESTANDCLIP(yMin, yMax, c, 1, 0, 3, 2);
1378 if (res == CRES_INVISIBLE) return;
1379 clipped = IS_CLIPPED(res);
1380 //
1381 // TESTANDCLIP(yMin, yMax, y2, x2, y1, x1, res);
1382 //
1383 res = TESTANDCLIP(yMin, yMax, c, 3, 2, 1, 0);
1384 if (res == CRES_INVISIBLE) return;
1385 lastClipped = IS_CLIPPED(res);
1386 clipped = clipped || lastClipped;
1387
1388 if (hnd.clipMode == PH_MODE_DRAW_CLIP) {
1389 //
1390 // TESTANDCLIP(xMin, xMax, x1, y1, x2, y2, res);
1391 //
1392 res = TESTANDCLIP(xMin, xMax, c, 0, 1, 2, 3);
1393 if (res == CRES_INVISIBLE) return;
1394 clipped = clipped || IS_CLIPPED(res);
1395 //
1396 // TESTANDCLIP(xMin, xMax, x2, y2, x1, y1, res);
1397 //
1398 res = TESTANDCLIP(xMin, xMax, c, 2, 3, 0, 1);
1399 if (res == CRES_INVISIBLE) return;
1400 lastClipped = lastClipped || IS_CLIPPED(res);
1401 clipped = clipped || lastClipped;
1402 X1 = (int)(c[0]*MDP_MULT);
1403 Y1 = (int)(c[1]*MDP_MULT);
1404 X2 = (int)(c[2]*MDP_MULT);
1405 Y2 = (int)(c[3]*MDP_MULT);
1406
1407 hnd.processFixedLine(X1, Y1, X2, Y2, pixelInfo,
1408 clipped, /* enable boundary checking in
1409 case of clipping to avoid
1410 entering out of bounds which
1411 could happens during rounding
1412 */
1413 lastClipped /* Notify pProcessFixedLine
1414 that
1415 this is the end of the
1416 subpath (because of exiting
1417 out of boundaries)
1418 */
1419 );
1420 } else {
1421 /* Clamping starting from first vertex of the the processed
1422 * segment
1423 *
1424 * CLIPCLAMP(xMin, xMax, x1, y1, x2, y2, x3, y3, res);
1425 */
1426 res = CLIPCLAMP(xMin, xMax, c, 0, 1, 2, 3, 4, 5);
1427 X1 = (int)(c[0]*MDP_MULT);
1428 Y1 = (int)(c[1]*MDP_MULT);
1429
1430 /* Clamping only by left boundary */
1431 if (res == CRES_MIN_CLIPPED) {
1432 X3 = (int)(c[4]*MDP_MULT);
1433 Y3 = (int)(c[5]*MDP_MULT);
1434 hnd.processFixedLine(X3, Y3, X1, Y1, pixelInfo,
1435 false, lastClipped);
1436
1437 } else if (res == CRES_INVISIBLE) {
1438 return;
1439 }
1440
1441 /* Clamping starting from last vertex of the the processed
1442 * segment
1443 *
1444 * CLIPCLAMP(xMin, xMax, x2, y2, x1, y1, x3, y3, res);
1445 */
1446 res = CLIPCLAMP(xMin, xMax, c, 2, 3, 0, 1, 4, 5);
1447
1448 /* Checking if there was a clip by right boundary */
1449 lastClipped = lastClipped || (res == CRES_MAX_CLIPPED);
1450
1451 X2 = (int)(c[2]*MDP_MULT);
1452 Y2 = (int)(c[3]*MDP_MULT);
1453 hnd.processFixedLine(X1, Y1, X2, Y2, pixelInfo,
1454 false, lastClipped);
1455
1456 /* Clamping only by left boundary */
1457 if (res == CRES_MIN_CLIPPED) {
1458 X3 = (int)(c[4]*MDP_MULT);
1459 Y3 = (int)(c[5]*MDP_MULT);
1460 hnd.processFixedLine(X2, Y2, X3, Y3, pixelInfo,
1461 false, lastClipped);
1462 }
1463 }
1464 }
1465
1466 private static boolean doProcessPath(ProcessHandler hnd,
1467 Path2D.Float p2df,
1468 float transXf, float transYf) {
1469 float coords[] = new float[8];
1470 float tCoords[] = new float[8];
1471 float closeCoord[] = new float[] {0.0f, 0.0f};
1472 float firstCoord[] = new float[2];
1473 int pixelInfo[] = new int[5];
1474 boolean subpathStarted = false;
1475 boolean skip = false;
1476 float lastX, lastY;
1477 pixelInfo[0] = 0;
1478
1479 /* Adjusting boundaries to the capabilities of the
1480 * ProcessPath code
1481 */
1482 hnd.dhnd.adjustBounds(LOWER_OUT_BND, LOWER_OUT_BND,
1483 UPPER_OUT_BND, UPPER_OUT_BND);
1484
1485 /* Adding support of the KEY_STROKE_CONTROL rendering hint.
1486 * Now we are supporting two modes: "pixels at centers" and
1487 * "pixels at corners".
1488 * First one is disabled by default but could be enabled by setting
1489 * VALUE_STROKE_PURE to the rendering hint. It means that pixel at the
1490 * screen (x,y) has (x + 0.5, y + 0.5) float coordinates.
1491 *
1492 * Second one is enabled by default and means straightforward mapping
1493 * (x,y) --> (x,y)
1494 */
1495 if (hnd.dhnd.strokeControl == SunHints.INTVAL_STROKE_PURE) {
1496 closeCoord[0] = -0.5f;
1497 closeCoord[1] = -0.5f;
1498 transXf -= 0.5;
1499 transYf -= 0.5;
1500 }
1501
1502 PathIterator pi = p2df.getPathIterator(null);
1503
1504 while (!pi.isDone()) {
1505 switch (pi.currentSegment(coords)) {
1506 case PathIterator.SEG_MOVETO:
1507 /* Performing closing of the unclosed segments */
1508 if (subpathStarted && !skip) {
1509 if (hnd.clipMode == PH_MODE_FILL_CLIP) {
1510 if (tCoords[0] != closeCoord[0] ||
1511 tCoords[1] != closeCoord[1])
1512 {
1513 ProcessLine(hnd, tCoords[0], tCoords[1],
1514 closeCoord[0], closeCoord[1],
1515 pixelInfo);
1516 }
1517 }
1518 hnd.processEndSubPath();
1519 }
1520
1521 tCoords[0] = coords[0] + transXf;
1522 tCoords[1] = coords[1] + transYf;
1523
1524 /* Checking SEG_MOVETO coordinates if they are out of the
1525 * [LOWER_BND, UPPER_BND] range. This check also handles
1526 * NaN and Infinity values. Skipping next path segment in
1527 * case of invalid data.
1528 */
1529
1530 if (tCoords[0] < UPPER_BND &&
1531 tCoords[0] > LOWER_BND &&
1532 tCoords[1] < UPPER_BND &&
1533 tCoords[1] > LOWER_BND)
1534 {
1535 subpathStarted = true;
1536 skip = false;
1537 closeCoord[0] = tCoords[0];
1538 closeCoord[1] = tCoords[1];
1539 } else {
1540 skip = true;
1541 }
1542 pixelInfo[0] = 0;
1543 break;
1544 case PathIterator.SEG_LINETO:
1545 lastX = tCoords[2] = coords[0] + transXf;
1546 lastY = tCoords[3] = coords[1] + transYf;
1547
1548 /* Checking SEG_LINETO coordinates if they are out of the
1549 * [LOWER_BND, UPPER_BND] range. This check also handles
1550 * NaN and Infinity values. Ignoring current path segment
1551 * in case of invalid data. If segment is skipped its
1552 * endpoint (if valid) is used to begin new subpath.
1553 */
1554
1555 if (lastX < UPPER_BND &&
1556 lastX > LOWER_BND &&
1557 lastY < UPPER_BND &&
1558 lastY > LOWER_BND)
1559 {
1560 if (skip) {
1561 tCoords[0] = closeCoord[0] = lastX;
1562 tCoords[1] = closeCoord[1] = lastY;
1563 subpathStarted = true;
1564 skip = false;
1565 } else {
1566 ProcessLine(hnd, tCoords[0], tCoords[1],
1567 tCoords[2], tCoords[3], pixelInfo);
1568 tCoords[0] = lastX;
1569 tCoords[1] = lastY;
1570 }
1571 }
1572 break;
1573 case PathIterator.SEG_QUADTO:
1574 tCoords[2] = coords[0] + transXf;
1575 tCoords[3] = coords[1] + transYf;
1576 lastX = tCoords[4] = coords[2] + transXf;
1577 lastY = tCoords[5] = coords[3] + transYf;
1578
1579 /* Checking SEG_QUADTO coordinates if they are out of the
1580 * [LOWER_BND, UPPER_BND] range. This check also handles
1581 * NaN and Infinity values. Ignoring current path segment
1582 * in case of invalid endpoints's data. Equivalent to
1583 * the SEG_LINETO if endpoint coordinates are valid but
1584 * there are invalid data among other coordinates
1585 */
1586
1587 if (lastX < UPPER_BND &&
1588 lastX > LOWER_BND &&
1589 lastY < UPPER_BND &&
1590 lastY > LOWER_BND)
1591 {
1592 if (skip) {
1593 tCoords[0] = closeCoord[0] = lastX;
1594 tCoords[1] = closeCoord[1] = lastY;
1595 subpathStarted = true;
1596 skip = false;
1597 } else {
1598 if (tCoords[2] < UPPER_BND &&
1599 tCoords[2] > LOWER_BND &&
1600 tCoords[3] < UPPER_BND &&
1601 tCoords[3] > LOWER_BND)
1602 {
1603 ProcessQuad(hnd, tCoords, pixelInfo);
1604 } else {
1605 ProcessLine(hnd, tCoords[0], tCoords[1],
1606 tCoords[4], tCoords[5],
1607 pixelInfo);
1608 }
1609 tCoords[0] = lastX;
1610 tCoords[1] = lastY;
1611 }
1612 }
1613 break;
1614 case PathIterator.SEG_CUBICTO:
1615 tCoords[2] = coords[0] + transXf;
1616 tCoords[3] = coords[1] + transYf;
1617 tCoords[4] = coords[2] + transXf;
1618 tCoords[5] = coords[3] + transYf;
1619 lastX = tCoords[6] = coords[4] + transXf;
1620 lastY = tCoords[7] = coords[5] + transYf;
1621
1622 /* Checking SEG_CUBICTO coordinates if they are out of the
1623 * [LOWER_BND, UPPER_BND] range. This check also handles
1624 * NaN and Infinity values. Ignoring current path segment
1625 * in case of invalid endpoints's data. Equivalent to
1626 * the SEG_LINETO if endpoint coordinates are valid but
1627 * there are invalid data among other coordinates
1628 */
1629
1630 if (lastX < UPPER_BND &&
1631 lastX > LOWER_BND &&
1632 lastY < UPPER_BND &&
1633 lastY > LOWER_BND)
1634 {
1635 if (skip) {
1636 tCoords[0] = closeCoord[0] = tCoords[6];
1637 tCoords[1] = closeCoord[1] = tCoords[7];
1638 subpathStarted = true;
1639 skip = false;
1640 } else {
1641 if (tCoords[2] < UPPER_BND &&
1642 tCoords[2] > LOWER_BND &&
1643 tCoords[3] < UPPER_BND &&
1644 tCoords[3] > LOWER_BND &&
1645 tCoords[4] < UPPER_BND &&
1646 tCoords[4] > LOWER_BND &&
1647 tCoords[5] < UPPER_BND &&
1648 tCoords[5] > LOWER_BND)
1649 {
1650 ProcessCubic(hnd, tCoords, pixelInfo);
1651 } else {
1652 ProcessLine(hnd, tCoords[0], tCoords[1],
1653 tCoords[6], tCoords[7],
1654 pixelInfo);
1655 }
1656 tCoords[0] = lastX;
1657 tCoords[1] = lastY;
1658 }
1659 }
1660 break;
1661 case PathIterator.SEG_CLOSE:
1662 if (subpathStarted && !skip) {
1663 skip = false;
1664 if (tCoords[0] != closeCoord[0] ||
1665 tCoords[1] != closeCoord[1])
1666 {
1667 ProcessLine(hnd, tCoords[0], tCoords[1],
1668 closeCoord[0], closeCoord[1],
1669 pixelInfo);
1670
1671 /* Storing last path's point for using in following
1672 * segments without initial moveTo
1673 */
1674 tCoords[0] = closeCoord[0];
1675 tCoords[1] = closeCoord[1];
1676 }
1677 hnd.processEndSubPath();
1678 }
1679 break;
1680 }
1681 pi.next();
1682 }
1683
1684 /* Performing closing of the unclosed segments */
1685 if (subpathStarted & !skip) {
1686 if (hnd.clipMode == PH_MODE_FILL_CLIP) {
1687 if (tCoords[0] != closeCoord[0] ||
1688 tCoords[1] != closeCoord[1])
1689 {
1690 ProcessLine(hnd, tCoords[0], tCoords[1],
1691 closeCoord[0], closeCoord[1],
1692 pixelInfo);
1693 }
1694 }
1695 hnd.processEndSubPath();
1696 }
1697 return true;
1698 }
1699
1700 private static class Point {
1701 public int x;
1702 public int y;
1703 public boolean lastPoint;
1704 public Point prev;
1705 public Point next;
1706 public Point nextByY;
1707 public Edge edge;
1708 public Point(int x, int y, boolean lastPoint) {
1709 this.x = x;
1710 this.y = y;
1711 this.lastPoint = lastPoint;
1712 }
1713 };
1714
1715 private static class Edge {
1716 int x;
1717 int dx;
1718 Point p;
1719 int dir;
1720 Edge prev;
1721 Edge next;
1722
1723 public Edge(Point p, int x, int dx, int dir) {
1724 this.p = p;
1725 this.x = x;
1726 this.dx = dx;
1727 this.dir = dir;
1728 }
1729 };
1730
1731 /* Size of the default buffer in the FillData structure. This buffer is
1732 * replaced with heap allocated in case of large paths.
1733 */
1734 private static final int DF_MAX_POINT = 256;
1735
1736 /* Following class accumulates points of the non-continuous flattened
1737 * general path during iteration through the origin path's segments . The
1738 * end of the each subpath is marked as lastPoint flag set at the last
1739 * point
1740 */
1741 private static class FillData {
1742 List<Point> plgPnts;
1743 public int plgYMin;
1744 public int plgYMax;
1745
1746 public FillData() {
1747 plgPnts = new Vector<Point>(DF_MAX_POINT);
1748 }
1749
1750 public void addPoint(int x, int y, boolean lastPoint) {
1751 if (plgPnts.size() == 0) {
1752 plgYMin = plgYMax = y;
1753 } else {
1754 plgYMin = (plgYMin > y)?y:plgYMin;
1755 plgYMax = (plgYMax < y)?y:plgYMax;
1756 }
1757
1758 plgPnts.add(new Point(x, y, lastPoint));
1759 }
1760
1761 public boolean isEmpty() {
1762 return plgPnts.size() == 0;
1763 }
1764
1765 public boolean isEnded() {
1766 return plgPnts.get(plgPnts.size() - 1).lastPoint;
1767 }
1768
1769 public boolean setEnded() {
1770 return plgPnts.get(plgPnts.size() - 1).lastPoint = true;
1771 }
1772 }
1773
1774 private static class ActiveEdgeList {
1775 Edge head;
1776
1777 public boolean isEmpty() {
1778 return (head == null);
1779 }
1780
1781 public void insert(Point pnt, int cy) {
1782 Point np = pnt.next;
1783 int X1 = pnt.x, Y1 = pnt.y;
1784 int X2 = np.x, Y2 = np.y;
1785 Edge ne;
1786 if (Y1 == Y2) {
1787 /* Skipping horizontal segments */
1788 return;
1789 } else {
1790 int dX = X2 - X1;
1791 int dY = Y2 - Y1;
1792 int stepx, x0, dy, dir;
1793
1794 if (Y1 < Y2) {
1795 x0 = X1;
1796 dy = cy - Y1;
1797 dir = -1;
1798 } else { // (Y1 > Y2)
1799 x0 = X2;
1800 dy = cy - Y2;
1801 dir = 1;
1802 }
1803
1804 /* We need to worry only about dX because dY is in denominator
1805 * and abs(dy) < MDP_MULT (cy is a first scanline of the scan
1806 * converted segment and we subtract y coordinate of the
1807 * nearest segment's end from it to obtain dy)
1808 */
1809 if (dX > CALC_UBND || dX < CALC_LBND) {
1810 stepx = (int)((((double)dX)*MDP_MULT)/dY);
1811 x0 = x0 + (int)((((double)dX)*dy)/dY);
1812 } else {
1813 stepx = (dX<<MDP_PREC)/dY;
1814 x0 += (dX*dy)/dY;
1815 }
1816
1817 ne = new Edge(pnt, x0, stepx, dir);
1818 }
1819
1820 ne.next = head;
1821 ne.prev = null;
1822 if (head != null) {
1823 head.prev = ne;
1824 }
1825 head = pnt.edge = ne;
1826 }
1827
1828 public void delete(Edge e) {
1829 Edge prevp = e.prev;
1830 Edge nextp = e.next;
1831 if (prevp != null) {
1832 prevp.next = nextp;
1833 } else {
1834 head = nextp;
1835 }
1836 if (nextp != null) {
1837 nextp.prev = prevp;
1838 }
1839 }
1840
1841 /**
1842 * Bubble sorting in the ascending order of the linked list. This
1843 * implementation stops processing the list if there were no changes
1844 * during the previous pass.
1845 *
1846 * We could not use O(N) Radix sort here because in most cases list of
1847 * edges almost sorted. So, bubble sort (O(N^2)) is working much
1848 * better. Note, in case of array of edges Shell sort is more
1849 * efficient.
1850 */
1851 public void sort() {
1852 Edge p, q, r, s = null, temp;
1853 boolean wasSwap = true;
1854
1855 // r precedes p and s points to the node up to which
1856 // comparisons are to be made
1857 while (s != head.next && wasSwap) {
1858 r = p = head;
1859 q = p.next;
1860 wasSwap = false;
1861 while (p != s) {
1862 if (p.x >= q.x) {
1863 wasSwap = true;
1864 if (p == head) {
1865 temp = q.next;
1866 q.next = p;
1867 p.next = temp;
1868 head = q;
1869 r = q;
1870 } else {
1871 temp = q.next;
1872 q.next = p;
1873 p.next = temp;
1874 r.next = q;
1875 r = q;
1876 }
1877 } else {
1878 r = p;
1879 p = p.next;
1880 }
1881 q = p.next;
1882 if (q == s) s = p;
1883 }
1884 }
1885
1886 // correction of the back links in the double linked edge list
1887 p = head;
1888 q = null;
1889 while (p != null) {
1890 p.prev = q;
1891 q = p;
1892 p = p.next;
1893 }
1894 }
1895 }
1896
1897 private static void FillPolygon(FillProcessHandler hnd,
1898 int fillRule) {
1899 int k, y, n;
1900 boolean drawing;
1901 Edge active;
1902 int rightBnd = hnd.dhnd.xMax - 1;
1903 FillData fd = hnd.fd;
1904 int yMin = fd.plgYMin;
1905 int yMax = fd.plgYMax;
1906 int hashSize = ((yMax - yMin)>>MDP_PREC) + 4;
1907
1908 /* Because of support of the KEY_STROKE_CONTROL hint we are performing
1909 * shift of the coordinates at the higher level
1910 */
1911 int hashOffset = ((yMin - 1) & MDP_W_MASK);
1912
1913 /* Winding counter */
1914 int counter;
1915
1916 /* Calculating mask to be applied to the winding counter */
1917 int counterMask =
1918 (fillRule == PathIterator.WIND_NON_ZERO)? -1:1;
1919
1920 int pntOffset;
1921 List<Point> pnts = fd.plgPnts;
1922 n = pnts.size();
1923
1924 if (n <=1) return;
1925
1926 Point[] yHash = new Point[hashSize];
1927
1928 /* Creating double linked list (prev, next links) describing path order
1929 * and hash table with points which fall between scanlines. nextByY
1930 * link is used for the points which are between same scanlines.
1931 * Scanlines are passed through the centers of the pixels.
1932 */
1933 Point curpt = pnts.get(0);
1934 curpt.prev = null;
1935 for (int i = 0; i < n - 1; i++) {
1936 curpt = pnts.get(i);
1937 Point nextpt = pnts.get(i + 1);
1938 int curHashInd = (curpt.y - hashOffset - 1) >> MDP_PREC;
1939 curpt.nextByY = yHash[curHashInd];
1940 yHash[curHashInd] = curpt;
1941 curpt.next = nextpt;
1942 nextpt.prev = curpt;
1943 }
1944
1945 Point ept = pnts.get(n - 1);
1946 int curHashInd = (ept.y - hashOffset - 1) >> MDP_PREC;
1947 ept.nextByY = yHash[curHashInd];
1948 yHash[curHashInd] = ept;
1949
1950 ActiveEdgeList activeList = new ActiveEdgeList();
1951
1952 for (y=hashOffset + MDP_MULT,k = 0;
1953 y<=yMax && k < hashSize; y += MDP_MULT, k++)
1954 {
1955 for(Point pt = yHash[k];pt != null; pt=pt.nextByY) {
1956 /* pt.y should be inside hashed interval
1957 * assert(y-MDP_MULT <= pt.y && pt.y < y);
1958 */
1959 if (pt.prev != null && !pt.prev.lastPoint) {
1960 if (pt.prev.edge != null && pt.prev.y <= y) {
1961 activeList.delete(pt.prev.edge);
1962 pt.prev.edge = null;
1963 } else if (pt.prev.y > y) {
1964 activeList.insert(pt.prev, y);
1965 }
1966 }
1967
1968 if (!pt.lastPoint && pt.next != null) {
1969 if (pt.edge != null && pt.next.y <= y) {
1970 activeList.delete(pt.edge);
1971 pt.edge = null;
1972 } else if (pt.next.y > y) {
1973 activeList.insert(pt, y);
1974 }
1975 }
1976 }
1977
1978 if (activeList.isEmpty()) continue;
1979
1980 activeList.sort();
1981
1982 counter = 0;
1983 drawing = false;
1984 int xl, xr;
1985 xl = xr = hnd.dhnd.xMin;
1986 Edge curEdge = activeList.head;
1987 while (curEdge != null) {
1988 counter += curEdge.dir;
1989 if ((counter & counterMask) != 0 && !drawing) {
1990 xl = (curEdge.x + MDP_MULT - 1)>>MDP_PREC;
1991 drawing = true;
1992 }
1993
1994 if ((counter & counterMask) == 0 && drawing) {
1995 xr = (curEdge.x - 1) >> MDP_PREC;
1996 if (xl <= xr) {
1997 hnd.dhnd.drawScanline(xl, xr, y >> MDP_PREC);
1998 }
1999 drawing = false;
2000 }
2001
2002 curEdge.x += curEdge.dx;
2003 curEdge = curEdge.next;
2004 }
2005
2006 /* Performing drawing till the right boundary (for correct
2007 * rendering shapes clipped at the right side)
2008 */
2009 if (drawing && xl <= rightBnd) {
2010
2011 /* Support of the strokeHint was added into the
2012 * draw and fill methods of the sun.java2d.pipe.LoopPipe
2013 */
2014 hnd.dhnd.drawScanline(xl, rightBnd, y >> MDP_PREC);
2015 }
2016 }
2017 }
2018
2019 private static class FillProcessHandler extends ProcessHandler {
2020
2021 FillData fd;
2022
2023 /* Note: For more easy reading of the code below each java version of
2024 * the macros from the ProcessPath.c preceded by the commented
2025 * origin call containing verbose names of the parameters
2026 */
2027 public void processFixedLine(int x1, int y1, int x2, int y2,
2028 int[] pixelInfo, boolean checkBounds,
2029 boolean endSubPath)
2030 {
2031 int outXMin, outXMax, outYMin, outYMax;
2032 int res;
2033
2034 /* There is no need to round line coordinates to the forward
2035 * differencing precision anymore. Such a rounding was used for
2036 * preventing the curve go out the endpoint (this sometimes does
2037 * not help). The problem was fixed in the forward differencing
2038 * loops.
2039 */
2040 if (checkBounds) {
2041 boolean lastClipped;
2042
2043 /* This function is used only for filling shapes, so there is no
2044 * check for the type of clipping
2045 */
2046 int c[] = new int[]{x1, y1, x2, y2, 0, 0};
2047 outXMin = (int)(dhnd.xMinf * MDP_MULT);
2048 outXMax = (int)(dhnd.xMaxf * MDP_MULT);
2049 outYMin = (int)(dhnd.yMinf * MDP_MULT);
2050 outYMax = (int)(dhnd.yMaxf * MDP_MULT);
2051
2052 /*
2053 * TESTANDCLIP(outYMin, outYMax, y1, x1, y2, x2, res);
2054 */
2055 res = TESTANDCLIP(outYMin, outYMax, c, 1, 0, 3, 2);
2056 if (res == CRES_INVISIBLE) return;
2057
2058 /*
2059 * TESTANDCLIP(outYMin, outYMax, y2, x2, y1, x1, res);
2060 */
2061 res = TESTANDCLIP(outYMin, outYMax, c, 3, 2, 1, 0);
2062 if (res == CRES_INVISIBLE) return;
2063 lastClipped = IS_CLIPPED(res);
2064
2065 /* Clamping starting from first vertex of the the processed
2066 * segment
2067 *
2068 * CLIPCLAMP(outXMin, outXMax, x1, y1, x2, y2, x3, y3, res);
2069 */
2070 res = CLIPCLAMP(outXMin, outXMax, c, 0, 1, 2, 3, 4, 5);
2071
2072 /* Clamping only by left boundary */
2073 if (res == CRES_MIN_CLIPPED) {
2074 processFixedLine(c[4], c[5], c[0], c[1], pixelInfo,
2075 false, lastClipped);
2076
2077 } else if (res == CRES_INVISIBLE) {
2078 return;
2079 }
2080
2081 /* Clamping starting from last vertex of the the processed
2082 * segment
2083 *
2084 * CLIPCLAMP(outXMin, outXMax, x2, y2, x1, y1, x3, y3, res);
2085 */
2086 res = CLIPCLAMP(outXMin, outXMax, c, 2, 3, 0, 1, 4, 5);
2087
2088 /* Checking if there was a clip by right boundary */
2089 lastClipped = lastClipped || (res == CRES_MAX_CLIPPED);
2090
2091 processFixedLine(c[0], c[1], c[2], c[3], pixelInfo,
2092 false, lastClipped);
2093
2094 /* Clamping only by left boundary */
2095 if (res == CRES_MIN_CLIPPED) {
2096 processFixedLine(c[2], c[3], c[4], c[5], pixelInfo,
2097 false, lastClipped);
2098 }
2099
2100 return;
2101 }
2102
2103 /* Adding first point of the line only in case of empty or just
2104 * finished path
2105 */
2106 if (fd.isEmpty() || fd.isEnded()) {
2107 fd.addPoint(x1, y1, false);
2108 }
2109
2110 fd.addPoint(x2, y2, false);
2111
2112 if (endSubPath) {
2113 fd.setEnded();
2114 }
2115 }
2116
2117 FillProcessHandler(DrawHandler dhnd) {
2118 super(dhnd, PH_MODE_FILL_CLIP);
2119 this.fd = new FillData();
2120 }
2121
2122 public void processEndSubPath() {
2123 if (!fd.isEmpty()) {
2124 fd.setEnded();
2125 }
2126 }
2127 }
2128}