Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/sun/java2d/loops/ProcessPath.java
41159 views
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
package sun.java2d.loops;
27
28
import java.awt.geom.Path2D;
29
import java.awt.geom.PathIterator;
30
import java.awt.geom.QuadCurve2D;
31
import sun.awt.SunHints;
32
import 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
44
public class ProcessPath {
45
46
/* Public interfaces and methods for drawing and filling general paths */
47
48
public abstract static 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 abstract static 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 + (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 + (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 split 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 split
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 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 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 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 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
}
2129
2130