Path: blob/master/src/java.desktop/share/classes/sun/java2d/marlin/Helpers.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 java.util.Arrays;28import sun.java2d.marlin.stats.Histogram;29import sun.java2d.marlin.stats.StatLong;3031final class Helpers implements MarlinConst {3233private Helpers() {34throw new Error("This is a non instantiable class");35}3637static boolean within(final double x, final double y, final double err) {38final double d = y - x;39return (d <= err && d >= -err);40}4142static double evalCubic(final double a, final double b,43final double c, final double d,44final double t)45{46return t * (t * (t * a + b) + c) + d;47}4849static double evalQuad(final double a, final double b,50final double c, final double t)51{52return t * (t * a + b) + c;53}5455static int quadraticRoots(final double a, final double b, final double c,56final double[] zeroes, final int off)57{58int ret = off;59if (a != 0.0d) {60final double dis = b*b - 4.0d * a * c;61if (dis > 0.0d) {62final double sqrtDis = Math.sqrt(dis);63// depending on the sign of b we use a slightly different64// algorithm than the traditional one to find one of the roots65// so we can avoid adding numbers of different signs (which66// might result in loss of precision).67if (b >= 0.0d) {68zeroes[ret++] = (2.0d * c) / (-b - sqrtDis);69zeroes[ret++] = (-b - sqrtDis) / (2.0d * a);70} else {71zeroes[ret++] = (-b + sqrtDis) / (2.0d * a);72zeroes[ret++] = (2.0d * c) / (-b + sqrtDis);73}74} else if (dis == 0.0d) {75zeroes[ret++] = -b / (2.0d * a);76}77} else if (b != 0.0d) {78zeroes[ret++] = -c / b;79}80return ret - off;81}8283// find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B)84static int cubicRootsInAB(final double d, double a, double b, double c,85final double[] pts, final int off,86final double A, final double B)87{88if (d == 0.0d) {89final int num = quadraticRoots(a, b, c, pts, off);90return filterOutNotInAB(pts, off, num, A, B) - off;91}92// From Graphics Gems:93// https://github.com/erich666/GraphicsGems/blob/master/gems/Roots3And4.c94// (also from awt.geom.CubicCurve2D. But here we don't need as95// much accuracy and we don't want to create arrays so we use96// our own customized version).9798// normal form: x^3 + ax^2 + bx + c = 099100/*101* TODO: cleanup all that code after reading Roots3And4.c102*/103a /= d;104b /= d;105c /= d;106107// substitute x = y - A/3 to eliminate quadratic term:108// x^3 +Px + Q = 0109//110// Since we actually need P/3 and Q/2 for all of the111// calculations that follow, we will calculate112// p = P/3113// q = Q/2114// instead and use those values for simplicity of the code.115final double sub = (1.0d / 3.0d) * a;116final double sq_A = a * a;117final double p = (1.0d / 3.0d) * ((-1.0d / 3.0d) * sq_A + b);118final double q = (1.0d / 2.0d) * ((2.0d / 27.0d) * a * sq_A - sub * b + c);119120// use Cardano's formula121122final double cb_p = p * p * p;123final double D = q * q + cb_p;124125int num;126if (D < 0.0d) {127// see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method128final double phi = (1.0d / 3.0d) * Math.acos(-q / Math.sqrt(-cb_p));129final double t = 2.0d * Math.sqrt(-p);130131pts[off ] = ( t * Math.cos(phi) - sub);132pts[off + 1] = (-t * Math.cos(phi + (Math.PI / 3.0d)) - sub);133pts[off + 2] = (-t * Math.cos(phi - (Math.PI / 3.0d)) - sub);134num = 3;135} else {136final double sqrt_D = Math.sqrt(D);137final double u = Math.cbrt(sqrt_D - q);138final double v = - Math.cbrt(sqrt_D + q);139140pts[off ] = (u + v - sub);141num = 1;142143if (within(D, 0.0d, 1e-8d)) {144pts[off + 1] = ((-1.0d / 2.0d) * (u + v) - sub);145num = 2;146}147}148149return filterOutNotInAB(pts, off, num, A, B) - off;150}151152// returns the index 1 past the last valid element remaining after filtering153static int filterOutNotInAB(final double[] nums, final int off, final int len,154final double a, final double b)155{156int ret = off;157for (int i = off, end = off + len; i < end; i++) {158if (nums[i] >= a && nums[i] < b) {159nums[ret++] = nums[i];160}161}162return ret;163}164165static double fastLineLen(final double x0, final double y0,166final double x1, final double y1)167{168final double dx = x1 - x0;169final double dy = y1 - y0;170171// use manhattan norm:172return Math.abs(dx) + Math.abs(dy);173}174175static double linelen(final double x0, final double y0,176final double x1, final double y1)177{178final double dx = x1 - x0;179final double dy = y1 - y0;180return Math.sqrt(dx * dx + dy * dy);181}182183static double fastQuadLen(final double x0, final double y0,184final double x1, final double y1,185final double x2, final double y2)186{187final double dx1 = x1 - x0;188final double dx2 = x2 - x1;189final double dy1 = y1 - y0;190final double dy2 = y2 - y1;191192// use manhattan norm:193return Math.abs(dx1) + Math.abs(dx2)194+ Math.abs(dy1) + Math.abs(dy2);195}196197static double quadlen(final double x0, final double y0,198final double x1, final double y1,199final double x2, final double y2)200{201return (linelen(x0, y0, x1, y1)202+ linelen(x1, y1, x2, y2)203+ linelen(x0, y0, x2, y2)) / 2.0d;204}205206static double fastCurvelen(final double x0, final double y0,207final double x1, final double y1,208final double x2, final double y2,209final double x3, final double y3)210{211final double dx1 = x1 - x0;212final double dx2 = x2 - x1;213final double dx3 = x3 - x2;214final double dy1 = y1 - y0;215final double dy2 = y2 - y1;216final double dy3 = y3 - y2;217218// use manhattan norm:219return Math.abs(dx1) + Math.abs(dx2) + Math.abs(dx3)220+ Math.abs(dy1) + Math.abs(dy2) + Math.abs(dy3);221}222223static double curvelen(final double x0, final double y0,224final double x1, final double y1,225final double x2, final double y2,226final double x3, final double y3)227{228return (linelen(x0, y0, x1, y1)229+ linelen(x1, y1, x2, y2)230+ linelen(x2, y2, x3, y3)231+ linelen(x0, y0, x3, y3)) / 2.0d;232}233234// finds values of t where the curve in pts should be subdivided in order235// to get good offset curves a distance of w away from the middle curve.236// Stores the points in ts, and returns how many of them there were.237static int findSubdivPoints(final Curve c, final double[] pts,238final double[] ts, final int type,239final double w2)240{241final double x12 = pts[2] - pts[0];242final double y12 = pts[3] - pts[1];243// if the curve is already parallel to either axis we gain nothing244// from rotating it.245if ((y12 != 0.0d) && (x12 != 0.0d)) {246// we rotate it so that the first vector in the control polygon is247// parallel to the x-axis. This will ensure that rotated quarter248// circles won't be subdivided.249final double hypot = Math.sqrt(x12 * x12 + y12 * y12);250final double cos = x12 / hypot;251final double sin = y12 / hypot;252final double x1 = cos * pts[0] + sin * pts[1];253final double y1 = cos * pts[1] - sin * pts[0];254final double x2 = cos * pts[2] + sin * pts[3];255final double y2 = cos * pts[3] - sin * pts[2];256final double x3 = cos * pts[4] + sin * pts[5];257final double y3 = cos * pts[5] - sin * pts[4];258259switch(type) {260case 8:261final double x4 = cos * pts[6] + sin * pts[7];262final double y4 = cos * pts[7] - sin * pts[6];263c.set(x1, y1, x2, y2, x3, y3, x4, y4);264break;265case 6:266c.set(x1, y1, x2, y2, x3, y3);267break;268default:269}270} else {271c.set(pts, type);272}273274int ret = 0;275// we subdivide at values of t such that the remaining rotated276// curves are monotonic in x and y.277ret += c.dxRoots(ts, ret);278ret += c.dyRoots(ts, ret);279280// subdivide at inflection points.281if (type == 8) {282// quadratic curves can't have inflection points283ret += c.infPoints(ts, ret);284}285286// now we must subdivide at points where one of the offset curves will have287// a cusp. This happens at ts where the radius of curvature is equal to w.288ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001d);289290ret = filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d);291isort(ts, ret);292return ret;293}294295// finds values of t where the curve in pts should be subdivided in order296// to get intersections with the given clip rectangle.297// Stores the points in ts, and returns how many of them there were.298static int findClipPoints(final Curve curve, final double[] pts,299final double[] ts, final int type,300final int outCodeOR,301final double[] clipRect)302{303curve.set(pts, type);304305// clip rectangle (ymin, ymax, xmin, xmax)306int ret = 0;307308if ((outCodeOR & OUTCODE_LEFT) != 0) {309ret += curve.xPoints(ts, ret, clipRect[2]);310}311if ((outCodeOR & OUTCODE_RIGHT) != 0) {312ret += curve.xPoints(ts, ret, clipRect[3]);313}314if ((outCodeOR & OUTCODE_TOP) != 0) {315ret += curve.yPoints(ts, ret, clipRect[0]);316}317if ((outCodeOR & OUTCODE_BOTTOM) != 0) {318ret += curve.yPoints(ts, ret, clipRect[1]);319}320isort(ts, ret);321return ret;322}323324static void subdivide(final double[] src,325final double[] left, final double[] right,326final int type)327{328switch(type) {329case 8:330subdivideCubic(src, left, right);331return;332case 6:333subdivideQuad(src, left, right);334return;335default:336throw new InternalError("Unsupported curve type");337}338}339340static void isort(final double[] a, final int len) {341for (int i = 1, j; i < len; i++) {342final double ai = a[i];343j = i - 1;344for (; j >= 0 && a[j] > ai; j--) {345a[j + 1] = a[j];346}347a[j + 1] = ai;348}349}350351// Most of these are copied from classes in java.awt.geom because we need352// both single and double precision variants of these functions, and Line2D,353// CubicCurve2D, QuadCurve2D don't provide them.354/**355* Subdivides the cubic curve specified by the coordinates356* stored in the <code>src</code> array at indices <code>srcoff</code>357* through (<code>srcoff</code> + 7) and stores the358* resulting two subdivided curves into the two result arrays at the359* corresponding indices.360* Either or both of the <code>left</code> and <code>right</code>361* arrays may be <code>null</code> or a reference to the same array362* as the <code>src</code> array.363* Note that the last point in the first subdivided curve is the364* same as the first point in the second subdivided curve. Thus,365* it is possible to pass the same array for <code>left</code>366* and <code>right</code> and to use offsets, such as <code>rightoff</code>367* equals (<code>leftoff</code> + 6), in order368* to avoid allocating extra storage for this common point.369* @param src the array holding the coordinates for the source curve370* @param left the array for storing the coordinates for the first371* half of the subdivided curve372* @param right the array for storing the coordinates for the second373* half of the subdivided curve374* @since 1.7375*/376static void subdivideCubic(final double[] src,377final double[] left,378final double[] right)379{380double x1 = src[0];381double y1 = src[1];382double cx1 = src[2];383double cy1 = src[3];384double cx2 = src[4];385double cy2 = src[5];386double x2 = src[6];387double y2 = src[7];388389left[0] = x1;390left[1] = y1;391392right[6] = x2;393right[7] = y2;394395x1 = (x1 + cx1) / 2.0d;396y1 = (y1 + cy1) / 2.0d;397x2 = (x2 + cx2) / 2.0d;398y2 = (y2 + cy2) / 2.0d;399400double cx = (cx1 + cx2) / 2.0d;401double cy = (cy1 + cy2) / 2.0d;402403cx1 = (x1 + cx) / 2.0d;404cy1 = (y1 + cy) / 2.0d;405cx2 = (x2 + cx) / 2.0d;406cy2 = (y2 + cy) / 2.0d;407cx = (cx1 + cx2) / 2.0d;408cy = (cy1 + cy2) / 2.0d;409410left[2] = x1;411left[3] = y1;412left[4] = cx1;413left[5] = cy1;414left[6] = cx;415left[7] = cy;416417right[0] = cx;418right[1] = cy;419right[2] = cx2;420right[3] = cy2;421right[4] = x2;422right[5] = y2;423}424425static void subdivideCubicAt(final double t,426final double[] src, final int offS,427final double[] pts, final int offL, final int offR)428{429double x1 = src[offS ];430double y1 = src[offS + 1];431double cx1 = src[offS + 2];432double cy1 = src[offS + 3];433double cx2 = src[offS + 4];434double cy2 = src[offS + 5];435double x2 = src[offS + 6];436double y2 = src[offS + 7];437438pts[offL ] = x1;439pts[offL + 1] = y1;440441pts[offR + 6] = x2;442pts[offR + 7] = y2;443444x1 = x1 + t * (cx1 - x1);445y1 = y1 + t * (cy1 - y1);446x2 = cx2 + t * (x2 - cx2);447y2 = cy2 + t * (y2 - cy2);448449double cx = cx1 + t * (cx2 - cx1);450double cy = cy1 + t * (cy2 - cy1);451452cx1 = x1 + t * (cx - x1);453cy1 = y1 + t * (cy - y1);454cx2 = cx + t * (x2 - cx);455cy2 = cy + t * (y2 - cy);456cx = cx1 + t * (cx2 - cx1);457cy = cy1 + t * (cy2 - cy1);458459pts[offL + 2] = x1;460pts[offL + 3] = y1;461pts[offL + 4] = cx1;462pts[offL + 5] = cy1;463pts[offL + 6] = cx;464pts[offL + 7] = cy;465466pts[offR ] = cx;467pts[offR + 1] = cy;468pts[offR + 2] = cx2;469pts[offR + 3] = cy2;470pts[offR + 4] = x2;471pts[offR + 5] = y2;472}473474static void subdivideQuad(final double[] src,475final double[] left,476final double[] right)477{478double x1 = src[0];479double y1 = src[1];480double cx = src[2];481double cy = src[3];482double x2 = src[4];483double y2 = src[5];484485left[0] = x1;486left[1] = y1;487488right[4] = x2;489right[5] = y2;490491x1 = (x1 + cx) / 2.0d;492y1 = (y1 + cy) / 2.0d;493x2 = (x2 + cx) / 2.0d;494y2 = (y2 + cy) / 2.0d;495cx = (x1 + x2) / 2.0d;496cy = (y1 + y2) / 2.0d;497498left[2] = x1;499left[3] = y1;500left[4] = cx;501left[5] = cy;502503right[0] = cx;504right[1] = cy;505right[2] = x2;506right[3] = y2;507}508509static void subdivideQuadAt(final double t,510final double[] src, final int offS,511final double[] pts, final int offL, final int offR)512{513double x1 = src[offS ];514double y1 = src[offS + 1];515double cx = src[offS + 2];516double cy = src[offS + 3];517double x2 = src[offS + 4];518double y2 = src[offS + 5];519520pts[offL ] = x1;521pts[offL + 1] = y1;522523pts[offR + 4] = x2;524pts[offR + 5] = y2;525526x1 = x1 + t * (cx - x1);527y1 = y1 + t * (cy - y1);528x2 = cx + t * (x2 - cx);529y2 = cy + t * (y2 - cy);530cx = x1 + t * (x2 - x1);531cy = y1 + t * (y2 - y1);532533pts[offL + 2] = x1;534pts[offL + 3] = y1;535pts[offL + 4] = cx;536pts[offL + 5] = cy;537538pts[offR ] = cx;539pts[offR + 1] = cy;540pts[offR + 2] = x2;541pts[offR + 3] = y2;542}543544static void subdivideLineAt(final double t,545final double[] src, final int offS,546final double[] pts, final int offL, final int offR)547{548double x1 = src[offS ];549double y1 = src[offS + 1];550double x2 = src[offS + 2];551double y2 = src[offS + 3];552553pts[offL ] = x1;554pts[offL + 1] = y1;555556pts[offR + 2] = x2;557pts[offR + 3] = y2;558559x1 = x1 + t * (x2 - x1);560y1 = y1 + t * (y2 - y1);561562pts[offL + 2] = x1;563pts[offL + 3] = y1;564565pts[offR ] = x1;566pts[offR + 1] = y1;567}568569static void subdivideAt(final double t,570final double[] src, final int offS,571final double[] pts, final int offL, final int type)572{573// if instead of switch (perf + most probable cases first)574if (type == 8) {575subdivideCubicAt(t, src, offS, pts, offL, offL + type);576} else if (type == 4) {577subdivideLineAt(t, src, offS, pts, offL, offL + type);578} else {579subdivideQuadAt(t, src, offS, pts, offL, offL + type);580}581}582583// From sun.java2d.loops.GeneralRenderer:584585static int outcode(final double x, final double y,586final double[] clipRect)587{588int code;589if (y < clipRect[0]) {590code = OUTCODE_TOP;591} else if (y >= clipRect[1]) {592code = OUTCODE_BOTTOM;593} else {594code = 0;595}596if (x < clipRect[2]) {597code |= OUTCODE_LEFT;598} else if (x >= clipRect[3]) {599code |= OUTCODE_RIGHT;600}601return code;602}603604// a stack of polynomial curves where each curve shares endpoints with605// adjacent ones.606static final class PolyStack {607private static final byte TYPE_LINETO = (byte) 0;608private static final byte TYPE_QUADTO = (byte) 1;609private static final byte TYPE_CUBICTO = (byte) 2;610611// curves capacity = edges count (8192) = edges x 2 (coords)612private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;613614// types capacity = edges count (4096)615private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;616617double[] curves;618int end;619byte[] curveTypes;620int numCurves;621622// curves ref (dirty)623final DoubleArrayCache.Reference curves_ref;624// curveTypes ref (dirty)625final ByteArrayCache.Reference curveTypes_ref;626627// used marks (stats only)628int curveTypesUseMark;629int curvesUseMark;630631private final StatLong stat_polystack_types;632private final StatLong stat_polystack_curves;633private final Histogram hist_polystack_curves;634private final StatLong stat_array_polystack_curves;635private final StatLong stat_array_polystack_curveTypes;636637PolyStack(final RendererContext rdrCtx) {638this(rdrCtx, null, null, null, null, null);639}640641PolyStack(final RendererContext rdrCtx,642final StatLong stat_polystack_types,643final StatLong stat_polystack_curves,644final Histogram hist_polystack_curves,645final StatLong stat_array_polystack_curves,646final StatLong stat_array_polystack_curveTypes)647{648curves_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_CURVES_COUNT); // 32K649curves = curves_ref.initial;650651curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K652curveTypes = curveTypes_ref.initial;653numCurves = 0;654end = 0;655656if (DO_STATS) {657curveTypesUseMark = 0;658curvesUseMark = 0;659}660this.stat_polystack_types = stat_polystack_types;661this.stat_polystack_curves = stat_polystack_curves;662this.hist_polystack_curves = hist_polystack_curves;663this.stat_array_polystack_curves = stat_array_polystack_curves;664this.stat_array_polystack_curveTypes = stat_array_polystack_curveTypes;665}666667/**668* Disposes this PolyStack:669* clean up before reusing this instance670*/671void dispose() {672end = 0;673numCurves = 0;674675if (DO_STATS) {676stat_polystack_types.add(curveTypesUseMark);677stat_polystack_curves.add(curvesUseMark);678hist_polystack_curves.add(curvesUseMark);679680// reset marks681curveTypesUseMark = 0;682curvesUseMark = 0;683}684685// Return arrays:686// curves and curveTypes are kept dirty687curves = curves_ref.putArray(curves);688curveTypes = curveTypes_ref.putArray(curveTypes);689}690691private void ensureSpace(final int n) {692// use substraction to avoid integer overflow:693if (curves.length - end < n) {694if (DO_STATS) {695stat_array_polystack_curves.add(end + n);696}697curves = curves_ref.widenArray(curves, end, end + n);698}699if (curveTypes.length <= numCurves) {700if (DO_STATS) {701stat_array_polystack_curveTypes.add(numCurves + 1);702}703curveTypes = curveTypes_ref.widenArray(curveTypes,704numCurves,705numCurves + 1);706}707}708709void pushCubic(double x0, double y0,710double x1, double y1,711double x2, double y2)712{713ensureSpace(6);714curveTypes[numCurves++] = TYPE_CUBICTO;715// we reverse the coordinate order to make popping easier716final double[] _curves = curves;717int e = end;718_curves[e++] = x2; _curves[e++] = y2;719_curves[e++] = x1; _curves[e++] = y1;720_curves[e++] = x0; _curves[e++] = y0;721end = e;722}723724void pushQuad(double x0, double y0,725double x1, double y1)726{727ensureSpace(4);728curveTypes[numCurves++] = TYPE_QUADTO;729final double[] _curves = curves;730int e = end;731_curves[e++] = x1; _curves[e++] = y1;732_curves[e++] = x0; _curves[e++] = y0;733end = e;734}735736void pushLine(double x, double y) {737ensureSpace(2);738curveTypes[numCurves++] = TYPE_LINETO;739curves[end++] = x; curves[end++] = y;740}741742void pullAll(final DPathConsumer2D io) {743final int nc = numCurves;744if (nc == 0) {745return;746}747if (DO_STATS) {748// update used marks:749if (numCurves > curveTypesUseMark) {750curveTypesUseMark = numCurves;751}752if (end > curvesUseMark) {753curvesUseMark = end;754}755}756final byte[] _curveTypes = curveTypes;757final double[] _curves = curves;758int e = 0;759760for (int i = 0; i < nc; i++) {761switch(_curveTypes[i]) {762case TYPE_LINETO:763io.lineTo(_curves[e], _curves[e+1]);764e += 2;765continue;766case TYPE_CUBICTO:767io.curveTo(_curves[e], _curves[e+1],768_curves[e+2], _curves[e+3],769_curves[e+4], _curves[e+5]);770e += 6;771continue;772case TYPE_QUADTO:773io.quadTo(_curves[e], _curves[e+1],774_curves[e+2], _curves[e+3]);775e += 4;776continue;777default:778}779}780numCurves = 0;781end = 0;782}783784void popAll(final DPathConsumer2D io) {785int nc = numCurves;786if (nc == 0) {787return;788}789if (DO_STATS) {790// update used marks:791if (numCurves > curveTypesUseMark) {792curveTypesUseMark = numCurves;793}794if (end > curvesUseMark) {795curvesUseMark = end;796}797}798final byte[] _curveTypes = curveTypes;799final double[] _curves = curves;800int e = end;801802while (nc != 0) {803switch(_curveTypes[--nc]) {804case TYPE_LINETO:805e -= 2;806io.lineTo(_curves[e], _curves[e+1]);807continue;808case TYPE_CUBICTO:809e -= 6;810io.curveTo(_curves[e], _curves[e+1],811_curves[e+2], _curves[e+3],812_curves[e+4], _curves[e+5]);813continue;814case TYPE_QUADTO:815e -= 4;816io.quadTo(_curves[e], _curves[e+1],817_curves[e+2], _curves[e+3]);818continue;819default:820}821}822numCurves = 0;823end = 0;824}825826@Override827public String toString() {828String ret = "";829int nc = numCurves;830int last = end;831int len;832while (nc != 0) {833switch(curveTypes[--nc]) {834case TYPE_LINETO:835len = 2;836ret += "line: ";837break;838case TYPE_QUADTO:839len = 4;840ret += "quad: ";841break;842case TYPE_CUBICTO:843len = 6;844ret += "cubic: ";845break;846default:847len = 0;848}849last -= len;850ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))851+ "\n";852}853return ret;854}855}856857// a stack of integer indices858static final class IndexStack {859860// integer capacity = edges count / 4 ~ 1024861private static final int INITIAL_COUNT = INITIAL_EDGES_COUNT >> 2;862863private int end;864private int[] indices;865866// indices ref (dirty)867private final IntArrayCache.Reference indices_ref;868869// used marks (stats only)870private int indicesUseMark;871872private final StatLong stat_idxstack_indices;873private final Histogram hist_idxstack_indices;874private final StatLong stat_array_idxstack_indices;875876IndexStack(final RendererContext rdrCtx) {877this(rdrCtx, null, null, null);878}879880IndexStack(final RendererContext rdrCtx,881final StatLong stat_idxstack_indices,882final Histogram hist_idxstack_indices,883final StatLong stat_array_idxstack_indices)884{885indices_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_COUNT); // 4K886indices = indices_ref.initial;887end = 0;888889if (DO_STATS) {890indicesUseMark = 0;891}892this.stat_idxstack_indices = stat_idxstack_indices;893this.hist_idxstack_indices = hist_idxstack_indices;894this.stat_array_idxstack_indices = stat_array_idxstack_indices;895}896897/**898* Disposes this PolyStack:899* clean up before reusing this instance900*/901void dispose() {902end = 0;903904if (DO_STATS) {905stat_idxstack_indices.add(indicesUseMark);906hist_idxstack_indices.add(indicesUseMark);907908// reset marks909indicesUseMark = 0;910}911912// Return arrays:913// values is kept dirty914indices = indices_ref.putArray(indices);915}916917boolean isEmpty() {918return (end == 0);919}920921void reset() {922end = 0;923}924925void push(final int v) {926// remove redundant values (reverse order):927int[] _values = indices;928final int nc = end;929if (nc != 0) {930if (_values[nc - 1] == v) {931// remove both duplicated values:932end--;933return;934}935}936if (_values.length <= nc) {937if (DO_STATS) {938stat_array_idxstack_indices.add(nc + 1);939}940indices = _values = indices_ref.widenArray(_values, nc, nc + 1);941}942_values[end++] = v;943944if (DO_STATS) {945// update used marks:946if (end > indicesUseMark) {947indicesUseMark = end;948}949}950}951952void pullAll(final double[] points, final DPathConsumer2D io) {953final int nc = end;954if (nc == 0) {955return;956}957final int[] _values = indices;958959for (int i = 0, j; i < nc; i++) {960j = _values[i] << 1;961io.lineTo(points[j], points[j + 1]);962}963end = 0;964}965}966}967968969