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