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