blob: 54d9abaf7001fc6133109407d2fa25740b5b76d9 [file] [log] [blame]
Laurent Bourgès80581a02017-05-17 22:05:11 +02001/*
2 * Copyright (c) 2007, 2017, 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
26package sun.java2d.marlin;
27
28import static sun.java2d.marlin.OffHeapArray.SIZE_INT;
29import jdk.internal.misc.Unsafe;
30
31final class DRenderer implements DPathConsumer2D, MarlinRenderer {
32
33 static final boolean DISABLE_RENDER = false;
34
35 static final boolean ENABLE_BLOCK_FLAGS = MarlinProperties.isUseTileFlags();
36 static final boolean ENABLE_BLOCK_FLAGS_HEURISTICS = MarlinProperties.isUseTileFlagsWithHeuristics();
37
38 private static final int ALL_BUT_LSB = 0xFFFFFFFE;
39 private static final int ERR_STEP_MAX = 0x7FFFFFFF; // = 2^31 - 1
40
41 private static final double POWER_2_TO_32 = 0x1.0p32d;
42
43 // use double to make tosubpix methods faster (no int to double conversion)
44 static final double SUBPIXEL_SCALE_X = SUBPIXEL_POSITIONS_X;
45 static final double SUBPIXEL_SCALE_Y = SUBPIXEL_POSITIONS_Y;
46 static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1;
47 static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1;
48
49 // number of subpixels corresponding to a tile line
50 private static final int SUBPIXEL_TILE
51 = TILE_H << SUBPIXEL_LG_POSITIONS_Y;
52
53 // 2048 (pixelSize) pixels (height) x 8 subpixels = 64K
54 static final int INITIAL_BUCKET_ARRAY
55 = INITIAL_PIXEL_DIM * SUBPIXEL_POSITIONS_Y;
56
57 // crossing capacity = edges count / 4 ~ 1024
58 static final int INITIAL_CROSSING_COUNT = INITIAL_EDGES_COUNT >> 2;
59
60 public static final int WIND_EVEN_ODD = 0;
61 public static final int WIND_NON_ZERO = 1;
62
63 // common to all types of input path segments.
64 // OFFSET as bytes
65 // only integer values:
66 public static final long OFF_CURX_OR = 0;
67 public static final long OFF_ERROR = OFF_CURX_OR + SIZE_INT;
68 public static final long OFF_BUMP_X = OFF_ERROR + SIZE_INT;
69 public static final long OFF_BUMP_ERR = OFF_BUMP_X + SIZE_INT;
70 public static final long OFF_NEXT = OFF_BUMP_ERR + SIZE_INT;
71 public static final long OFF_YMAX = OFF_NEXT + SIZE_INT;
72
73 // size of one edge in bytes
74 public static final int SIZEOF_EDGE_BYTES = (int)(OFF_YMAX + SIZE_INT);
75
76 // curve break into lines
77 // cubic error in subpixels to decrement step
78 private static final double CUB_DEC_ERR_SUBPIX
79 = MarlinProperties.getCubicDecD2() * (NORM_SUBPIXELS / 8.0d); // 1 pixel
80 // cubic error in subpixels to increment step
81 private static final double CUB_INC_ERR_SUBPIX
82 = MarlinProperties.getCubicIncD1() * (NORM_SUBPIXELS / 8.0d); // 0.4 pixel
83
84 // TestNonAARasterization (JDK-8170879): cubics
85 // bad paths (59294/100000 == 59,29%, 94335 bad pixels (avg = 1,59), 3966 warnings (avg = 0,07)
86
87 // cubic bind length to decrement step
88 public static final double CUB_DEC_BND
89 = 8.0d * CUB_DEC_ERR_SUBPIX;
90 // cubic bind length to increment step
91 public static final double CUB_INC_BND
92 = 8.0d * CUB_INC_ERR_SUBPIX;
93
94 // cubic countlg
95 public static final int CUB_COUNT_LG = 2;
96 // cubic count = 2^countlg
97 private static final int CUB_COUNT = 1 << CUB_COUNT_LG;
98 // cubic count^2 = 4^countlg
99 private static final int CUB_COUNT_2 = 1 << (2 * CUB_COUNT_LG);
100 // cubic count^3 = 8^countlg
101 private static final int CUB_COUNT_3 = 1 << (3 * CUB_COUNT_LG);
102 // cubic dt = 1 / count
103 private static final double CUB_INV_COUNT = 1.0d / CUB_COUNT;
104 // cubic dt^2 = 1 / count^2 = 1 / 4^countlg
105 private static final double CUB_INV_COUNT_2 = 1.0d / CUB_COUNT_2;
106 // cubic dt^3 = 1 / count^3 = 1 / 8^countlg
107 private static final double CUB_INV_COUNT_3 = 1.0d / CUB_COUNT_3;
108
109 // quad break into lines
110 // quadratic error in subpixels
111 private static final double QUAD_DEC_ERR_SUBPIX
112 = MarlinProperties.getQuadDecD2() * (NORM_SUBPIXELS / 8.0d); // 0.5 pixel
113
114 // TestNonAARasterization (JDK-8170879): quads
115 // bad paths (62916/100000 == 62,92%, 103818 bad pixels (avg = 1,65), 6514 warnings (avg = 0,10)
116
117 // quadratic bind length to decrement step
118 public static final double QUAD_DEC_BND
119 = 8.0d * QUAD_DEC_ERR_SUBPIX;
120
121//////////////////////////////////////////////////////////////////////////////
122// SCAN LINE
123//////////////////////////////////////////////////////////////////////////////
124 // crossings ie subpixel edge x coordinates
125 private int[] crossings;
126 // auxiliary storage for crossings (merge sort)
127 private int[] aux_crossings;
128
129 // indices into the segment pointer lists. They indicate the "active"
130 // sublist in the segment lists (the portion of the list that contains
131 // all the segments that cross the next scan line).
132 private int edgeCount;
133 private int[] edgePtrs;
134 // auxiliary storage for edge pointers (merge sort)
135 private int[] aux_edgePtrs;
136
137 // max used for both edgePtrs and crossings (stats only)
138 private int activeEdgeMaxUsed;
139
140 // crossings ref (dirty)
141 private final IntArrayCache.Reference crossings_ref;
142 // edgePtrs ref (dirty)
143 private final IntArrayCache.Reference edgePtrs_ref;
144 // merge sort initial arrays (large enough to satisfy most usages) (1024)
145 // aux_crossings ref (dirty)
146 private final IntArrayCache.Reference aux_crossings_ref;
147 // aux_edgePtrs ref (dirty)
148 private final IntArrayCache.Reference aux_edgePtrs_ref;
149
150//////////////////////////////////////////////////////////////////////////////
151// EDGE LIST
152//////////////////////////////////////////////////////////////////////////////
153 private int edgeMinY = Integer.MAX_VALUE;
154 private int edgeMaxY = Integer.MIN_VALUE;
155 private double edgeMinX = Double.POSITIVE_INFINITY;
156 private double edgeMaxX = Double.NEGATIVE_INFINITY;
157
158 // edges [ints] stored in off-heap memory
159 private final OffHeapArray edges;
160
161 private int[] edgeBuckets;
162 private int[] edgeBucketCounts; // 2*newedges + (1 if pruning needed)
163 // used range for edgeBuckets / edgeBucketCounts
164 private int buckets_minY;
165 private int buckets_maxY;
166
167 // edgeBuckets ref (clean)
168 private final IntArrayCache.Reference edgeBuckets_ref;
169 // edgeBucketCounts ref (clean)
170 private final IntArrayCache.Reference edgeBucketCounts_ref;
171
172 // Flattens using adaptive forward differencing. This only carries out
173 // one iteration of the AFD loop. All it does is update AFD variables (i.e.
174 // X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings).
175 private void quadBreakIntoLinesAndAdd(double x0, double y0,
176 final DCurve c,
177 final double x2, final double y2)
178 {
179 int count = 1; // dt = 1 / count
180
181 // maximum(ddX|Y) = norm(dbx, dby) * dt^2 (= 1)
182 double maxDD = Math.abs(c.dbx) + Math.abs(c.dby);
183
184 final double _DEC_BND = QUAD_DEC_BND;
185
186 while (maxDD >= _DEC_BND) {
187 // divide step by half:
188 maxDD /= 4.0d; // error divided by 2^2 = 4
189
190 count <<= 1;
191 if (DO_STATS) {
192 rdrCtx.stats.stat_rdr_quadBreak_dec.add(count);
193 }
194 }
195
196 int nL = 0; // line count
197 if (count > 1) {
198 final double icount = 1.0d / count; // dt
199 final double icount2 = icount * icount; // dt^2
200
201 final double ddx = c.dbx * icount2;
202 final double ddy = c.dby * icount2;
203 double dx = c.bx * icount2 + c.cx * icount;
204 double dy = c.by * icount2 + c.cy * icount;
205
206 double x1, y1;
207
208 while (--count > 0) {
209 x1 = x0 + dx;
210 dx += ddx;
211 y1 = y0 + dy;
212 dy += ddy;
213
214 addLine(x0, y0, x1, y1);
215
216 if (DO_STATS) { nL++; }
217 x0 = x1;
218 y0 = y1;
219 }
220 }
221 addLine(x0, y0, x2, y2);
222
223 if (DO_STATS) {
224 rdrCtx.stats.stat_rdr_quadBreak.add(nL + 1);
225 }
226 }
227
228 // x0, y0 and x3,y3 are the endpoints of the curve. We could compute these
229 // using c.xat(0),c.yat(0) and c.xat(1),c.yat(1), but this might introduce
230 // numerical errors, and our callers already have the exact values.
231 // Another alternative would be to pass all the control points, and call
232 // c.set here, but then too many numbers are passed around.
233 private void curveBreakIntoLinesAndAdd(double x0, double y0,
234 final DCurve c,
235 final double x3, final double y3)
236 {
237 int count = CUB_COUNT;
238 final double icount = CUB_INV_COUNT; // dt
239 final double icount2 = CUB_INV_COUNT_2; // dt^2
240 final double icount3 = CUB_INV_COUNT_3; // dt^3
241
242 // the dx and dy refer to forward differencing variables, not the last
243 // coefficients of the "points" polynomial
244 double dddx, dddy, ddx, ddy, dx, dy;
245 dddx = 2.0d * c.dax * icount3;
246 dddy = 2.0d * c.day * icount3;
247 ddx = dddx + c.dbx * icount2;
248 ddy = dddy + c.dby * icount2;
249 dx = c.ax * icount3 + c.bx * icount2 + c.cx * icount;
250 dy = c.ay * icount3 + c.by * icount2 + c.cy * icount;
251
252 // we use x0, y0 to walk the line
253 double x1 = x0, y1 = y0;
254 int nL = 0; // line count
255
256 final double _DEC_BND = CUB_DEC_BND;
257 final double _INC_BND = CUB_INC_BND;
258
259 while (count > 0) {
260 // divide step by half:
261 while (Math.abs(ddx) + Math.abs(ddy) >= _DEC_BND) {
262 dddx /= 8.0d;
263 dddy /= 8.0d;
264 ddx = ddx / 4.0d - dddx;
265 ddy = ddy / 4.0d - dddy;
266 dx = (dx - ddx) / 2.0d;
267 dy = (dy - ddy) / 2.0d;
268
269 count <<= 1;
270 if (DO_STATS) {
271 rdrCtx.stats.stat_rdr_curveBreak_dec.add(count);
272 }
273 }
274
275 // double step:
276 // can only do this on even "count" values, because we must divide count by 2
277 while (count % 2 == 0
278 && Math.abs(dx) + Math.abs(dy) <= _INC_BND)
279 {
280 dx = 2.0d * dx + ddx;
281 dy = 2.0d * dy + ddy;
282 ddx = 4.0d * (ddx + dddx);
283 ddy = 4.0d * (ddy + dddy);
284 dddx *= 8.0d;
285 dddy *= 8.0d;
286
287 count >>= 1;
288 if (DO_STATS) {
289 rdrCtx.stats.stat_rdr_curveBreak_inc.add(count);
290 }
291 }
292 if (--count > 0) {
293 x1 += dx;
294 dx += ddx;
295 ddx += dddx;
296 y1 += dy;
297 dy += ddy;
298 ddy += dddy;
299 } else {
300 x1 = x3;
301 y1 = y3;
302 }
303
304 addLine(x0, y0, x1, y1);
305
306 if (DO_STATS) { nL++; }
307 x0 = x1;
308 y0 = y1;
309 }
310 if (DO_STATS) {
311 rdrCtx.stats.stat_rdr_curveBreak.add(nL);
312 }
313 }
314
315 private void addLine(double x1, double y1, double x2, double y2) {
316 if (DO_MONITORS) {
317 rdrCtx.stats.mon_rdr_addLine.start();
318 }
319 if (DO_STATS) {
320 rdrCtx.stats.stat_rdr_addLine.add(1);
321 }
322 int or = 1; // orientation of the line. 1 if y increases, 0 otherwise.
323 if (y2 < y1) {
324 or = 0;
325 double tmp = y2;
326 y2 = y1;
327 y1 = tmp;
328 tmp = x2;
329 x2 = x1;
330 x1 = tmp;
331 }
332
333 // convert subpixel coordinates [double] into pixel positions [int]
334
335 // The index of the pixel that holds the next HPC is at ceil(trueY - 0.5)
336 // Since y1 and y2 are biased by -0.5 in tosubpixy(), this is simply
337 // ceil(y1) or ceil(y2)
338 // upper integer (inclusive)
339 final int firstCrossing = FloatMath.max(FloatMath.ceil_int(y1), boundsMinY);
340
341 // note: use boundsMaxY (last Y exclusive) to compute correct coverage
342 // upper integer (exclusive)
343 final int lastCrossing = FloatMath.min(FloatMath.ceil_int(y2), boundsMaxY);
344
345 /* skip horizontal lines in pixel space and clip edges
346 out of y range [boundsMinY; boundsMaxY] */
347 if (firstCrossing >= lastCrossing) {
348 if (DO_MONITORS) {
349 rdrCtx.stats.mon_rdr_addLine.stop();
350 }
351 if (DO_STATS) {
352 rdrCtx.stats.stat_rdr_addLine_skip.add(1);
353 }
354 return;
355 }
356
357 // edge min/max X/Y are in subpixel space (half-open interval):
358 // note: Use integer crossings to ensure consistent range within
359 // edgeBuckets / edgeBucketCounts arrays in case of NaN values (int = 0)
360 if (firstCrossing < edgeMinY) {
361 edgeMinY = firstCrossing;
362 }
363 if (lastCrossing > edgeMaxY) {
364 edgeMaxY = lastCrossing;
365 }
366
367 final double slope = (x1 - x2) / (y1 - y2);
368
369 if (slope >= 0.0d) { // <==> x1 < x2
370 if (x1 < edgeMinX) {
371 edgeMinX = x1;
372 }
373 if (x2 > edgeMaxX) {
374 edgeMaxX = x2;
375 }
376 } else {
377 if (x2 < edgeMinX) {
378 edgeMinX = x2;
379 }
380 if (x1 > edgeMaxX) {
381 edgeMaxX = x1;
382 }
383 }
384
385 // local variables for performance:
386 final int _SIZEOF_EDGE_BYTES = SIZEOF_EDGE_BYTES;
387
388 final OffHeapArray _edges = edges;
389
390 // get free pointer (ie length in bytes)
391 final int edgePtr = _edges.used;
392
393 // use substraction to avoid integer overflow:
394 if (_edges.length - edgePtr < _SIZEOF_EDGE_BYTES) {
395 // suppose _edges.length > _SIZEOF_EDGE_BYTES
396 // so doubling size is enough to add needed bytes
397 // note: throw IOOB if neededSize > 2Gb:
398 final long edgeNewSize = ArrayCacheConst.getNewLargeSize(
399 _edges.length,
400 edgePtr + _SIZEOF_EDGE_BYTES);
401
402 if (DO_STATS) {
403 rdrCtx.stats.stat_rdr_edges_resizes.add(edgeNewSize);
404 }
405 _edges.resize(edgeNewSize);
406 }
407
408
409 final Unsafe _unsafe = OffHeapArray.UNSAFE;
410 final long SIZE_INT = 4L;
411 long addr = _edges.address + edgePtr;
412
413 // The x value must be bumped up to its position at the next HPC we will evaluate.
414 // "firstcrossing" is the (sub)pixel number where the next crossing occurs
415 // thus, the actual coordinate of the next HPC is "firstcrossing + 0.5"
416 // so the Y distance we cover is "firstcrossing + 0.5 - trueY".
417 // Note that since y1 (and y2) are already biased by -0.5 in tosubpixy(), we have
418 // y1 = trueY - 0.5
419 // trueY = y1 + 0.5
420 // firstcrossing + 0.5 - trueY = firstcrossing + 0.5 - (y1 + 0.5)
421 // = firstcrossing - y1
422 // The x coordinate at that HPC is then:
423 // x1_intercept = x1 + (firstcrossing - y1) * slope
424 // The next VPC is then given by:
425 // VPC index = ceil(x1_intercept - 0.5), or alternately
426 // VPC index = floor(x1_intercept - 0.5 + 1 - epsilon)
427 // epsilon is hard to pin down in floating point, but easy in fixed point, so if
428 // we convert to fixed point then these operations get easier:
429 // long x1_fixed = x1_intercept * 2^32; (fixed point 32.32 format)
430 // curx = next VPC = fixed_floor(x1_fixed - 2^31 + 2^32 - 1)
431 // = fixed_floor(x1_fixed + 2^31 - 1)
432 // = fixed_floor(x1_fixed + 0x7FFFFFFF)
433 // and error = fixed_fract(x1_fixed + 0x7FFFFFFF)
434 final double x1_intercept = x1 + (firstCrossing - y1) * slope;
435
436 // inlined scalb(x1_intercept, 32):
437 final long x1_fixed_biased = ((long) (POWER_2_TO_32 * x1_intercept))
438 + 0x7FFFFFFFL;
439 // curx:
440 // last bit corresponds to the orientation
441 _unsafe.putInt(addr, (((int) (x1_fixed_biased >> 31L)) & ALL_BUT_LSB) | or);
442 addr += SIZE_INT;
443 _unsafe.putInt(addr, ((int) x1_fixed_biased) >>> 1);
444 addr += SIZE_INT;
445
446 // inlined scalb(slope, 32):
447 final long slope_fixed = (long) (POWER_2_TO_32 * slope);
448
449 // last bit set to 0 to keep orientation:
450 _unsafe.putInt(addr, (((int) (slope_fixed >> 31L)) & ALL_BUT_LSB));
451 addr += SIZE_INT;
452 _unsafe.putInt(addr, ((int) slope_fixed) >>> 1);
453 addr += SIZE_INT;
454
455 final int[] _edgeBuckets = edgeBuckets;
456 final int[] _edgeBucketCounts = edgeBucketCounts;
457
458 final int _boundsMinY = boundsMinY;
459
460 // each bucket is a linked list. this method adds ptr to the
461 // start of the "bucket"th linked list.
462 final int bucketIdx = firstCrossing - _boundsMinY;
463
464 // pointer from bucket
465 _unsafe.putInt(addr, _edgeBuckets[bucketIdx]);
466 addr += SIZE_INT;
467 // y max (exclusive)
468 _unsafe.putInt(addr, lastCrossing);
469
470 // Update buckets:
471 // directly the edge struct "pointer"
472 _edgeBuckets[bucketIdx] = edgePtr;
473 _edgeBucketCounts[bucketIdx] += 2; // 1 << 1
474 // last bit means edge end
475 _edgeBucketCounts[lastCrossing - _boundsMinY] |= 0x1;
476
477 // update free pointer (ie length in bytes)
478 _edges.used += _SIZEOF_EDGE_BYTES;
479
480 if (DO_MONITORS) {
481 rdrCtx.stats.mon_rdr_addLine.stop();
482 }
483 }
484
485// END EDGE LIST
486//////////////////////////////////////////////////////////////////////////////
487
488 // Cache to store RLE-encoded coverage mask of the current primitive
489 final MarlinCache cache;
490
491 // Bounds of the drawing region, at subpixel precision.
492 private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;
493
494 // Current winding rule
495 private int windingRule;
496
497 // Current drawing position, i.e., final point of last segment
498 private double x0, y0;
499
500 // Position of most recent 'moveTo' command
501 private double sx0, sy0;
502
503 // per-thread renderer context
504 final DRendererContext rdrCtx;
505 // dirty curve
506 private final DCurve curve;
507
508 // clean alpha array (zero filled)
509 private int[] alphaLine;
510
511 // alphaLine ref (clean)
512 private final IntArrayCache.Reference alphaLine_ref;
513
514 private boolean enableBlkFlags = false;
515 private boolean prevUseBlkFlags = false;
516
517 /* block flags (0|1) */
518 private int[] blkFlags;
519
520 // blkFlags ref (clean)
521 private final IntArrayCache.Reference blkFlags_ref;
522
523 DRenderer(final DRendererContext rdrCtx) {
524 this.rdrCtx = rdrCtx;
525
526 this.edges = rdrCtx.newOffHeapArray(INITIAL_EDGES_CAPACITY); // 96K
527
528 this.curve = rdrCtx.curve;
529
530 edgeBuckets_ref = rdrCtx.newCleanIntArrayRef(INITIAL_BUCKET_ARRAY); // 64K
531 edgeBucketCounts_ref = rdrCtx.newCleanIntArrayRef(INITIAL_BUCKET_ARRAY); // 64K
532
533 edgeBuckets = edgeBuckets_ref.initial;
534 edgeBucketCounts = edgeBucketCounts_ref.initial;
535
536 // 2048 (pixelsize) pixel large
537 alphaLine_ref = rdrCtx.newCleanIntArrayRef(INITIAL_AA_ARRAY); // 8K
538 alphaLine = alphaLine_ref.initial;
539
540 this.cache = rdrCtx.cache;
541
542 crossings_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K
543 aux_crossings_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K
544 edgePtrs_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K
545 aux_edgePtrs_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K
546
547 crossings = crossings_ref.initial;
548 aux_crossings = aux_crossings_ref.initial;
549 edgePtrs = edgePtrs_ref.initial;
550 aux_edgePtrs = aux_edgePtrs_ref.initial;
551
552 blkFlags_ref = rdrCtx.newCleanIntArrayRef(INITIAL_ARRAY); // 1K = 1 tile line
553 blkFlags = blkFlags_ref.initial;
554 }
555
556 DRenderer init(final int pix_boundsX, final int pix_boundsY,
557 final int pix_boundsWidth, final int pix_boundsHeight,
558 final int windingRule)
559 {
560 this.windingRule = windingRule;
561
562 // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
563 this.boundsMinX = pix_boundsX << SUBPIXEL_LG_POSITIONS_X;
564 this.boundsMaxX =
565 (pix_boundsX + pix_boundsWidth) << SUBPIXEL_LG_POSITIONS_X;
566 this.boundsMinY = pix_boundsY << SUBPIXEL_LG_POSITIONS_Y;
567 this.boundsMaxY =
568 (pix_boundsY + pix_boundsHeight) << SUBPIXEL_LG_POSITIONS_Y;
569
570 if (DO_LOG_BOUNDS) {
571 MarlinUtils.logInfo("boundsXY = [" + boundsMinX + " ... "
572 + boundsMaxX + "[ [" + boundsMinY + " ... "
573 + boundsMaxY + "[");
574 }
575
576 // see addLine: ceil(boundsMaxY) => boundsMaxY + 1
577 // +1 for edgeBucketCounts
578 final int edgeBucketsLength = (boundsMaxY - boundsMinY) + 1;
579
580 if (edgeBucketsLength > INITIAL_BUCKET_ARRAY) {
581 if (DO_STATS) {
582 rdrCtx.stats.stat_array_renderer_edgeBuckets
583 .add(edgeBucketsLength);
584 rdrCtx.stats.stat_array_renderer_edgeBucketCounts
585 .add(edgeBucketsLength);
586 }
587 edgeBuckets = edgeBuckets_ref.getArray(edgeBucketsLength);
588 edgeBucketCounts = edgeBucketCounts_ref.getArray(edgeBucketsLength);
589 }
590
591 edgeMinY = Integer.MAX_VALUE;
592 edgeMaxY = Integer.MIN_VALUE;
593 edgeMinX = Double.POSITIVE_INFINITY;
594 edgeMaxX = Double.NEGATIVE_INFINITY;
595
596 // reset used mark:
597 edgeCount = 0;
598 activeEdgeMaxUsed = 0;
599 edges.used = 0;
600
601 return this; // fluent API
602 }
603
604 /**
605 * Disposes this renderer and recycle it clean up before reusing this instance
606 */
607 void dispose() {
608 if (DO_STATS) {
609 rdrCtx.stats.stat_rdr_activeEdges.add(activeEdgeMaxUsed);
610 rdrCtx.stats.stat_rdr_edges.add(edges.used);
611 rdrCtx.stats.stat_rdr_edges_count.add(edges.used / SIZEOF_EDGE_BYTES);
612 rdrCtx.stats.hist_rdr_edges_count.add(edges.used / SIZEOF_EDGE_BYTES);
613 rdrCtx.stats.totalOffHeap += edges.length;
614 }
615 // Return arrays:
616 crossings = crossings_ref.putArray(crossings);
617 aux_crossings = aux_crossings_ref.putArray(aux_crossings);
618
619 edgePtrs = edgePtrs_ref.putArray(edgePtrs);
620 aux_edgePtrs = aux_edgePtrs_ref.putArray(aux_edgePtrs);
621
622 alphaLine = alphaLine_ref.putArray(alphaLine, 0, 0); // already zero filled
623 blkFlags = blkFlags_ref.putArray(blkFlags, 0, 0); // already zero filled
624
625 if (edgeMinY != Integer.MAX_VALUE) {
626 // if context is maked as DIRTY:
627 if (rdrCtx.dirty) {
628 // may happen if an exception if thrown in the pipeline processing:
629 // clear completely buckets arrays:
630 buckets_minY = 0;
631 buckets_maxY = boundsMaxY - boundsMinY;
632 }
633 // clear only used part
634 edgeBuckets = edgeBuckets_ref.putArray(edgeBuckets, buckets_minY,
635 buckets_maxY);
636 edgeBucketCounts = edgeBucketCounts_ref.putArray(edgeBucketCounts,
637 buckets_minY,
638 buckets_maxY + 1);
639 } else {
640 // unused arrays
641 edgeBuckets = edgeBuckets_ref.putArray(edgeBuckets, 0, 0);
642 edgeBucketCounts = edgeBucketCounts_ref.putArray(edgeBucketCounts, 0, 0);
643 }
644
645 // At last: resize back off-heap edges to initial size
646 if (edges.length != INITIAL_EDGES_CAPACITY) {
647 // note: may throw OOME:
648 edges.resize(INITIAL_EDGES_CAPACITY);
649 }
650 if (DO_CLEAN_DIRTY) {
651 // Force zero-fill dirty arrays:
652 edges.fill(BYTE_0);
653 }
654 if (DO_MONITORS) {
655 rdrCtx.stats.mon_rdr_endRendering.stop();
656 }
657 // recycle the RendererContext instance
658 DMarlinRenderingEngine.returnRendererContext(rdrCtx);
659 }
660
661 private static double tosubpixx(final double pix_x) {
662 return SUBPIXEL_SCALE_X * pix_x;
663 }
664
665 private static double tosubpixy(final double pix_y) {
666 // shift y by -0.5 for fast ceil(y - 0.5):
667 return SUBPIXEL_SCALE_Y * pix_y - 0.5d;
668 }
669
670 @Override
671 public void moveTo(double pix_x0, double pix_y0) {
672 closePath();
673 final double sx = tosubpixx(pix_x0);
674 final double sy = tosubpixy(pix_y0);
675 this.sx0 = sx;
676 this.sy0 = sy;
677 this.x0 = sx;
678 this.y0 = sy;
679 }
680
681 @Override
682 public void lineTo(double pix_x1, double pix_y1) {
683 final double x1 = tosubpixx(pix_x1);
684 final double y1 = tosubpixy(pix_y1);
685 addLine(x0, y0, x1, y1);
686 x0 = x1;
687 y0 = y1;
688 }
689
690 @Override
691 public void curveTo(double x1, double y1,
692 double x2, double y2,
693 double x3, double y3)
694 {
695 final double xe = tosubpixx(x3);
696 final double ye = tosubpixy(y3);
697 curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1),
698 tosubpixx(x2), tosubpixy(y2), xe, ye);
699 curveBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
700 x0 = xe;
701 y0 = ye;
702 }
703
704 @Override
705 public void quadTo(double x1, double y1, double x2, double y2) {
706 final double xe = tosubpixx(x2);
707 final double ye = tosubpixy(y2);
708 curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);
709 quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
710 x0 = xe;
711 y0 = ye;
712 }
713
714 @Override
715 public void closePath() {
716 addLine(x0, y0, sx0, sy0);
717 x0 = sx0;
718 y0 = sy0;
719 }
720
721 @Override
722 public void pathDone() {
723 closePath();
724 }
725
726 @Override
727 public long getNativeConsumer() {
728 throw new InternalError("Renderer does not use a native consumer.");
729 }
730
731 private void _endRendering(final int ymin, final int ymax) {
732 if (DISABLE_RENDER) {
733 return;
734 }
735
736 // Get X bounds as true pixel boundaries to compute correct pixel coverage:
737 final int bboxx0 = bbox_spminX;
738 final int bboxx1 = bbox_spmaxX;
739
740 final boolean windingRuleEvenOdd = (windingRule == WIND_EVEN_ODD);
741
742 // Useful when processing tile line by tile line
743 final int[] _alpha = alphaLine;
744
745 // local vars (performance):
746 final MarlinCache _cache = cache;
747 final OffHeapArray _edges = edges;
748 final int[] _edgeBuckets = edgeBuckets;
749 final int[] _edgeBucketCounts = edgeBucketCounts;
750
751 int[] _crossings = this.crossings;
752 int[] _edgePtrs = this.edgePtrs;
753
754 // merge sort auxiliary storage:
755 int[] _aux_crossings = this.aux_crossings;
756 int[] _aux_edgePtrs = this.aux_edgePtrs;
757
758 // copy constants:
759 final long _OFF_ERROR = OFF_ERROR;
760 final long _OFF_BUMP_X = OFF_BUMP_X;
761 final long _OFF_BUMP_ERR = OFF_BUMP_ERR;
762
763 final long _OFF_NEXT = OFF_NEXT;
764 final long _OFF_YMAX = OFF_YMAX;
765
766 final int _ALL_BUT_LSB = ALL_BUT_LSB;
767 final int _ERR_STEP_MAX = ERR_STEP_MAX;
768
769 // unsafe I/O:
770 final Unsafe _unsafe = OffHeapArray.UNSAFE;
771 final long addr0 = _edges.address;
772 long addr;
773 final int _SUBPIXEL_LG_POSITIONS_X = SUBPIXEL_LG_POSITIONS_X;
774 final int _SUBPIXEL_LG_POSITIONS_Y = SUBPIXEL_LG_POSITIONS_Y;
775 final int _SUBPIXEL_MASK_X = SUBPIXEL_MASK_X;
776 final int _SUBPIXEL_MASK_Y = SUBPIXEL_MASK_Y;
777 final int _SUBPIXEL_POSITIONS_X = SUBPIXEL_POSITIONS_X;
778
779 final int _MIN_VALUE = Integer.MIN_VALUE;
780 final int _MAX_VALUE = Integer.MAX_VALUE;
781
782 // Now we iterate through the scanlines. We must tell emitRow the coord
783 // of the first non-transparent pixel, so we must keep accumulators for
784 // the first and last pixels of the section of the current pixel row
785 // that we will emit.
786 // We also need to accumulate pix_bbox, but the iterator does it
787 // for us. We will just get the values from it once this loop is done
788 int minX = _MAX_VALUE;
789 int maxX = _MIN_VALUE;
790
791 int y = ymin;
792 int bucket = y - boundsMinY;
793
794 int numCrossings = this.edgeCount;
795 int edgePtrsLen = _edgePtrs.length;
796 int crossingsLen = _crossings.length;
797 int _arrayMaxUsed = activeEdgeMaxUsed;
798 int ptrLen = 0, newCount, ptrEnd;
799
800 int bucketcount, i, j, ecur;
801 int cross, lastCross;
802 int x0, x1, tmp, sum, prev, curx, curxo, crorientation, err;
803 int pix_x, pix_xmaxm1, pix_xmax;
804
805 int low, high, mid, prevNumCrossings;
806 boolean useBinarySearch;
807
808 final int[] _blkFlags = blkFlags;
809 final int _BLK_SIZE_LG = BLOCK_SIZE_LG;
810 final int _BLK_SIZE = BLOCK_SIZE;
811
812 final boolean _enableBlkFlagsHeuristics = ENABLE_BLOCK_FLAGS_HEURISTICS && this.enableBlkFlags;
813
814 // Use block flags if large pixel span and few crossings:
815 // ie mean(distance between crossings) is high
816 boolean useBlkFlags = this.prevUseBlkFlags;
817
818 final int stroking = rdrCtx.stroking;
819
820 int lastY = -1; // last emited row
821
822
823 // Iteration on scanlines
824 for (; y < ymax; y++, bucket++) {
825 // --- from former ScanLineIterator.next()
826 bucketcount = _edgeBucketCounts[bucket];
827
828 // marker on previously sorted edges:
829 prevNumCrossings = numCrossings;
830
831 // bucketCount indicates new edge / edge end:
832 if (bucketcount != 0) {
833 if (DO_STATS) {
834 rdrCtx.stats.stat_rdr_activeEdges_updates.add(numCrossings);
835 }
836
837 // last bit set to 1 means that edges ends
838 if ((bucketcount & 0x1) != 0) {
839 // eviction in active edge list
840 // cache edges[] address + offset
841 addr = addr0 + _OFF_YMAX;
842
843 for (i = 0, newCount = 0; i < numCrossings; i++) {
844 // get the pointer to the edge
845 ecur = _edgePtrs[i];
846 // random access so use unsafe:
847 if (_unsafe.getInt(addr + ecur) > y) {
848 _edgePtrs[newCount++] = ecur;
849 }
850 }
851 // update marker on sorted edges minus removed edges:
852 prevNumCrossings = numCrossings = newCount;
853 }
854
855 ptrLen = bucketcount >> 1; // number of new edge
856
857 if (ptrLen != 0) {
858 if (DO_STATS) {
859 rdrCtx.stats.stat_rdr_activeEdges_adds.add(ptrLen);
860 if (ptrLen > 10) {
861 rdrCtx.stats.stat_rdr_activeEdges_adds_high.add(ptrLen);
862 }
863 }
864 ptrEnd = numCrossings + ptrLen;
865
866 if (edgePtrsLen < ptrEnd) {
867 if (DO_STATS) {
868 rdrCtx.stats.stat_array_renderer_edgePtrs.add(ptrEnd);
869 }
870 this.edgePtrs = _edgePtrs
871 = edgePtrs_ref.widenArray(_edgePtrs, numCrossings,
872 ptrEnd);
873
874 edgePtrsLen = _edgePtrs.length;
875 // Get larger auxiliary storage:
876 aux_edgePtrs_ref.putArray(_aux_edgePtrs);
877
878 // use ArrayCache.getNewSize() to use the same growing
879 // factor than widenArray():
880 if (DO_STATS) {
881 rdrCtx.stats.stat_array_renderer_aux_edgePtrs.add(ptrEnd);
882 }
883 this.aux_edgePtrs = _aux_edgePtrs
884 = aux_edgePtrs_ref.getArray(
885 ArrayCacheConst.getNewSize(numCrossings, ptrEnd)
886 );
887 }
888
889 // cache edges[] address + offset
890 addr = addr0 + _OFF_NEXT;
891
892 // add new edges to active edge list:
893 for (ecur = _edgeBuckets[bucket];
894 numCrossings < ptrEnd; numCrossings++)
895 {
896 // store the pointer to the edge
897 _edgePtrs[numCrossings] = ecur;
898 // random access so use unsafe:
899 ecur = _unsafe.getInt(addr + ecur);
900 }
901
902 if (crossingsLen < numCrossings) {
903 // Get larger array:
904 crossings_ref.putArray(_crossings);
905
906 if (DO_STATS) {
907 rdrCtx.stats.stat_array_renderer_crossings
908 .add(numCrossings);
909 }
910 this.crossings = _crossings
911 = crossings_ref.getArray(numCrossings);
912
913 // Get larger auxiliary storage:
914 aux_crossings_ref.putArray(_aux_crossings);
915
916 if (DO_STATS) {
917 rdrCtx.stats.stat_array_renderer_aux_crossings
918 .add(numCrossings);
919 }
920 this.aux_crossings = _aux_crossings
921 = aux_crossings_ref.getArray(numCrossings);
922
923 crossingsLen = _crossings.length;
924 }
925 if (DO_STATS) {
926 // update max used mark
927 if (numCrossings > _arrayMaxUsed) {
928 _arrayMaxUsed = numCrossings;
929 }
930 }
931 } // ptrLen != 0
932 } // bucketCount != 0
933
934
935 if (numCrossings != 0) {
936 /*
937 * thresholds to switch to optimized merge sort
938 * for newly added edges + final merge pass.
939 */
940 if ((ptrLen < 10) || (numCrossings < 40)) {
941 if (DO_STATS) {
942 rdrCtx.stats.hist_rdr_crossings.add(numCrossings);
943 rdrCtx.stats.hist_rdr_crossings_adds.add(ptrLen);
944 }
945
946 /*
947 * threshold to use binary insertion sort instead of
948 * straight insertion sort (to reduce minimize comparisons).
949 */
950 useBinarySearch = (numCrossings >= 20);
951
952 // if small enough:
953 lastCross = _MIN_VALUE;
954
955 for (i = 0; i < numCrossings; i++) {
956 // get the pointer to the edge
957 ecur = _edgePtrs[i];
958
959 /* convert subpixel coordinates into pixel
960 positions for coming scanline */
961 /* note: it is faster to always update edges even
962 if it is removed from AEL for coming or last scanline */
963
964 // random access so use unsafe:
965 addr = addr0 + ecur; // ecur + OFF_F_CURX
966
967 // get current crossing:
968 curx = _unsafe.getInt(addr);
969
970 // update crossing with orientation at last bit:
971 cross = curx;
972
973 // Increment x using DDA (fixed point):
974 curx += _unsafe.getInt(addr + _OFF_BUMP_X);
975
976 // Increment error:
977 err = _unsafe.getInt(addr + _OFF_ERROR)
978 + _unsafe.getInt(addr + _OFF_BUMP_ERR);
979
980 // Manual carry handling:
981 // keep sign and carry bit only and ignore last bit (preserve orientation):
982 _unsafe.putInt(addr, curx - ((err >> 30) & _ALL_BUT_LSB));
983 _unsafe.putInt(addr + _OFF_ERROR, (err & _ERR_STEP_MAX));
984
985 if (DO_STATS) {
986 rdrCtx.stats.stat_rdr_crossings_updates.add(numCrossings);
987 }
988
989 // insertion sort of crossings:
990 if (cross < lastCross) {
991 if (DO_STATS) {
992 rdrCtx.stats.stat_rdr_crossings_sorts.add(i);
993 }
994
995 /* use binary search for newly added edges
996 in crossings if arrays are large enough */
997 if (useBinarySearch && (i >= prevNumCrossings)) {
998 if (DO_STATS) {
999 rdrCtx.stats.stat_rdr_crossings_bsearch.add(i);
1000 }
1001 low = 0;
1002 high = i - 1;
1003
1004 do {
1005 // note: use signed shift (not >>>) for performance
1006 // as indices are small enough to exceed Integer.MAX_VALUE
1007 mid = (low + high) >> 1;
1008
1009 if (_crossings[mid] < cross) {
1010 low = mid + 1;
1011 } else {
1012 high = mid - 1;
1013 }
1014 } while (low <= high);
1015
1016 for (j = i - 1; j >= low; j--) {
1017 _crossings[j + 1] = _crossings[j];
1018 _edgePtrs [j + 1] = _edgePtrs[j];
1019 }
1020 _crossings[low] = cross;
1021 _edgePtrs [low] = ecur;
1022
1023 } else {
1024 j = i - 1;
1025 _crossings[i] = _crossings[j];
1026 _edgePtrs[i] = _edgePtrs[j];
1027
1028 while ((--j >= 0) && (_crossings[j] > cross)) {
1029 _crossings[j + 1] = _crossings[j];
1030 _edgePtrs [j + 1] = _edgePtrs[j];
1031 }
1032 _crossings[j + 1] = cross;
1033 _edgePtrs [j + 1] = ecur;
1034 }
1035
1036 } else {
1037 _crossings[i] = lastCross = cross;
1038 }
1039 }
1040 } else {
1041 if (DO_STATS) {
1042 rdrCtx.stats.stat_rdr_crossings_msorts.add(numCrossings);
1043 rdrCtx.stats.hist_rdr_crossings_ratio
1044 .add((1000 * ptrLen) / numCrossings);
1045 rdrCtx.stats.hist_rdr_crossings_msorts.add(numCrossings);
1046 rdrCtx.stats.hist_rdr_crossings_msorts_adds.add(ptrLen);
1047 }
1048
1049 // Copy sorted data in auxiliary arrays
1050 // and perform insertion sort on almost sorted data
1051 // (ie i < prevNumCrossings):
1052
1053 lastCross = _MIN_VALUE;
1054
1055 for (i = 0; i < numCrossings; i++) {
1056 // get the pointer to the edge
1057 ecur = _edgePtrs[i];
1058
1059 /* convert subpixel coordinates into pixel
1060 positions for coming scanline */
1061 /* note: it is faster to always update edges even
1062 if it is removed from AEL for coming or last scanline */
1063
1064 // random access so use unsafe:
1065 addr = addr0 + ecur; // ecur + OFF_F_CURX
1066
1067 // get current crossing:
1068 curx = _unsafe.getInt(addr);
1069
1070 // update crossing with orientation at last bit:
1071 cross = curx;
1072
1073 // Increment x using DDA (fixed point):
1074 curx += _unsafe.getInt(addr + _OFF_BUMP_X);
1075
1076 // Increment error:
1077 err = _unsafe.getInt(addr + _OFF_ERROR)
1078 + _unsafe.getInt(addr + _OFF_BUMP_ERR);
1079
1080 // Manual carry handling:
1081 // keep sign and carry bit only and ignore last bit (preserve orientation):
1082 _unsafe.putInt(addr, curx - ((err >> 30) & _ALL_BUT_LSB));
1083 _unsafe.putInt(addr + _OFF_ERROR, (err & _ERR_STEP_MAX));
1084
1085 if (DO_STATS) {
1086 rdrCtx.stats.stat_rdr_crossings_updates.add(numCrossings);
1087 }
1088
1089 if (i >= prevNumCrossings) {
1090 // simply store crossing as edgePtrs is in-place:
1091 // will be copied and sorted efficiently by mergesort later:
1092 _crossings[i] = cross;
1093
1094 } else if (cross < lastCross) {
1095 if (DO_STATS) {
1096 rdrCtx.stats.stat_rdr_crossings_sorts.add(i);
1097 }
1098
1099 // (straight) insertion sort of crossings:
1100 j = i - 1;
1101 _aux_crossings[i] = _aux_crossings[j];
1102 _aux_edgePtrs[i] = _aux_edgePtrs[j];
1103
1104 while ((--j >= 0) && (_aux_crossings[j] > cross)) {
1105 _aux_crossings[j + 1] = _aux_crossings[j];
1106 _aux_edgePtrs [j + 1] = _aux_edgePtrs[j];
1107 }
1108 _aux_crossings[j + 1] = cross;
1109 _aux_edgePtrs [j + 1] = ecur;
1110
1111 } else {
1112 // auxiliary storage:
1113 _aux_crossings[i] = lastCross = cross;
1114 _aux_edgePtrs [i] = ecur;
1115 }
1116 }
1117
1118 // use Mergesort using auxiliary arrays (sort only right part)
1119 MergeSort.mergeSortNoCopy(_crossings, _edgePtrs,
1120 _aux_crossings, _aux_edgePtrs,
1121 numCrossings, prevNumCrossings);
1122 }
1123
1124 // reset ptrLen
1125 ptrLen = 0;
1126 // --- from former ScanLineIterator.next()
1127
1128
1129 /* note: bboxx0 and bboxx1 must be pixel boundaries
1130 to have correct coverage computation */
1131
1132 // right shift on crossings to get the x-coordinate:
1133 curxo = _crossings[0];
1134 x0 = curxo >> 1;
1135 if (x0 < minX) {
1136 minX = x0; // subpixel coordinate
1137 }
1138
1139 x1 = _crossings[numCrossings - 1] >> 1;
1140 if (x1 > maxX) {
1141 maxX = x1; // subpixel coordinate
1142 }
1143
1144
1145 // compute pixel coverages
1146 prev = curx = x0;
1147 // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
1148 // last bit contains orientation (0 or 1)
1149 crorientation = ((curxo & 0x1) << 1) - 1;
1150
1151 if (windingRuleEvenOdd) {
1152 sum = crorientation;
1153
1154 // Even Odd winding rule: take care of mask ie sum(orientations)
1155 for (i = 1; i < numCrossings; i++) {
1156 curxo = _crossings[i];
1157 curx = curxo >> 1;
1158 // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
1159 // last bit contains orientation (0 or 1)
1160 crorientation = ((curxo & 0x1) << 1) - 1;
1161
1162 if ((sum & 0x1) != 0) {
1163 // TODO: perform line clipping on left-right sides
1164 // to avoid such bound checks:
1165 x0 = (prev > bboxx0) ? prev : bboxx0;
1166
1167 if (curx < bboxx1) {
1168 x1 = curx;
1169 } else {
1170 x1 = bboxx1;
1171 // skip right side (fast exit loop):
1172 i = numCrossings;
1173 }
1174
1175 if (x0 < x1) {
1176 x0 -= bboxx0; // turn x0, x1 from coords to indices
1177 x1 -= bboxx0; // in the alpha array.
1178
1179 pix_x = x0 >> _SUBPIXEL_LG_POSITIONS_X;
1180 pix_xmaxm1 = (x1 - 1) >> _SUBPIXEL_LG_POSITIONS_X;
1181
1182 if (pix_x == pix_xmaxm1) {
1183 // Start and end in same pixel
1184 tmp = (x1 - x0); // number of subpixels
1185 _alpha[pix_x ] += tmp;
1186 _alpha[pix_x + 1] -= tmp;
1187
1188 if (useBlkFlags) {
1189 // flag used blocks:
1190 // note: block processing handles extra pixel:
1191 _blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
1192 }
1193 } else {
1194 tmp = (x0 & _SUBPIXEL_MASK_X);
1195 _alpha[pix_x ]
1196 += (_SUBPIXEL_POSITIONS_X - tmp);
1197 _alpha[pix_x + 1]
1198 += tmp;
1199
1200 pix_xmax = x1 >> _SUBPIXEL_LG_POSITIONS_X;
1201
1202 tmp = (x1 & _SUBPIXEL_MASK_X);
1203 _alpha[pix_xmax ]
1204 -= (_SUBPIXEL_POSITIONS_X - tmp);
1205 _alpha[pix_xmax + 1]
1206 -= tmp;
1207
1208 if (useBlkFlags) {
1209 // flag used blocks:
1210 // note: block processing handles extra pixel:
1211 _blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
1212 _blkFlags[pix_xmax >> _BLK_SIZE_LG] = 1;
1213 }
1214 }
1215 }
1216 }
1217
1218 sum += crorientation;
1219 prev = curx;
1220 }
1221 } else {
1222 // Non-zero winding rule: optimize that case (default)
1223 // and avoid processing intermediate crossings
1224 for (i = 1, sum = 0;; i++) {
1225 sum += crorientation;
1226
1227 if (sum != 0) {
1228 // prev = min(curx)
1229 if (prev > curx) {
1230 prev = curx;
1231 }
1232 } else {
1233 // TODO: perform line clipping on left-right sides
1234 // to avoid such bound checks:
1235 x0 = (prev > bboxx0) ? prev : bboxx0;
1236
1237 if (curx < bboxx1) {
1238 x1 = curx;
1239 } else {
1240 x1 = bboxx1;
1241 // skip right side (fast exit loop):
1242 i = numCrossings;
1243 }
1244
1245 if (x0 < x1) {
1246 x0 -= bboxx0; // turn x0, x1 from coords to indices
1247 x1 -= bboxx0; // in the alpha array.
1248
1249 pix_x = x0 >> _SUBPIXEL_LG_POSITIONS_X;
1250 pix_xmaxm1 = (x1 - 1) >> _SUBPIXEL_LG_POSITIONS_X;
1251
1252 if (pix_x == pix_xmaxm1) {
1253 // Start and end in same pixel
1254 tmp = (x1 - x0); // number of subpixels
1255 _alpha[pix_x ] += tmp;
1256 _alpha[pix_x + 1] -= tmp;
1257
1258 if (useBlkFlags) {
1259 // flag used blocks:
1260 // note: block processing handles extra pixel:
1261 _blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
1262 }
1263 } else {
1264 tmp = (x0 & _SUBPIXEL_MASK_X);
1265 _alpha[pix_x ]
1266 += (_SUBPIXEL_POSITIONS_X - tmp);
1267 _alpha[pix_x + 1]
1268 += tmp;
1269
1270 pix_xmax = x1 >> _SUBPIXEL_LG_POSITIONS_X;
1271
1272 tmp = (x1 & _SUBPIXEL_MASK_X);
1273 _alpha[pix_xmax ]
1274 -= (_SUBPIXEL_POSITIONS_X - tmp);
1275 _alpha[pix_xmax + 1]
1276 -= tmp;
1277
1278 if (useBlkFlags) {
1279 // flag used blocks:
1280 // note: block processing handles extra pixel:
1281 _blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
1282 _blkFlags[pix_xmax >> _BLK_SIZE_LG] = 1;
1283 }
1284 }
1285 }
1286 prev = _MAX_VALUE;
1287 }
1288
1289 if (i == numCrossings) {
1290 break;
1291 }
1292
1293 curxo = _crossings[i];
1294 curx = curxo >> 1;
1295 // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
1296 // last bit contains orientation (0 or 1)
1297 crorientation = ((curxo & 0x1) << 1) - 1;
1298 }
1299 }
1300 } // numCrossings > 0
1301
1302 // even if this last row had no crossings, alpha will be zeroed
1303 // from the last emitRow call. But this doesn't matter because
1304 // maxX < minX, so no row will be emitted to the MarlinCache.
1305 if ((y & _SUBPIXEL_MASK_Y) == _SUBPIXEL_MASK_Y) {
1306 lastY = y >> _SUBPIXEL_LG_POSITIONS_Y;
1307
1308 // convert subpixel to pixel coordinate within boundaries:
1309 minX = FloatMath.max(minX, bboxx0) >> _SUBPIXEL_LG_POSITIONS_X;
1310 maxX = FloatMath.min(maxX, bboxx1) >> _SUBPIXEL_LG_POSITIONS_X;
1311
1312 if (maxX >= minX) {
1313 // note: alpha array will be zeroed by copyAARow()
1314 // +1 because alpha [pix_minX; pix_maxX[
1315 // fix range [x0; x1[
1316 // note: if x1=bboxx1, then alpha is written up to bboxx1+1
1317 // inclusive: alpha[bboxx1] ignored, alpha[bboxx1+1] == 0
1318 // (normally so never cleared below)
1319 copyAARow(_alpha, lastY, minX, maxX + 1, useBlkFlags);
1320
1321 // speculative for next pixel row (scanline coherence):
1322 if (_enableBlkFlagsHeuristics) {
1323 // Use block flags if large pixel span and few crossings:
1324 // ie mean(distance between crossings) is larger than
1325 // 1 block size;
1326
1327 // fast check width:
1328 maxX -= minX;
1329
1330 // if stroking: numCrossings /= 2
1331 // => shift numCrossings by 1
1332 // condition = (width / (numCrossings - 1)) > blockSize
1333 useBlkFlags = (maxX > _BLK_SIZE) && (maxX >
1334 (((numCrossings >> stroking) - 1) << _BLK_SIZE_LG));
1335
1336 if (DO_STATS) {
1337 tmp = FloatMath.max(1,
1338 ((numCrossings >> stroking) - 1));
1339 rdrCtx.stats.hist_tile_generator_encoding_dist
1340 .add(maxX / tmp);
1341 }
1342 }
1343 } else {
1344 _cache.clearAARow(lastY);
1345 }
1346 minX = _MAX_VALUE;
1347 maxX = _MIN_VALUE;
1348 }
1349 } // scan line iterator
1350
1351 // Emit final row
1352 y--;
1353 y >>= _SUBPIXEL_LG_POSITIONS_Y;
1354
1355 // convert subpixel to pixel coordinate within boundaries:
1356 minX = FloatMath.max(minX, bboxx0) >> _SUBPIXEL_LG_POSITIONS_X;
1357 maxX = FloatMath.min(maxX, bboxx1) >> _SUBPIXEL_LG_POSITIONS_X;
1358
1359 if (maxX >= minX) {
1360 // note: alpha array will be zeroed by copyAARow()
1361 // +1 because alpha [pix_minX; pix_maxX[
1362 // fix range [x0; x1[
1363 // note: if x1=bboxx1, then alpha is written up to bboxx1+1
1364 // inclusive: alpha[bboxx1] ignored then cleared and
1365 // alpha[bboxx1+1] == 0 (normally so never cleared after)
1366 copyAARow(_alpha, y, minX, maxX + 1, useBlkFlags);
1367 } else if (y != lastY) {
1368 _cache.clearAARow(y);
1369 }
1370
1371 // update member:
1372 edgeCount = numCrossings;
1373 prevUseBlkFlags = useBlkFlags;
1374
1375 if (DO_STATS) {
1376 // update max used mark
1377 activeEdgeMaxUsed = _arrayMaxUsed;
1378 }
1379 }
1380
1381 boolean endRendering() {
1382 if (DO_MONITORS) {
1383 rdrCtx.stats.mon_rdr_endRendering.start();
1384 }
1385 if (edgeMinY == Integer.MAX_VALUE) {
1386 return false; // undefined edges bounds
1387 }
1388
1389 // bounds as half-open intervals
1390 final int spminX = FloatMath.max(FloatMath.ceil_int(edgeMinX - 0.5d), boundsMinX);
1391 final int spmaxX = FloatMath.min(FloatMath.ceil_int(edgeMaxX - 0.5d), boundsMaxX);
1392
1393 // edge Min/Max Y are already rounded to subpixels within bounds:
1394 final int spminY = edgeMinY;
1395 final int spmaxY = edgeMaxY;
1396
1397 buckets_minY = spminY - boundsMinY;
1398 buckets_maxY = spmaxY - boundsMinY;
1399
1400 if (DO_LOG_BOUNDS) {
1401 MarlinUtils.logInfo("edgesXY = [" + edgeMinX + " ... " + edgeMaxX
1402 + "[ [" + edgeMinY + " ... " + edgeMaxY + "[");
1403 MarlinUtils.logInfo("spXY = [" + spminX + " ... " + spmaxX
1404 + "[ [" + spminY + " ... " + spmaxY + "[");
1405 }
1406
1407 // test clipping for shapes out of bounds
1408 if ((spminX >= spmaxX) || (spminY >= spmaxY)) {
1409 return false;
1410 }
1411
1412 // half open intervals
1413 // inclusive:
1414 final int pminX = spminX >> SUBPIXEL_LG_POSITIONS_X;
1415 // exclusive:
1416 final int pmaxX = (spmaxX + SUBPIXEL_MASK_X) >> SUBPIXEL_LG_POSITIONS_X;
1417 // inclusive:
1418 final int pminY = spminY >> SUBPIXEL_LG_POSITIONS_Y;
1419 // exclusive:
1420 final int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >> SUBPIXEL_LG_POSITIONS_Y;
1421
1422 // store BBox to answer ptg.getBBox():
1423 this.cache.init(pminX, pminY, pmaxX, pmaxY);
1424
1425 // Heuristics for using block flags:
1426 if (ENABLE_BLOCK_FLAGS) {
1427 enableBlkFlags = this.cache.useRLE;
1428 prevUseBlkFlags = enableBlkFlags && !ENABLE_BLOCK_FLAGS_HEURISTICS;
1429
1430 if (enableBlkFlags) {
1431 // ensure blockFlags array is large enough:
1432 // note: +2 to ensure enough space left at end
1433 final int blkLen = ((pmaxX - pminX) >> BLOCK_SIZE_LG) + 2;
1434 if (blkLen > INITIAL_ARRAY) {
1435 blkFlags = blkFlags_ref.getArray(blkLen);
1436 }
1437 }
1438 }
1439
1440 // memorize the rendering bounding box:
1441 /* note: bbox_spminX and bbox_spmaxX must be pixel boundaries
1442 to have correct coverage computation */
1443 // inclusive:
1444 bbox_spminX = pminX << SUBPIXEL_LG_POSITIONS_X;
1445 // exclusive:
1446 bbox_spmaxX = pmaxX << SUBPIXEL_LG_POSITIONS_X;
1447 // inclusive:
1448 bbox_spminY = spminY;
1449 // exclusive:
1450 bbox_spmaxY = spmaxY;
1451
1452 if (DO_LOG_BOUNDS) {
1453 MarlinUtils.logInfo("pXY = [" + pminX + " ... " + pmaxX
1454 + "[ [" + pminY + " ... " + pmaxY + "[");
1455 MarlinUtils.logInfo("bbox_spXY = [" + bbox_spminX + " ... "
1456 + bbox_spmaxX + "[ [" + bbox_spminY + " ... "
1457 + bbox_spmaxY + "[");
1458 }
1459
1460 // Prepare alpha line:
1461 // add 2 to better deal with the last pixel in a pixel row.
1462 final int width = (pmaxX - pminX) + 2;
1463
1464 // Useful when processing tile line by tile line
1465 if (width > INITIAL_AA_ARRAY) {
1466 if (DO_STATS) {
1467 rdrCtx.stats.stat_array_renderer_alphaline.add(width);
1468 }
1469 alphaLine = alphaLine_ref.getArray(width);
1470 }
1471
1472 // process first tile line:
1473 endRendering(pminY);
1474
1475 return true;
1476 }
1477
1478 private int bbox_spminX, bbox_spmaxX, bbox_spminY, bbox_spmaxY;
1479
1480 void endRendering(final int pminY) {
1481 if (DO_MONITORS) {
1482 rdrCtx.stats.mon_rdr_endRendering_Y.start();
1483 }
1484
1485 final int spminY = pminY << SUBPIXEL_LG_POSITIONS_Y;
1486 final int fixed_spminY = FloatMath.max(bbox_spminY, spminY);
1487
1488 // avoid rendering for last call to nextTile()
1489 if (fixed_spminY < bbox_spmaxY) {
1490 // process a complete tile line ie scanlines for 32 rows
1491 final int spmaxY = FloatMath.min(bbox_spmaxY, spminY + SUBPIXEL_TILE);
1492
1493 // process tile line [0 - 32]
1494 cache.resetTileLine(pminY);
1495
1496 // Process only one tile line:
1497 _endRendering(fixed_spminY, spmaxY);
1498 }
1499 if (DO_MONITORS) {
1500 rdrCtx.stats.mon_rdr_endRendering_Y.stop();
1501 }
1502 }
1503
1504 void copyAARow(final int[] alphaRow,
1505 final int pix_y, final int pix_from, final int pix_to,
1506 final boolean useBlockFlags)
1507 {
1508 if (DO_MONITORS) {
1509 rdrCtx.stats.mon_rdr_copyAARow.start();
1510 }
1511 if (useBlockFlags) {
1512 if (DO_STATS) {
1513 rdrCtx.stats.hist_tile_generator_encoding.add(1);
1514 }
1515 cache.copyAARowRLE_WithBlockFlags(blkFlags, alphaRow, pix_y, pix_from, pix_to);
1516 } else {
1517 if (DO_STATS) {
1518 rdrCtx.stats.hist_tile_generator_encoding.add(0);
1519 }
1520 cache.copyAARowNoRLE(alphaRow, pix_y, pix_from, pix_to);
1521 }
1522 if (DO_MONITORS) {
1523 rdrCtx.stats.mon_rdr_copyAARow.stop();
1524 }
1525 }
1526}