Path: blob/master/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java
41159 views
/*1* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.marlin;2627import static sun.java2d.marlin.OffHeapArray.SIZE_INT;28import jdk.internal.misc.Unsafe;2930final class Renderer implements DPathConsumer2D, MarlinConst {3132static final boolean DISABLE_RENDER = false;3334static final boolean ENABLE_BLOCK_FLAGS = MarlinProperties.isUseTileFlags();35static final boolean ENABLE_BLOCK_FLAGS_HEURISTICS = MarlinProperties.isUseTileFlagsWithHeuristics();3637private static final int ALL_BUT_LSB = 0xFFFFFFFE;38private static final int ERR_STEP_MAX = 0x7FFFFFFF; // = 2^31 - 13940private static final double POWER_2_TO_32 = 0x1.0p32d;4142// use double to make tosubpix methods faster (no int to double conversion)43static final double SUBPIXEL_SCALE_X = SUBPIXEL_POSITIONS_X;44static final double SUBPIXEL_SCALE_Y = SUBPIXEL_POSITIONS_Y;45static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1;46static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1;4748static final double RDR_OFFSET_X = 0.5d / SUBPIXEL_SCALE_X;49static final double RDR_OFFSET_Y = 0.5d / SUBPIXEL_SCALE_Y;5051// number of subpixels corresponding to a tile line52private static final int SUBPIXEL_TILE53= TILE_H << SUBPIXEL_LG_POSITIONS_Y;5455// 2176 pixels (height) x 8 subpixels = 68K56static final int INITIAL_BUCKET_ARRAY57= INITIAL_PIXEL_HEIGHT * SUBPIXEL_POSITIONS_Y;5859// crossing capacity = edges count / 4 ~ 102460static final int INITIAL_CROSSING_COUNT = INITIAL_EDGES_COUNT >> 2;6162// common to all types of input path segments.63// OFFSET as bytes64// only integer values:65public static final long OFF_CURX_OR = 0;66public static final long OFF_ERROR = OFF_CURX_OR + SIZE_INT;67public static final long OFF_BUMP_X = OFF_ERROR + SIZE_INT;68public static final long OFF_BUMP_ERR = OFF_BUMP_X + SIZE_INT;69public static final long OFF_NEXT = OFF_BUMP_ERR + SIZE_INT;70public static final long OFF_YMAX = OFF_NEXT + SIZE_INT;7172// size of one edge in bytes73public static final int SIZEOF_EDGE_BYTES = (int)(OFF_YMAX + SIZE_INT);7475// curve break into lines76// cubic error in subpixels to decrement step77private static final double CUB_DEC_ERR_SUBPIX78= MarlinProperties.getCubicDecD2() * (SUBPIXEL_POSITIONS_X / 8.0d); // 1.0 / 8th pixel79// cubic error in subpixels to increment step80private static final double CUB_INC_ERR_SUBPIX81= MarlinProperties.getCubicIncD1() * (SUBPIXEL_POSITIONS_X / 8.0d); // 0.4 / 8th pixel82// scale factor for Y-axis contribution to quad / cubic errors:83public static final double SCALE_DY = ((double) SUBPIXEL_POSITIONS_X) / SUBPIXEL_POSITIONS_Y;8485// TestNonAARasterization (JDK-8170879): cubics86// bad paths (59294/100000 == 59,29%, 94335 bad pixels (avg = 1,59), 3966 warnings (avg = 0,07)87// 201888// 1.0 / 0.2: bad paths (67194/100000 == 67,19%, 117394 bad pixels (avg = 1,75 - max = 9), 4042 warnings (avg = 0,06)8990// cubic bind length to decrement step91public static final double CUB_DEC_BND92= 8.0d * CUB_DEC_ERR_SUBPIX;93// cubic bind length to increment step94public static final double CUB_INC_BND95= 8.0d * CUB_INC_ERR_SUBPIX;9697// cubic countlg98public static final int CUB_COUNT_LG = 2;99// cubic count = 2^countlg100private static final int CUB_COUNT = 1 << CUB_COUNT_LG;101// cubic count^2 = 4^countlg102private static final int CUB_COUNT_2 = 1 << (2 * CUB_COUNT_LG);103// cubic count^3 = 8^countlg104private static final int CUB_COUNT_3 = 1 << (3 * CUB_COUNT_LG);105// cubic dt = 1 / count106private static final double CUB_INV_COUNT = 1.0d / CUB_COUNT;107// cubic dt^2 = 1 / count^2 = 1 / 4^countlg108private static final double CUB_INV_COUNT_2 = 1.0d / CUB_COUNT_2;109// cubic dt^3 = 1 / count^3 = 1 / 8^countlg110private static final double CUB_INV_COUNT_3 = 1.0d / CUB_COUNT_3;111112// quad break into lines113// quadratic error in subpixels114private static final double QUAD_DEC_ERR_SUBPIX115= MarlinProperties.getQuadDecD2() * (SUBPIXEL_POSITIONS_X / 8.0d); // 0.5 / 8th pixel116117// TestNonAARasterization (JDK-8170879): quads118// bad paths (62916/100000 == 62,92%, 103818 bad pixels (avg = 1,65), 6514 warnings (avg = 0,10)119// 2018120// 0.50px = bad paths (62915/100000 == 62,92%, 103810 bad pixels (avg = 1,65), 6512 warnings (avg = 0,10)121122// quadratic bind length to decrement step123public static final double QUAD_DEC_BND124= 8.0d * QUAD_DEC_ERR_SUBPIX;125126//////////////////////////////////////////////////////////////////////////////127// SCAN LINE128//////////////////////////////////////////////////////////////////////////////129// crossings ie subpixel edge x coordinates130private int[] crossings;131// auxiliary storage for crossings (merge sort)132private int[] aux_crossings;133134// indices into the segment pointer lists. They indicate the "active"135// sublist in the segment lists (the portion of the list that contains136// all the segments that cross the next scan line).137private int edgeCount;138private int[] edgePtrs;139// auxiliary storage for edge pointers (merge sort)140private int[] aux_edgePtrs;141142// max used for both edgePtrs and crossings (stats only)143private int activeEdgeMaxUsed;144145// crossings ref (dirty)146private final IntArrayCache.Reference crossings_ref;147// edgePtrs ref (dirty)148private final IntArrayCache.Reference edgePtrs_ref;149// merge sort initial arrays (large enough to satisfy most usages) (1024)150// aux_crossings ref (dirty)151private final IntArrayCache.Reference aux_crossings_ref;152// aux_edgePtrs ref (dirty)153private final IntArrayCache.Reference aux_edgePtrs_ref;154155//////////////////////////////////////////////////////////////////////////////156// EDGE LIST157//////////////////////////////////////////////////////////////////////////////158private int edgeMinY = Integer.MAX_VALUE;159private int edgeMaxY = Integer.MIN_VALUE;160private double edgeMinX = Double.POSITIVE_INFINITY;161private double edgeMaxX = Double.NEGATIVE_INFINITY;162163// edges [ints] stored in off-heap memory164private final OffHeapArray edges;165166private int[] edgeBuckets;167private int[] edgeBucketCounts; // 2*newedges + (1 if pruning needed)168// used range for edgeBuckets / edgeBucketCounts169private int buckets_minY;170private int buckets_maxY;171172// edgeBuckets ref (clean)173private final IntArrayCache.Reference edgeBuckets_ref;174// edgeBucketCounts ref (clean)175private final IntArrayCache.Reference edgeBucketCounts_ref;176177// Flattens using adaptive forward differencing. This only carries out178// one iteration of the AFD loop. All it does is update AFD variables (i.e.179// X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings).180private void quadBreakIntoLinesAndAdd(double x0, double y0,181final Curve c,182final double x2, final double y2)183{184int count = 1; // dt = 1 / count185186// maximum(ddX|Y) = norm(dbx, dby) * dt^2 (= 1)187double maxDD = Math.abs(c.dbx) + Math.abs(c.dby) * SCALE_DY;188189final double _DEC_BND = QUAD_DEC_BND;190191while (maxDD >= _DEC_BND) {192// divide step by half:193maxDD /= 4.0d; // error divided by 2^2 = 4194195count <<= 1;196if (DO_STATS) {197rdrCtx.stats.stat_rdr_quadBreak_dec.add(count);198}199}200201final int nL = count; // line count202203if (count > 1) {204final double icount = 1.0d / count; // dt205final double icount2 = icount * icount; // dt^2206207final double ddx = c.dbx * icount2;208final double ddy = c.dby * icount2;209double dx = c.bx * icount2 + c.cx * icount;210double dy = c.by * icount2 + c.cy * icount;211212// we use x0, y0 to walk the line213for (double x1 = x0, y1 = y0; --count > 0; dx += ddx, dy += ddy) {214x1 += dx;215y1 += dy;216217addLine(x0, y0, x1, y1);218x0 = x1;219y0 = y1;220}221}222addLine(x0, y0, x2, y2);223224if (DO_STATS) {225rdrCtx.stats.stat_rdr_quadBreak.add(nL);226}227}228229// x0, y0 and x3,y3 are the endpoints of the curve. We could compute these230// using c.xat(0),c.yat(0) and c.xat(1),c.yat(1), but this might introduce231// numerical errors, and our callers already have the exact values.232// Another alternative would be to pass all the control points, and call233// c.set here, but then too many numbers are passed around.234private void curveBreakIntoLinesAndAdd(double x0, double y0,235final Curve c,236final double x3, final double y3)237{238int count = CUB_COUNT;239final double icount = CUB_INV_COUNT; // dt240final double icount2 = CUB_INV_COUNT_2; // dt^2241final double icount3 = CUB_INV_COUNT_3; // dt^3242243// the dx and dy refer to forward differencing variables, not the last244// coefficients of the "points" polynomial245double dddx, dddy, ddx, ddy, dx, dy;246dddx = 2.0d * c.dax * icount3;247dddy = 2.0d * c.day * icount3;248ddx = dddx + c.dbx * icount2;249ddy = dddy + c.dby * icount2;250dx = c.ax * icount3 + c.bx * icount2 + c.cx * icount;251dy = c.ay * icount3 + c.by * icount2 + c.cy * icount;252253int nL = 0; // line count254255final double _DEC_BND = CUB_DEC_BND;256final double _INC_BND = CUB_INC_BND;257final double _SCALE_DY = SCALE_DY;258259// we use x0, y0 to walk the line260for (double x1 = x0, y1 = y0; count > 0; ) {261// inc / dec => ratio ~ 5 to minimize upscale / downscale but minimize edges262263// double step:264// can only do this on even "count" values, because we must divide count by 2265while ((count % 2 == 0)266&& ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) <= _INC_BND)) {267dx = 2.0d * dx + ddx;268dy = 2.0d * dy + ddy;269ddx = 4.0d * (ddx + dddx);270ddy = 4.0d * (ddy + dddy);271dddx *= 8.0d;272dddy *= 8.0d;273274count >>= 1;275if (DO_STATS) {276rdrCtx.stats.stat_rdr_curveBreak_inc.add(count);277}278}279280// divide step by half:281while ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) >= _DEC_BND) {282dddx /= 8.0d;283dddy /= 8.0d;284ddx = ddx / 4.0d - dddx;285ddy = ddy / 4.0d - dddy;286dx = (dx - ddx) / 2.0d;287dy = (dy - ddy) / 2.0d;288289count <<= 1;290if (DO_STATS) {291rdrCtx.stats.stat_rdr_curveBreak_dec.add(count);292}293}294if (--count == 0) {295break;296}297298x1 += dx;299y1 += dy;300dx += ddx;301dy += ddy;302ddx += dddx;303ddy += dddy;304305addLine(x0, y0, x1, y1);306x0 = x1;307y0 = y1;308}309addLine(x0, y0, x3, y3);310311if (DO_STATS) {312rdrCtx.stats.stat_rdr_curveBreak.add(nL + 1);313}314}315316private void addLine(double x1, double y1, double x2, double y2) {317if (DO_MONITORS) {318rdrCtx.stats.mon_rdr_addLine.start();319}320if (DO_STATS) {321rdrCtx.stats.stat_rdr_addLine.add(1);322}323int or = 1; // orientation of the line. 1 if y increases, 0 otherwise.324if (y2 < y1) {325or = 0;326double tmp = y2;327y2 = y1;328y1 = tmp;329tmp = x2;330x2 = x1;331x1 = tmp;332}333334// convert subpixel coordinates [double] into pixel positions [int]335336// The index of the pixel that holds the next HPC is at ceil(trueY - 0.5)337// Since y1 and y2 are biased by -0.5 in tosubpixy(), this is simply338// ceil(y1) or ceil(y2)339// upper integer (inclusive)340final int firstCrossing = FloatMath.max(FloatMath.ceil_int(y1), boundsMinY);341342// note: use boundsMaxY (last Y exclusive) to compute correct coverage343// upper integer (exclusive)344final int lastCrossing = FloatMath.min(FloatMath.ceil_int(y2), boundsMaxY);345346/* skip horizontal lines in pixel space and clip edges347out of y range [boundsMinY; boundsMaxY] */348if (firstCrossing >= lastCrossing) {349if (DO_MONITORS) {350rdrCtx.stats.mon_rdr_addLine.stop();351}352if (DO_STATS) {353rdrCtx.stats.stat_rdr_addLine_skip.add(1);354}355return;356}357358// edge min/max X/Y are in subpixel space (half-open interval):359// note: Use integer crossings to ensure consistent range within360// edgeBuckets / edgeBucketCounts arrays in case of NaN values (int = 0)361if (firstCrossing < edgeMinY) {362edgeMinY = firstCrossing;363}364if (lastCrossing > edgeMaxY) {365edgeMaxY = lastCrossing;366}367368final double slope = (x1 - x2) / (y1 - y2);369370if (slope >= 0.0d) { // <==> x1 < x2371if (x1 < edgeMinX) {372edgeMinX = x1;373}374if (x2 > edgeMaxX) {375edgeMaxX = x2;376}377} else {378if (x2 < edgeMinX) {379edgeMinX = x2;380}381if (x1 > edgeMaxX) {382edgeMaxX = x1;383}384}385386// local variables for performance:387final int _SIZEOF_EDGE_BYTES = SIZEOF_EDGE_BYTES;388389final OffHeapArray _edges = edges;390391// get free pointer (ie length in bytes)392final int edgePtr = _edges.used;393394// use substraction to avoid integer overflow:395if (_edges.length - edgePtr < _SIZEOF_EDGE_BYTES) {396// suppose _edges.length > _SIZEOF_EDGE_BYTES397// so doubling size is enough to add needed bytes398// note: throw IOOB if neededSize > 2Gb:399final long edgeNewSize = ArrayCacheConst.getNewLargeSize(400_edges.length,401edgePtr + _SIZEOF_EDGE_BYTES);402403if (DO_STATS) {404rdrCtx.stats.stat_rdr_edges_resizes.add(edgeNewSize);405}406_edges.resize(edgeNewSize);407}408409410final Unsafe _unsafe = OffHeapArray.UNSAFE;411final long SIZE_INT = 4L;412long addr = _edges.address + edgePtr;413414// The x value must be bumped up to its position at the next HPC we will evaluate.415// "firstcrossing" is the (sub)pixel number where the next crossing occurs416// thus, the actual coordinate of the next HPC is "firstcrossing + 0.5"417// so the Y distance we cover is "firstcrossing + 0.5 - trueY".418// Note that since y1 (and y2) are already biased by -0.5 in tosubpixy(), we have419// y1 = trueY - 0.5420// trueY = y1 + 0.5421// firstcrossing + 0.5 - trueY = firstcrossing + 0.5 - (y1 + 0.5)422// = firstcrossing - y1423// The x coordinate at that HPC is then:424// x1_intercept = x1 + (firstcrossing - y1) * slope425// The next VPC is then given by:426// VPC index = ceil(x1_intercept - 0.5), or alternately427// VPC index = floor(x1_intercept - 0.5 + 1 - epsilon)428// epsilon is hard to pin down in floating point, but easy in fixed point, so if429// we convert to fixed point then these operations get easier:430// long x1_fixed = x1_intercept * 2^32; (fixed point 32.32 format)431// curx = next VPC = fixed_floor(x1_fixed - 2^31 + 2^32 - 1)432// = fixed_floor(x1_fixed + 2^31 - 1)433// = fixed_floor(x1_fixed + 0x7FFFFFFF)434// and error = fixed_fract(x1_fixed + 0x7FFFFFFF)435final double x1_intercept = x1 + (firstCrossing - y1) * slope;436437// inlined scalb(x1_intercept, 32):438final long x1_fixed_biased = ((long) (POWER_2_TO_32 * x1_intercept))439+ 0x7FFFFFFFL;440// curx:441// last bit corresponds to the orientation442_unsafe.putInt(addr, (((int) (x1_fixed_biased >> 31L)) & ALL_BUT_LSB) | or);443addr += SIZE_INT;444_unsafe.putInt(addr, ((int) x1_fixed_biased) >>> 1);445addr += SIZE_INT;446447// inlined scalb(slope, 32):448final long slope_fixed = (long) (POWER_2_TO_32 * slope);449450// last bit set to 0 to keep orientation:451_unsafe.putInt(addr, (((int) (slope_fixed >> 31L)) & ALL_BUT_LSB));452addr += SIZE_INT;453_unsafe.putInt(addr, ((int) slope_fixed) >>> 1);454addr += SIZE_INT;455456final int[] _edgeBuckets = edgeBuckets;457final int[] _edgeBucketCounts = edgeBucketCounts;458459final int _boundsMinY = boundsMinY;460461// each bucket is a linked list. this method adds ptr to the462// start of the "bucket"th linked list.463final int bucketIdx = firstCrossing - _boundsMinY;464465// pointer from bucket466_unsafe.putInt(addr, _edgeBuckets[bucketIdx]);467addr += SIZE_INT;468// y max (exclusive)469_unsafe.putInt(addr, lastCrossing);470471// Update buckets:472// directly the edge struct "pointer"473_edgeBuckets[bucketIdx] = edgePtr;474_edgeBucketCounts[bucketIdx] += 2; // 1 << 1475// last bit means edge end476_edgeBucketCounts[lastCrossing - _boundsMinY] |= 0x1;477478// update free pointer (ie length in bytes)479_edges.used += _SIZEOF_EDGE_BYTES;480481if (DO_MONITORS) {482rdrCtx.stats.mon_rdr_addLine.stop();483}484}485486// END EDGE LIST487//////////////////////////////////////////////////////////////////////////////488489// Cache to store RLE-encoded coverage mask of the current primitive490final MarlinCache cache;491492// Bounds of the drawing region, at subpixel precision.493private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;494495// Current winding rule496private int windingRule;497498// Current drawing position, i.e., final point of last segment499private double x0, y0;500501// Position of most recent 'moveTo' command502private double sx0, sy0;503504// per-thread renderer context505final RendererContext rdrCtx;506// dirty curve507private final Curve curve;508509// clean alpha array (zero filled)510private int[] alphaLine;511512// alphaLine ref (clean)513private final IntArrayCache.Reference alphaLine_ref;514515private boolean enableBlkFlags = false;516private boolean prevUseBlkFlags = false;517518/* block flags (0|1) */519private int[] blkFlags;520521// blkFlags ref (clean)522private final IntArrayCache.Reference blkFlags_ref;523524Renderer(final RendererContext rdrCtx) {525this.rdrCtx = rdrCtx;526this.curve = rdrCtx.curve;527this.cache = rdrCtx.cache;528529this.edges = rdrCtx.newOffHeapArray(INITIAL_EDGES_CAPACITY); // 96K530531edgeBuckets_ref = rdrCtx.newCleanIntArrayRef(INITIAL_BUCKET_ARRAY); // 64K532edgeBucketCounts_ref = rdrCtx.newCleanIntArrayRef(INITIAL_BUCKET_ARRAY); // 64K533534edgeBuckets = edgeBuckets_ref.initial;535edgeBucketCounts = edgeBucketCounts_ref.initial;536537// 4096 pixels large538alphaLine_ref = rdrCtx.newCleanIntArrayRef(INITIAL_AA_ARRAY); // 16K539alphaLine = alphaLine_ref.initial;540541crossings_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K542aux_crossings_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K543edgePtrs_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K544aux_edgePtrs_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K545546crossings = crossings_ref.initial;547aux_crossings = aux_crossings_ref.initial;548edgePtrs = edgePtrs_ref.initial;549aux_edgePtrs = aux_edgePtrs_ref.initial;550551blkFlags_ref = rdrCtx.newCleanIntArrayRef(INITIAL_ARRAY); // 1K = 1 tile line552blkFlags = blkFlags_ref.initial;553}554555Renderer init(final int pix_boundsX, final int pix_boundsY,556final int pix_boundsWidth, final int pix_boundsHeight,557final int windingRule)558{559this.windingRule = windingRule;560561// bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY562this.boundsMinX = pix_boundsX << SUBPIXEL_LG_POSITIONS_X;563this.boundsMaxX =564(pix_boundsX + pix_boundsWidth) << SUBPIXEL_LG_POSITIONS_X;565this.boundsMinY = pix_boundsY << SUBPIXEL_LG_POSITIONS_Y;566this.boundsMaxY =567(pix_boundsY + pix_boundsHeight) << SUBPIXEL_LG_POSITIONS_Y;568569if (DO_LOG_BOUNDS) {570MarlinUtils.logInfo("boundsXY = [" + boundsMinX + " ... "571+ boundsMaxX + "[ [" + boundsMinY + " ... "572+ boundsMaxY + "[");573}574575// see addLine: ceil(boundsMaxY) => boundsMaxY + 1576// +1 for edgeBucketCounts577final int edgeBucketsLength = (boundsMaxY - boundsMinY) + 1;578579if (edgeBucketsLength > INITIAL_BUCKET_ARRAY) {580if (DO_STATS) {581rdrCtx.stats.stat_array_renderer_edgeBuckets582.add(edgeBucketsLength);583rdrCtx.stats.stat_array_renderer_edgeBucketCounts584.add(edgeBucketsLength);585}586edgeBuckets = edgeBuckets_ref.getArray(edgeBucketsLength);587edgeBucketCounts = edgeBucketCounts_ref.getArray(edgeBucketsLength);588}589590edgeMinY = Integer.MAX_VALUE;591edgeMaxY = Integer.MIN_VALUE;592edgeMinX = Double.POSITIVE_INFINITY;593edgeMaxX = Double.NEGATIVE_INFINITY;594595// reset used mark:596edgeCount = 0;597activeEdgeMaxUsed = 0;598edges.used = 0;599600return this; // fluent API601}602603/**604* Disposes this renderer and recycle it clean up before reusing this instance605*/606void dispose() {607if (DO_STATS) {608rdrCtx.stats.stat_rdr_activeEdges.add(activeEdgeMaxUsed);609rdrCtx.stats.stat_rdr_edges.add(edges.used);610rdrCtx.stats.stat_rdr_edges_count.add(edges.used / SIZEOF_EDGE_BYTES);611rdrCtx.stats.hist_rdr_edges_count.add(edges.used / SIZEOF_EDGE_BYTES);612rdrCtx.stats.totalOffHeap += edges.length;613}614// Return arrays:615crossings = crossings_ref.putArray(crossings);616aux_crossings = aux_crossings_ref.putArray(aux_crossings);617618edgePtrs = edgePtrs_ref.putArray(edgePtrs);619aux_edgePtrs = aux_edgePtrs_ref.putArray(aux_edgePtrs);620621alphaLine = alphaLine_ref.putArray(alphaLine, 0, 0); // already zero filled622blkFlags = blkFlags_ref.putArray(blkFlags, 0, 0); // already zero filled623624if (edgeMinY != Integer.MAX_VALUE) {625// if context is maked as DIRTY:626if (rdrCtx.dirty) {627// may happen if an exception if thrown in the pipeline processing:628// clear completely buckets arrays:629buckets_minY = 0;630buckets_maxY = boundsMaxY - boundsMinY;631}632// clear only used part633edgeBuckets = edgeBuckets_ref.putArray(edgeBuckets, buckets_minY,634buckets_maxY);635edgeBucketCounts = edgeBucketCounts_ref.putArray(edgeBucketCounts,636buckets_minY,637buckets_maxY + 1);638} else {639// unused arrays640edgeBuckets = edgeBuckets_ref.putArray(edgeBuckets, 0, 0);641edgeBucketCounts = edgeBucketCounts_ref.putArray(edgeBucketCounts, 0, 0);642}643644// At last: resize back off-heap edges to initial size645if (edges.length != INITIAL_EDGES_CAPACITY) {646// note: may throw OOME:647edges.resize(INITIAL_EDGES_CAPACITY);648}649if (DO_CLEAN_DIRTY) {650// Force zero-fill dirty arrays:651edges.fill(BYTE_0);652}653if (DO_MONITORS) {654rdrCtx.stats.mon_rdr_endRendering.stop();655}656// recycle the RendererContext instance657DMarlinRenderingEngine.returnRendererContext(rdrCtx);658}659660private static double tosubpixx(final double pix_x) {661return SUBPIXEL_SCALE_X * pix_x;662}663664private static double tosubpixy(final double pix_y) {665// shift y by -0.5 for fast ceil(y - 0.5):666return SUBPIXEL_SCALE_Y * pix_y - 0.5d;667}668669@Override670public void moveTo(final double pix_x0, final double pix_y0) {671closePath();672final double sx = tosubpixx(pix_x0);673final double sy = tosubpixy(pix_y0);674this.sx0 = sx;675this.sy0 = sy;676this.x0 = sx;677this.y0 = sy;678}679680@Override681public void lineTo(final double pix_x1, final double pix_y1) {682final double x1 = tosubpixx(pix_x1);683final double y1 = tosubpixy(pix_y1);684addLine(x0, y0, x1, y1);685x0 = x1;686y0 = y1;687}688689@Override690public void curveTo(final double pix_x1, final double pix_y1,691final double pix_x2, final double pix_y2,692final double pix_x3, final double pix_y3)693{694final double xe = tosubpixx(pix_x3);695final double ye = tosubpixy(pix_y3);696curve.set(x0, y0,697tosubpixx(pix_x1), tosubpixy(pix_y1),698tosubpixx(pix_x2), tosubpixy(pix_y2),699xe, ye);700curveBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);701x0 = xe;702y0 = ye;703}704705@Override706public void quadTo(final double pix_x1, final double pix_y1,707final double pix_x2, final double pix_y2)708{709final double xe = tosubpixx(pix_x2);710final double ye = tosubpixy(pix_y2);711curve.set(x0, y0,712tosubpixx(pix_x1), tosubpixy(pix_y1),713xe, ye);714quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);715x0 = xe;716y0 = ye;717}718719@Override720public void closePath() {721if (x0 != sx0 || y0 != sy0) {722addLine(x0, y0, sx0, sy0);723x0 = sx0;724y0 = sy0;725}726}727728@Override729public void pathDone() {730closePath();731}732733@Override734public long getNativeConsumer() {735throw new InternalError("Renderer does not use a native consumer.");736}737738private void _endRendering(final int ymin, final int ymax) {739if (DISABLE_RENDER) {740return;741}742743// Get X bounds as true pixel boundaries to compute correct pixel coverage:744final int bboxx0 = bbox_spminX;745final int bboxx1 = bbox_spmaxX;746747final boolean windingRuleEvenOdd = (windingRule == WIND_EVEN_ODD);748749// Useful when processing tile line by tile line750final int[] _alpha = alphaLine;751752// local vars (performance):753final MarlinCache _cache = cache;754final OffHeapArray _edges = edges;755final int[] _edgeBuckets = edgeBuckets;756final int[] _edgeBucketCounts = edgeBucketCounts;757758int[] _crossings = this.crossings;759int[] _edgePtrs = this.edgePtrs;760761// merge sort auxiliary storage:762int[] _aux_crossings = this.aux_crossings;763int[] _aux_edgePtrs = this.aux_edgePtrs;764765// copy constants:766final long _OFF_ERROR = OFF_ERROR;767final long _OFF_BUMP_X = OFF_BUMP_X;768final long _OFF_BUMP_ERR = OFF_BUMP_ERR;769770final long _OFF_NEXT = OFF_NEXT;771final long _OFF_YMAX = OFF_YMAX;772773final int _ALL_BUT_LSB = ALL_BUT_LSB;774final int _ERR_STEP_MAX = ERR_STEP_MAX;775776// unsafe I/O:777final Unsafe _unsafe = OffHeapArray.UNSAFE;778final long addr0 = _edges.address;779long addr;780final int _SUBPIXEL_LG_POSITIONS_X = SUBPIXEL_LG_POSITIONS_X;781final int _SUBPIXEL_LG_POSITIONS_Y = SUBPIXEL_LG_POSITIONS_Y;782final int _SUBPIXEL_MASK_X = SUBPIXEL_MASK_X;783final int _SUBPIXEL_MASK_Y = SUBPIXEL_MASK_Y;784final int _SUBPIXEL_POSITIONS_X = SUBPIXEL_POSITIONS_X;785786final int _MIN_VALUE = Integer.MIN_VALUE;787final int _MAX_VALUE = Integer.MAX_VALUE;788789// Now we iterate through the scanlines. We must tell emitRow the coord790// of the first non-transparent pixel, so we must keep accumulators for791// the first and last pixels of the section of the current pixel row792// that we will emit.793// We also need to accumulate pix_bbox, but the iterator does it794// for us. We will just get the values from it once this loop is done795int minX = _MAX_VALUE;796int maxX = _MIN_VALUE;797798int y = ymin;799int bucket = y - boundsMinY;800801int numCrossings = this.edgeCount;802int edgePtrsLen = _edgePtrs.length;803int crossingsLen = _crossings.length;804int _arrayMaxUsed = activeEdgeMaxUsed;805int ptrLen = 0, newCount, ptrEnd;806807int bucketcount, i, j, ecur;808int cross, lastCross;809int x0, x1, tmp, sum, prev, curx, curxo, crorientation, err;810int pix_x, pix_xmaxm1, pix_xmax;811812int low, high, mid, prevNumCrossings;813boolean useBinarySearch;814815final int[] _blkFlags = blkFlags;816final int _BLK_SIZE_LG = BLOCK_SIZE_LG;817final int _BLK_SIZE = BLOCK_SIZE;818819final boolean _enableBlkFlagsHeuristics = ENABLE_BLOCK_FLAGS_HEURISTICS && this.enableBlkFlags;820821// Use block flags if large pixel span and few crossings:822// ie mean(distance between crossings) is high823boolean useBlkFlags = this.prevUseBlkFlags;824825final int stroking = rdrCtx.stroking;826827int lastY = -1; // last emited row828829830// Iteration on scanlines831for (; y < ymax; y++, bucket++) {832// --- from former ScanLineIterator.next()833bucketcount = _edgeBucketCounts[bucket];834835// marker on previously sorted edges:836prevNumCrossings = numCrossings;837838// bucketCount indicates new edge / edge end:839if (bucketcount != 0) {840if (DO_STATS) {841rdrCtx.stats.stat_rdr_activeEdges_updates.add(numCrossings);842}843844// last bit set to 1 means that edges ends845if ((bucketcount & 0x1) != 0) {846// eviction in active edge list847// cache edges[] address + offset848addr = addr0 + _OFF_YMAX;849850for (i = 0, newCount = 0; i < numCrossings; i++) {851// get the pointer to the edge852ecur = _edgePtrs[i];853// random access so use unsafe:854if (_unsafe.getInt(addr + ecur) > y) {855_edgePtrs[newCount++] = ecur;856}857}858// update marker on sorted edges minus removed edges:859prevNumCrossings = numCrossings = newCount;860}861862ptrLen = bucketcount >> 1; // number of new edge863864if (ptrLen != 0) {865if (DO_STATS) {866rdrCtx.stats.stat_rdr_activeEdges_adds.add(ptrLen);867if (ptrLen > 10) {868rdrCtx.stats.stat_rdr_activeEdges_adds_high.add(ptrLen);869}870}871ptrEnd = numCrossings + ptrLen;872873if (edgePtrsLen < ptrEnd) {874if (DO_STATS) {875rdrCtx.stats.stat_array_renderer_edgePtrs.add(ptrEnd);876}877this.edgePtrs = _edgePtrs878= edgePtrs_ref.widenArray(_edgePtrs, numCrossings,879ptrEnd);880881edgePtrsLen = _edgePtrs.length;882// Get larger auxiliary storage:883aux_edgePtrs_ref.putArray(_aux_edgePtrs);884885// use ArrayCache.getNewSize() to use the same growing886// factor than widenArray():887if (DO_STATS) {888rdrCtx.stats.stat_array_renderer_aux_edgePtrs.add(ptrEnd);889}890this.aux_edgePtrs = _aux_edgePtrs891= aux_edgePtrs_ref.getArray(892ArrayCacheConst.getNewSize(numCrossings, ptrEnd)893);894}895896// cache edges[] address + offset897addr = addr0 + _OFF_NEXT;898899// add new edges to active edge list:900for (ecur = _edgeBuckets[bucket];901numCrossings < ptrEnd; numCrossings++)902{903// store the pointer to the edge904_edgePtrs[numCrossings] = ecur;905// random access so use unsafe:906ecur = _unsafe.getInt(addr + ecur);907}908909if (crossingsLen < numCrossings) {910// Get larger array:911crossings_ref.putArray(_crossings);912913if (DO_STATS) {914rdrCtx.stats.stat_array_renderer_crossings915.add(numCrossings);916}917this.crossings = _crossings918= crossings_ref.getArray(numCrossings);919920// Get larger auxiliary storage:921aux_crossings_ref.putArray(_aux_crossings);922923if (DO_STATS) {924rdrCtx.stats.stat_array_renderer_aux_crossings925.add(numCrossings);926}927this.aux_crossings = _aux_crossings928= aux_crossings_ref.getArray(numCrossings);929930crossingsLen = _crossings.length;931}932if (DO_STATS) {933// update max used mark934if (numCrossings > _arrayMaxUsed) {935_arrayMaxUsed = numCrossings;936}937}938} // ptrLen != 0939} // bucketCount != 0940941942if (numCrossings != 0) {943/*944* thresholds to switch to optimized merge sort945* for newly added edges + final merge pass.946*/947if ((ptrLen < 10) || (numCrossings < 40)) {948if (DO_STATS) {949rdrCtx.stats.hist_rdr_crossings.add(numCrossings);950rdrCtx.stats.hist_rdr_crossings_adds.add(ptrLen);951}952953/*954* threshold to use binary insertion sort instead of955* straight insertion sort (to reduce minimize comparisons).956*/957useBinarySearch = (numCrossings >= 20);958959// if small enough:960lastCross = _MIN_VALUE;961962for (i = 0; i < numCrossings; i++) {963// get the pointer to the edge964ecur = _edgePtrs[i];965966/* convert subpixel coordinates into pixel967positions for coming scanline */968/* note: it is faster to always update edges even969if it is removed from AEL for coming or last scanline */970971// random access so use unsafe:972addr = addr0 + ecur; // ecur + OFF_F_CURX973974// get current crossing:975curx = _unsafe.getInt(addr);976977// update crossing with orientation at last bit:978cross = curx;979980// Increment x using DDA (fixed point):981curx += _unsafe.getInt(addr + _OFF_BUMP_X);982983// Increment error:984err = _unsafe.getInt(addr + _OFF_ERROR)985+ _unsafe.getInt(addr + _OFF_BUMP_ERR);986987// Manual carry handling:988// keep sign and carry bit only and ignore last bit (preserve orientation):989_unsafe.putInt(addr, curx - ((err >> 30) & _ALL_BUT_LSB));990_unsafe.putInt(addr + _OFF_ERROR, (err & _ERR_STEP_MAX));991992if (DO_STATS) {993rdrCtx.stats.stat_rdr_crossings_updates.add(numCrossings);994}995996// insertion sort of crossings:997if (cross < lastCross) {998if (DO_STATS) {999rdrCtx.stats.stat_rdr_crossings_sorts.add(i);1000}10011002/* use binary search for newly added edges1003in crossings if arrays are large enough */1004if (useBinarySearch && (i >= prevNumCrossings)) {1005if (DO_STATS) {1006rdrCtx.stats.stat_rdr_crossings_bsearch.add(i);1007}1008low = 0;1009high = i - 1;10101011do {1012// note: use signed shift (not >>>) for performance1013// as indices are small enough to exceed Integer.MAX_VALUE1014mid = (low + high) >> 1;10151016if (_crossings[mid] < cross) {1017low = mid + 1;1018} else {1019high = mid - 1;1020}1021} while (low <= high);10221023for (j = i - 1; j >= low; j--) {1024_crossings[j + 1] = _crossings[j];1025_edgePtrs [j + 1] = _edgePtrs[j];1026}1027_crossings[low] = cross;1028_edgePtrs [low] = ecur;10291030} else {1031j = i - 1;1032_crossings[i] = _crossings[j];1033_edgePtrs[i] = _edgePtrs[j];10341035while ((--j >= 0) && (_crossings[j] > cross)) {1036_crossings[j + 1] = _crossings[j];1037_edgePtrs [j + 1] = _edgePtrs[j];1038}1039_crossings[j + 1] = cross;1040_edgePtrs [j + 1] = ecur;1041}10421043} else {1044_crossings[i] = lastCross = cross;1045}1046}1047} else {1048if (DO_STATS) {1049rdrCtx.stats.stat_rdr_crossings_msorts.add(numCrossings);1050rdrCtx.stats.hist_rdr_crossings_ratio1051.add((1000 * ptrLen) / numCrossings);1052rdrCtx.stats.hist_rdr_crossings_msorts.add(numCrossings);1053rdrCtx.stats.hist_rdr_crossings_msorts_adds.add(ptrLen);1054}10551056// Copy sorted data in auxiliary arrays1057// and perform insertion sort on almost sorted data1058// (ie i < prevNumCrossings):10591060lastCross = _MIN_VALUE;10611062for (i = 0; i < numCrossings; i++) {1063// get the pointer to the edge1064ecur = _edgePtrs[i];10651066/* convert subpixel coordinates into pixel1067positions for coming scanline */1068/* note: it is faster to always update edges even1069if it is removed from AEL for coming or last scanline */10701071// random access so use unsafe:1072addr = addr0 + ecur; // ecur + OFF_F_CURX10731074// get current crossing:1075curx = _unsafe.getInt(addr);10761077// update crossing with orientation at last bit:1078cross = curx;10791080// Increment x using DDA (fixed point):1081curx += _unsafe.getInt(addr + _OFF_BUMP_X);10821083// Increment error:1084err = _unsafe.getInt(addr + _OFF_ERROR)1085+ _unsafe.getInt(addr + _OFF_BUMP_ERR);10861087// Manual carry handling:1088// keep sign and carry bit only and ignore last bit (preserve orientation):1089_unsafe.putInt(addr, curx - ((err >> 30) & _ALL_BUT_LSB));1090_unsafe.putInt(addr + _OFF_ERROR, (err & _ERR_STEP_MAX));10911092if (DO_STATS) {1093rdrCtx.stats.stat_rdr_crossings_updates.add(numCrossings);1094}10951096if (i >= prevNumCrossings) {1097// simply store crossing as edgePtrs is in-place:1098// will be copied and sorted efficiently by mergesort later:1099_crossings[i] = cross;11001101} else if (cross < lastCross) {1102if (DO_STATS) {1103rdrCtx.stats.stat_rdr_crossings_sorts.add(i);1104}11051106// (straight) insertion sort of crossings:1107j = i - 1;1108_aux_crossings[i] = _aux_crossings[j];1109_aux_edgePtrs[i] = _aux_edgePtrs[j];11101111while ((--j >= 0) && (_aux_crossings[j] > cross)) {1112_aux_crossings[j + 1] = _aux_crossings[j];1113_aux_edgePtrs [j + 1] = _aux_edgePtrs[j];1114}1115_aux_crossings[j + 1] = cross;1116_aux_edgePtrs [j + 1] = ecur;11171118} else {1119// auxiliary storage:1120_aux_crossings[i] = lastCross = cross;1121_aux_edgePtrs [i] = ecur;1122}1123}11241125// use Mergesort using auxiliary arrays (sort only right part)1126MergeSort.mergeSortNoCopy(_crossings, _edgePtrs,1127_aux_crossings, _aux_edgePtrs,1128numCrossings, prevNumCrossings);1129}11301131// reset ptrLen1132ptrLen = 0;1133// --- from former ScanLineIterator.next()113411351136/* note: bboxx0 and bboxx1 must be pixel boundaries1137to have correct coverage computation */11381139// right shift on crossings to get the x-coordinate:1140curxo = _crossings[0];1141x0 = curxo >> 1;1142if (x0 < minX) {1143minX = x0; // subpixel coordinate1144}11451146x1 = _crossings[numCrossings - 1] >> 1;1147if (x1 > maxX) {1148maxX = x1; // subpixel coordinate1149}115011511152// compute pixel coverages1153prev = curx = x0;1154// to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.1155// last bit contains orientation (0 or 1)1156crorientation = ((curxo & 0x1) << 1) - 1;11571158if (windingRuleEvenOdd) {1159sum = crorientation;11601161// Even Odd winding rule: take care of mask ie sum(orientations)1162for (i = 1; i < numCrossings; i++) {1163curxo = _crossings[i];1164curx = curxo >> 1;1165// to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.1166// last bit contains orientation (0 or 1)1167crorientation = ((curxo & 0x1) << 1) - 1;11681169if ((sum & 0x1) != 0) {1170// TODO: perform line clipping on left-right sides1171// to avoid such bound checks:1172x0 = (prev > bboxx0) ? prev : bboxx0;11731174if (curx < bboxx1) {1175x1 = curx;1176} else {1177x1 = bboxx1;1178// skip right side (fast exit loop):1179i = numCrossings;1180}11811182if (x0 < x1) {1183x0 -= bboxx0; // turn x0, x1 from coords to indices1184x1 -= bboxx0; // in the alpha array.11851186pix_x = x0 >> _SUBPIXEL_LG_POSITIONS_X;1187pix_xmaxm1 = (x1 - 1) >> _SUBPIXEL_LG_POSITIONS_X;11881189if (pix_x == pix_xmaxm1) {1190// Start and end in same pixel1191tmp = (x1 - x0); // number of subpixels1192_alpha[pix_x ] += tmp;1193_alpha[pix_x + 1] -= tmp;11941195if (useBlkFlags) {1196// flag used blocks:1197// note: block processing handles extra pixel:1198_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;1199}1200} else {1201tmp = (x0 & _SUBPIXEL_MASK_X);1202_alpha[pix_x ]1203+= (_SUBPIXEL_POSITIONS_X - tmp);1204_alpha[pix_x + 1]1205+= tmp;12061207pix_xmax = x1 >> _SUBPIXEL_LG_POSITIONS_X;12081209tmp = (x1 & _SUBPIXEL_MASK_X);1210_alpha[pix_xmax ]1211-= (_SUBPIXEL_POSITIONS_X - tmp);1212_alpha[pix_xmax + 1]1213-= tmp;12141215if (useBlkFlags) {1216// flag used blocks:1217// note: block processing handles extra pixel:1218_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;1219_blkFlags[pix_xmax >> _BLK_SIZE_LG] = 1;1220}1221}1222}1223}12241225sum += crorientation;1226prev = curx;1227}1228} else {1229// Non-zero winding rule: optimize that case (default)1230// and avoid processing intermediate crossings1231for (i = 1, sum = 0;; i++) {1232sum += crorientation;12331234if (sum != 0) {1235// prev = min(curx)1236if (prev > curx) {1237prev = curx;1238}1239} else {1240// TODO: perform line clipping on left-right sides1241// to avoid such bound checks:1242x0 = (prev > bboxx0) ? prev : bboxx0;12431244if (curx < bboxx1) {1245x1 = curx;1246} else {1247x1 = bboxx1;1248// skip right side (fast exit loop):1249i = numCrossings;1250}12511252if (x0 < x1) {1253x0 -= bboxx0; // turn x0, x1 from coords to indices1254x1 -= bboxx0; // in the alpha array.12551256pix_x = x0 >> _SUBPIXEL_LG_POSITIONS_X;1257pix_xmaxm1 = (x1 - 1) >> _SUBPIXEL_LG_POSITIONS_X;12581259if (pix_x == pix_xmaxm1) {1260// Start and end in same pixel1261tmp = (x1 - x0); // number of subpixels1262_alpha[pix_x ] += tmp;1263_alpha[pix_x + 1] -= tmp;12641265if (useBlkFlags) {1266// flag used blocks:1267// note: block processing handles extra pixel:1268_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;1269}1270} else {1271tmp = (x0 & _SUBPIXEL_MASK_X);1272_alpha[pix_x ]1273+= (_SUBPIXEL_POSITIONS_X - tmp);1274_alpha[pix_x + 1]1275+= tmp;12761277pix_xmax = x1 >> _SUBPIXEL_LG_POSITIONS_X;12781279tmp = (x1 & _SUBPIXEL_MASK_X);1280_alpha[pix_xmax ]1281-= (_SUBPIXEL_POSITIONS_X - tmp);1282_alpha[pix_xmax + 1]1283-= tmp;12841285if (useBlkFlags) {1286// flag used blocks:1287// note: block processing handles extra pixel:1288_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;1289_blkFlags[pix_xmax >> _BLK_SIZE_LG] = 1;1290}1291}1292}1293prev = _MAX_VALUE;1294}12951296if (i == numCrossings) {1297break;1298}12991300curxo = _crossings[i];1301curx = curxo >> 1;1302// to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.1303// last bit contains orientation (0 or 1)1304crorientation = ((curxo & 0x1) << 1) - 1;1305}1306}1307} // numCrossings > 013081309// even if this last row had no crossings, alpha will be zeroed1310// from the last emitRow call. But this doesn't matter because1311// maxX < minX, so no row will be emitted to the MarlinCache.1312if ((y & _SUBPIXEL_MASK_Y) == _SUBPIXEL_MASK_Y) {1313lastY = y >> _SUBPIXEL_LG_POSITIONS_Y;13141315// convert subpixel to pixel coordinate within boundaries:1316minX = FloatMath.max(minX, bboxx0) >> _SUBPIXEL_LG_POSITIONS_X;1317maxX = FloatMath.min(maxX, bboxx1) >> _SUBPIXEL_LG_POSITIONS_X;13181319if (maxX >= minX) {1320// note: alpha array will be zeroed by copyAARow()1321// +1 because alpha [pix_minX; pix_maxX[1322// fix range [x0; x1[1323// note: if x1=bboxx1, then alpha is written up to bboxx1+11324// inclusive: alpha[bboxx1] ignored, alpha[bboxx1+1] == 01325// (normally so never cleared below)1326copyAARow(_alpha, lastY, minX, maxX + 1, useBlkFlags);13271328// speculative for next pixel row (scanline coherence):1329if (_enableBlkFlagsHeuristics) {1330// Use block flags if large pixel span and few crossings:1331// ie mean(distance between crossings) is larger than1332// 1 block size;13331334// fast check width:1335maxX -= minX;13361337// if stroking: numCrossings /= 21338// => shift numCrossings by 11339// condition = (width / (numCrossings - 1)) > blockSize1340useBlkFlags = (maxX > _BLK_SIZE) && (maxX >1341(((numCrossings >> stroking) - 1) << _BLK_SIZE_LG));13421343if (DO_STATS) {1344tmp = FloatMath.max(1,1345((numCrossings >> stroking) - 1));1346rdrCtx.stats.hist_tile_generator_encoding_dist1347.add(maxX / tmp);1348}1349}1350} else {1351_cache.clearAARow(lastY);1352}1353minX = _MAX_VALUE;1354maxX = _MIN_VALUE;1355}1356} // scan line iterator13571358// Emit final row1359y--;1360y >>= _SUBPIXEL_LG_POSITIONS_Y;13611362// convert subpixel to pixel coordinate within boundaries:1363minX = FloatMath.max(minX, bboxx0) >> _SUBPIXEL_LG_POSITIONS_X;1364maxX = FloatMath.min(maxX, bboxx1) >> _SUBPIXEL_LG_POSITIONS_X;13651366if (maxX >= minX) {1367// note: alpha array will be zeroed by copyAARow()1368// +1 because alpha [pix_minX; pix_maxX[1369// fix range [x0; x1[1370// note: if x1=bboxx1, then alpha is written up to bboxx1+11371// inclusive: alpha[bboxx1] ignored then cleared and1372// alpha[bboxx1+1] == 0 (normally so never cleared after)1373copyAARow(_alpha, y, minX, maxX + 1, useBlkFlags);1374} else if (y != lastY) {1375_cache.clearAARow(y);1376}13771378// update member:1379edgeCount = numCrossings;1380prevUseBlkFlags = useBlkFlags;13811382if (DO_STATS) {1383// update max used mark1384activeEdgeMaxUsed = _arrayMaxUsed;1385}1386}13871388boolean endRendering() {1389if (DO_MONITORS) {1390rdrCtx.stats.mon_rdr_endRendering.start();1391}1392if (edgeMinY == Integer.MAX_VALUE) {1393return false; // undefined edges bounds1394}13951396// bounds as half-open intervals1397final int spminX = FloatMath.max(FloatMath.ceil_int(edgeMinX - 0.5d), boundsMinX);1398final int spmaxX = FloatMath.min(FloatMath.ceil_int(edgeMaxX - 0.5d), boundsMaxX);13991400// edge Min/Max Y are already rounded to subpixels within bounds:1401final int spminY = edgeMinY;1402final int spmaxY = edgeMaxY;14031404buckets_minY = spminY - boundsMinY;1405buckets_maxY = spmaxY - boundsMinY;14061407if (DO_LOG_BOUNDS) {1408MarlinUtils.logInfo("edgesXY = [" + edgeMinX + " ... " + edgeMaxX1409+ "[ [" + edgeMinY + " ... " + edgeMaxY + "[");1410MarlinUtils.logInfo("spXY = [" + spminX + " ... " + spmaxX1411+ "[ [" + spminY + " ... " + spmaxY + "[");1412}14131414// test clipping for shapes out of bounds1415if ((spminX >= spmaxX) || (spminY >= spmaxY)) {1416return false;1417}14181419// half open intervals1420// inclusive:1421final int pminX = spminX >> SUBPIXEL_LG_POSITIONS_X;1422// exclusive:1423final int pmaxX = (spmaxX + SUBPIXEL_MASK_X) >> SUBPIXEL_LG_POSITIONS_X;1424// inclusive:1425final int pminY = spminY >> SUBPIXEL_LG_POSITIONS_Y;1426// exclusive:1427final int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >> SUBPIXEL_LG_POSITIONS_Y;14281429// store BBox to answer ptg.getBBox():1430this.cache.init(pminX, pminY, pmaxX, pmaxY);14311432// Heuristics for using block flags:1433if (ENABLE_BLOCK_FLAGS) {1434enableBlkFlags = this.cache.useRLE;1435prevUseBlkFlags = enableBlkFlags && !ENABLE_BLOCK_FLAGS_HEURISTICS;14361437if (enableBlkFlags) {1438// ensure blockFlags array is large enough:1439// note: +2 to ensure enough space left at end1440final int blkLen = ((pmaxX - pminX) >> BLOCK_SIZE_LG) + 2;1441if (blkLen > INITIAL_ARRAY) {1442blkFlags = blkFlags_ref.getArray(blkLen);1443}1444}1445}14461447// memorize the rendering bounding box:1448/* note: bbox_spminX and bbox_spmaxX must be pixel boundaries1449to have correct coverage computation */1450// inclusive:1451bbox_spminX = pminX << SUBPIXEL_LG_POSITIONS_X;1452// exclusive:1453bbox_spmaxX = pmaxX << SUBPIXEL_LG_POSITIONS_X;1454// inclusive:1455bbox_spminY = spminY;1456// exclusive:1457bbox_spmaxY = spmaxY;14581459if (DO_LOG_BOUNDS) {1460MarlinUtils.logInfo("pXY = [" + pminX + " ... " + pmaxX1461+ "[ [" + pminY + " ... " + pmaxY + "[");1462MarlinUtils.logInfo("bbox_spXY = [" + bbox_spminX + " ... "1463+ bbox_spmaxX + "[ [" + bbox_spminY + " ... "1464+ bbox_spmaxY + "[");1465}14661467// Prepare alpha line:1468// add 2 to better deal with the last pixel in a pixel row.1469final int width = (pmaxX - pminX) + 2;14701471// Useful when processing tile line by tile line1472if (width > INITIAL_AA_ARRAY) {1473if (DO_STATS) {1474rdrCtx.stats.stat_array_renderer_alphaline.add(width);1475}1476alphaLine = alphaLine_ref.getArray(width);1477}14781479// process first tile line:1480endRendering(pminY);14811482return true;1483}14841485private int bbox_spminX, bbox_spmaxX, bbox_spminY, bbox_spmaxY;14861487void endRendering(final int pminY) {1488if (DO_MONITORS) {1489rdrCtx.stats.mon_rdr_endRendering_Y.start();1490}14911492final int spminY = pminY << SUBPIXEL_LG_POSITIONS_Y;1493final int fixed_spminY = FloatMath.max(bbox_spminY, spminY);14941495// avoid rendering for last call to nextTile()1496if (fixed_spminY < bbox_spmaxY) {1497// process a complete tile line ie scanlines for 32 rows1498final int spmaxY = FloatMath.min(bbox_spmaxY, spminY + SUBPIXEL_TILE);14991500// process tile line [0 - 32]1501cache.resetTileLine(pminY);15021503// Process only one tile line:1504_endRendering(fixed_spminY, spmaxY);1505}1506if (DO_MONITORS) {1507rdrCtx.stats.mon_rdr_endRendering_Y.stop();1508}1509}15101511void copyAARow(final int[] alphaRow,1512final int pix_y, final int pix_from, final int pix_to,1513final boolean useBlockFlags)1514{1515if (DO_MONITORS) {1516rdrCtx.stats.mon_rdr_copyAARow.start();1517}1518if (useBlockFlags) {1519if (DO_STATS) {1520rdrCtx.stats.hist_tile_generator_encoding.add(1);1521}1522cache.copyAARowRLE_WithBlockFlags(blkFlags, alphaRow, pix_y, pix_from, pix_to);1523} else {1524if (DO_STATS) {1525rdrCtx.stats.hist_tile_generator_encoding.add(0);1526}1527cache.copyAARowNoRLE(alphaRow, pix_y, pix_from, pix_to);1528}1529if (DO_MONITORS) {1530rdrCtx.stats.mon_rdr_copyAARow.stop();1531}1532}1533}153415351536