1 /* 2 * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.java2d.marlin; 27 28 import static java.lang.Math.PI; 29 import java.util.Arrays; 30 import static sun.java2d.marlin.MarlinConst.INITIAL_EDGES_COUNT; 31 import sun.java2d.marlin.stats.Histogram; 32 import sun.java2d.marlin.stats.StatLong; 33 34 final class DHelpers implements MarlinConst { 35 36 private DHelpers() { 37 throw new Error("This is a non instantiable class"); 38 } 39 40 static boolean within(final double x, final double y, final double err) { 41 final double d = y - x; 42 return (d <= err && d >= -err); 43 } 44 45 static int quadraticRoots(final double a, final double b, 46 final double c, double[] zeroes, final int off) 47 { 48 int ret = off; 49 double t; 50 if (a != 0.0d) { 51 final double dis = b*b - 4*a*c; 52 if (dis > 0.0d) { 53 final double sqrtDis = Math.sqrt(dis); 54 // depending on the sign of b we use a slightly different 55 // algorithm than the traditional one to find one of the roots 56 // so we can avoid adding numbers of different signs (which 57 // might result in loss of precision). 58 if (b >= 0.0d) { 59 zeroes[ret++] = (2.0d * c) / (-b - sqrtDis); 60 zeroes[ret++] = (-b - sqrtDis) / (2.0d * a); 61 } else { 62 zeroes[ret++] = (-b + sqrtDis) / (2.0d * a); 63 zeroes[ret++] = (2.0d * c) / (-b + sqrtDis); 64 } 65 } else if (dis == 0.0d) { 66 t = (-b) / (2.0d * a); 67 zeroes[ret++] = t; 68 } 69 } else { 70 if (b != 0.0d) { 71 t = (-c) / b; 72 zeroes[ret++] = t; 73 } 74 } 75 return ret - off; 76 } 77 78 // find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B) 79 static int cubicRootsInAB(double d, double a, double b, double c, 80 double[] pts, final int off, 81 final double A, final double B) 82 { 83 if (d == 0.0d) { 84 int num = quadraticRoots(a, b, c, pts, off); 85 return filterOutNotInAB(pts, off, num, A, B) - off; 86 } 87 // From Graphics Gems: 88 // http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c 89 // (also from awt.geom.CubicCurve2D. But here we don't need as 90 // much accuracy and we don't want to create arrays so we use 91 // our own customized version). 92 93 // normal form: x^3 + ax^2 + bx + c = 0 94 a /= d; 95 b /= d; 96 c /= d; 97 98 // substitute x = y - A/3 to eliminate quadratic term: 99 // x^3 +Px + Q = 0 100 // 101 // Since we actually need P/3 and Q/2 for all of the 102 // calculations that follow, we will calculate 103 // p = P/3 104 // q = Q/2 105 // instead and use those values for simplicity of the code. 106 double sq_A = a * a; 107 double p = (1.0d/3.0d) * ((-1.0d/3.0d) * sq_A + b); 108 double q = (1.0d/2.0d) * ((2.0d/27.0d) * a * sq_A - (1.0d/3.0d) * a * b + c); 109 110 // use Cardano's formula 111 112 double cb_p = p * p * p; 113 double D = q * q + cb_p; 114 115 int num; 116 if (D < 0.0d) { 117 // see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method 118 final double phi = (1.0d/3.0d) * Math.acos(-q / Math.sqrt(-cb_p)); 119 final double t = 2.0d * Math.sqrt(-p); 120 121 pts[ off+0 ] = ( t * Math.cos(phi)); 122 pts[ off+1 ] = (-t * Math.cos(phi + (PI / 3.0d))); 123 pts[ off+2 ] = (-t * Math.cos(phi - (PI / 3.0d))); 124 num = 3; 125 } else { 126 final double sqrt_D = Math.sqrt(D); 127 final double u = Math.cbrt(sqrt_D - q); 128 final double v = - Math.cbrt(sqrt_D + q); 129 130 pts[ off ] = (u + v); 131 num = 1; 132 133 if (within(D, 0.0d, 1e-8d)) { 134 pts[off+1] = -(pts[off] / 2.0d); 135 num = 2; 136 } 137 } 138 139 final double sub = (1.0d/3.0d) * a; 140 141 for (int i = 0; i < num; ++i) { 142 pts[ off+i ] -= sub; 143 } 144 145 return filterOutNotInAB(pts, off, num, A, B) - off; 146 } 147 148 static double evalCubic(final double a, final double b, 149 final double c, final double d, 150 final double t) 151 { 152 return t * (t * (t * a + b) + c) + d; 153 } 154 155 static double evalQuad(final double a, final double b, 156 final double c, final double t) 157 { 158 return t * (t * a + b) + c; 159 } 160 161 // returns the index 1 past the last valid element remaining after filtering 162 static int filterOutNotInAB(double[] nums, final int off, final int len, 163 final double a, final double b) 164 { 165 int ret = off; 166 for (int i = off, end = off + len; i < end; i++) { 167 if (nums[i] >= a && nums[i] < b) { 168 nums[ret++] = nums[i]; 169 } 170 } 171 return ret; 172 } 173 174 static double linelen(double x1, double y1, double x2, double y2) { 175 final double dx = x2 - x1; 176 final double dy = y2 - y1; 177 return Math.sqrt(dx*dx + dy*dy); 178 } 179 180 static void subdivide(double[] src, int srcoff, double[] left, int leftoff, 181 double[] right, int rightoff, int type) 182 { 183 switch(type) { 184 case 6: 185 DHelpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff); 186 return; 187 case 8: 188 DHelpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff); 189 return; 190 default: 191 throw new InternalError("Unsupported curve type"); 192 } 193 } 194 195 static void isort(double[] a, int off, int len) { 196 for (int i = off + 1, end = off + len; i < end; i++) { 197 double ai = a[i]; 198 int j = i - 1; 199 for (; j >= off && a[j] > ai; j--) { 200 a[j+1] = a[j]; 201 } 202 a[j+1] = ai; 203 } 204 } 205 206 // Most of these are copied from classes in java.awt.geom because we need 207 // both single and double precision variants of these functions, and Line2D, 208 // CubicCurve2D, QuadCurve2D don't provide them. 209 /** 210 * Subdivides the cubic curve specified by the coordinates 211 * stored in the <code>src</code> array at indices <code>srcoff</code> 212 * through (<code>srcoff</code> + 7) and stores the 213 * resulting two subdivided curves into the two result arrays at the 214 * corresponding indices. 215 * Either or both of the <code>left</code> and <code>right</code> 216 * arrays may be <code>null</code> or a reference to the same array 217 * as the <code>src</code> array. 218 * Note that the last point in the first subdivided curve is the 219 * same as the first point in the second subdivided curve. Thus, 220 * it is possible to pass the same array for <code>left</code> 221 * and <code>right</code> and to use offsets, such as <code>rightoff</code> 222 * equals (<code>leftoff</code> + 6), in order 223 * to avoid allocating extra storage for this common point. 224 * @param src the array holding the coordinates for the source curve 225 * @param srcoff the offset into the array of the beginning of the 226 * the 6 source coordinates 227 * @param left the array for storing the coordinates for the first 228 * half of the subdivided curve 229 * @param leftoff the offset into the array of the beginning of the 230 * the 6 left coordinates 231 * @param right the array for storing the coordinates for the second 232 * half of the subdivided curve 233 * @param rightoff the offset into the array of the beginning of the 234 * the 6 right coordinates 235 * @since 1.7 236 */ 237 static void subdivideCubic(double[] src, int srcoff, 238 double[] left, int leftoff, 239 double[] right, int rightoff) 240 { 241 double x1 = src[srcoff + 0]; 242 double y1 = src[srcoff + 1]; 243 double ctrlx1 = src[srcoff + 2]; 244 double ctrly1 = src[srcoff + 3]; 245 double ctrlx2 = src[srcoff + 4]; 246 double ctrly2 = src[srcoff + 5]; 247 double x2 = src[srcoff + 6]; 248 double y2 = src[srcoff + 7]; 249 if (left != null) { 250 left[leftoff + 0] = x1; 251 left[leftoff + 1] = y1; 252 } 253 if (right != null) { 254 right[rightoff + 6] = x2; 255 right[rightoff + 7] = y2; 256 } 257 x1 = (x1 + ctrlx1) / 2.0d; 258 y1 = (y1 + ctrly1) / 2.0d; 259 x2 = (x2 + ctrlx2) / 2.0d; 260 y2 = (y2 + ctrly2) / 2.0d; 261 double centerx = (ctrlx1 + ctrlx2) / 2.0d; 262 double centery = (ctrly1 + ctrly2) / 2.0d; 263 ctrlx1 = (x1 + centerx) / 2.0d; 264 ctrly1 = (y1 + centery) / 2.0d; 265 ctrlx2 = (x2 + centerx) / 2.0d; 266 ctrly2 = (y2 + centery) / 2.0d; 267 centerx = (ctrlx1 + ctrlx2) / 2.0d; 268 centery = (ctrly1 + ctrly2) / 2.0d; 269 if (left != null) { 270 left[leftoff + 2] = x1; 271 left[leftoff + 3] = y1; 272 left[leftoff + 4] = ctrlx1; 273 left[leftoff + 5] = ctrly1; 274 left[leftoff + 6] = centerx; 275 left[leftoff + 7] = centery; 276 } 277 if (right != null) { 278 right[rightoff + 0] = centerx; 279 right[rightoff + 1] = centery; 280 right[rightoff + 2] = ctrlx2; 281 right[rightoff + 3] = ctrly2; 282 right[rightoff + 4] = x2; 283 right[rightoff + 5] = y2; 284 } 285 } 286 287 288 static void subdivideCubicAt(double t, double[] src, int srcoff, 289 double[] left, int leftoff, 290 double[] right, int rightoff) 291 { 292 double x1 = src[srcoff + 0]; 293 double y1 = src[srcoff + 1]; 294 double ctrlx1 = src[srcoff + 2]; 295 double ctrly1 = src[srcoff + 3]; 296 double ctrlx2 = src[srcoff + 4]; 297 double ctrly2 = src[srcoff + 5]; 298 double x2 = src[srcoff + 6]; 299 double y2 = src[srcoff + 7]; 300 if (left != null) { 301 left[leftoff + 0] = x1; 302 left[leftoff + 1] = y1; 303 } 304 if (right != null) { 305 right[rightoff + 6] = x2; 306 right[rightoff + 7] = y2; 307 } 308 x1 = x1 + t * (ctrlx1 - x1); 309 y1 = y1 + t * (ctrly1 - y1); 310 x2 = ctrlx2 + t * (x2 - ctrlx2); 311 y2 = ctrly2 + t * (y2 - ctrly2); 312 double centerx = ctrlx1 + t * (ctrlx2 - ctrlx1); 313 double centery = ctrly1 + t * (ctrly2 - ctrly1); 314 ctrlx1 = x1 + t * (centerx - x1); 315 ctrly1 = y1 + t * (centery - y1); 316 ctrlx2 = centerx + t * (x2 - centerx); 317 ctrly2 = centery + t * (y2 - centery); 318 centerx = ctrlx1 + t * (ctrlx2 - ctrlx1); 319 centery = ctrly1 + t * (ctrly2 - ctrly1); 320 if (left != null) { 321 left[leftoff + 2] = x1; 322 left[leftoff + 3] = y1; 323 left[leftoff + 4] = ctrlx1; 324 left[leftoff + 5] = ctrly1; 325 left[leftoff + 6] = centerx; 326 left[leftoff + 7] = centery; 327 } 328 if (right != null) { 329 right[rightoff + 0] = centerx; 330 right[rightoff + 1] = centery; 331 right[rightoff + 2] = ctrlx2; 332 right[rightoff + 3] = ctrly2; 333 right[rightoff + 4] = x2; 334 right[rightoff + 5] = y2; 335 } 336 } 337 338 static void subdivideQuad(double[] src, int srcoff, 339 double[] left, int leftoff, 340 double[] right, int rightoff) 341 { 342 double x1 = src[srcoff + 0]; 343 double y1 = src[srcoff + 1]; 344 double ctrlx = src[srcoff + 2]; 345 double ctrly = src[srcoff + 3]; 346 double x2 = src[srcoff + 4]; 347 double y2 = src[srcoff + 5]; 348 if (left != null) { 349 left[leftoff + 0] = x1; 350 left[leftoff + 1] = y1; 351 } 352 if (right != null) { 353 right[rightoff + 4] = x2; 354 right[rightoff + 5] = y2; 355 } 356 x1 = (x1 + ctrlx) / 2.0d; 357 y1 = (y1 + ctrly) / 2.0d; 358 x2 = (x2 + ctrlx) / 2.0d; 359 y2 = (y2 + ctrly) / 2.0d; 360 ctrlx = (x1 + x2) / 2.0d; 361 ctrly = (y1 + y2) / 2.0d; 362 if (left != null) { 363 left[leftoff + 2] = x1; 364 left[leftoff + 3] = y1; 365 left[leftoff + 4] = ctrlx; 366 left[leftoff + 5] = ctrly; 367 } 368 if (right != null) { 369 right[rightoff + 0] = ctrlx; 370 right[rightoff + 1] = ctrly; 371 right[rightoff + 2] = x2; 372 right[rightoff + 3] = y2; 373 } 374 } 375 376 static void subdivideQuadAt(double t, double[] src, int srcoff, 377 double[] left, int leftoff, 378 double[] right, int rightoff) 379 { 380 double x1 = src[srcoff + 0]; 381 double y1 = src[srcoff + 1]; 382 double ctrlx = src[srcoff + 2]; 383 double ctrly = src[srcoff + 3]; 384 double x2 = src[srcoff + 4]; 385 double y2 = src[srcoff + 5]; 386 if (left != null) { 387 left[leftoff + 0] = x1; 388 left[leftoff + 1] = y1; 389 } 390 if (right != null) { 391 right[rightoff + 4] = x2; 392 right[rightoff + 5] = y2; 393 } 394 x1 = x1 + t * (ctrlx - x1); 395 y1 = y1 + t * (ctrly - y1); 396 x2 = ctrlx + t * (x2 - ctrlx); 397 y2 = ctrly + t * (y2 - ctrly); 398 ctrlx = x1 + t * (x2 - x1); 399 ctrly = y1 + t * (y2 - y1); 400 if (left != null) { 401 left[leftoff + 2] = x1; 402 left[leftoff + 3] = y1; 403 left[leftoff + 4] = ctrlx; 404 left[leftoff + 5] = ctrly; 405 } 406 if (right != null) { 407 right[rightoff + 0] = ctrlx; 408 right[rightoff + 1] = ctrly; 409 right[rightoff + 2] = x2; 410 right[rightoff + 3] = y2; 411 } 412 } 413 414 static void subdivideAt(double t, double[] src, int srcoff, 415 double[] left, int leftoff, 416 double[] right, int rightoff, int size) 417 { 418 switch(size) { 419 case 8: 420 subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff); 421 return; 422 case 6: 423 subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff); 424 return; 425 } 426 } 427 428 // From sun.java2d.loops.GeneralRenderer: 429 430 static int outcode(final double x, final double y, 431 final double[] clipRect) 432 { 433 int code; 434 if (y < clipRect[0]) { 435 code = OUTCODE_TOP; 436 } else if (y >= clipRect[1]) { 437 code = OUTCODE_BOTTOM; 438 } else { 439 code = 0; 440 } 441 if (x < clipRect[2]) { 442 code |= OUTCODE_LEFT; 443 } else if (x >= clipRect[3]) { 444 code |= OUTCODE_RIGHT; 445 } 446 return code; 447 } 448 449 // a stack of polynomial curves where each curve shares endpoints with 450 // adjacent ones. 451 static final class PolyStack { 452 private static final byte TYPE_LINETO = (byte) 0; 453 private static final byte TYPE_QUADTO = (byte) 1; 454 private static final byte TYPE_CUBICTO = (byte) 2; 455 456 // curves capacity = edges count (8192) = edges x 2 (coords) 457 private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1; 458 459 // types capacity = edges count (4096) 460 private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT; 461 462 double[] curves; 463 int end; 464 byte[] curveTypes; 465 int numCurves; 466 467 // curves ref (dirty) 468 final DoubleArrayCache.Reference curves_ref; 469 // curveTypes ref (dirty) 470 final ByteArrayCache.Reference curveTypes_ref; 471 472 // used marks (stats only) 473 int curveTypesUseMark; 474 int curvesUseMark; 475 476 private final StatLong stat_polystack_types; 477 private final StatLong stat_polystack_curves; 478 private final Histogram hist_polystack_curves; 479 private final StatLong stat_array_polystack_curves; 480 private final StatLong stat_array_polystack_curveTypes; 481 482 PolyStack(final DRendererContext rdrCtx) { 483 this(rdrCtx, null, null, null, null, null); 484 } 485 486 PolyStack(final DRendererContext rdrCtx, 487 final StatLong stat_polystack_types, 488 final StatLong stat_polystack_curves, 489 final Histogram hist_polystack_curves, 490 final StatLong stat_array_polystack_curves, 491 final StatLong stat_array_polystack_curveTypes) 492 { 493 curves_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_CURVES_COUNT); // 32K 494 curves = curves_ref.initial; 495 496 curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K 497 curveTypes = curveTypes_ref.initial; 498 numCurves = 0; 499 end = 0; 500 501 if (DO_STATS) { 502 curveTypesUseMark = 0; 503 curvesUseMark = 0; 504 } 505 this.stat_polystack_types = stat_polystack_types; 506 this.stat_polystack_curves = stat_polystack_curves; 507 this.hist_polystack_curves = hist_polystack_curves; 508 this.stat_array_polystack_curves = stat_array_polystack_curves; 509 this.stat_array_polystack_curveTypes = stat_array_polystack_curveTypes; 510 } 511 512 /** 513 * Disposes this PolyStack: 514 * clean up before reusing this instance 515 */ 516 void dispose() { 517 end = 0; 518 numCurves = 0; 519 520 if (DO_STATS) { 521 stat_polystack_types.add(curveTypesUseMark); 522 stat_polystack_curves.add(curvesUseMark); 523 hist_polystack_curves.add(curvesUseMark); 524 525 // reset marks 526 curveTypesUseMark = 0; 527 curvesUseMark = 0; 528 } 529 530 // Return arrays: 531 // curves and curveTypes are kept dirty 532 curves = curves_ref.putArray(curves); 533 curveTypes = curveTypes_ref.putArray(curveTypes); 534 } 535 536 private void ensureSpace(final int n) { 537 // use substraction to avoid integer overflow: 538 if (curves.length - end < n) { 539 if (DO_STATS) { 540 stat_array_polystack_curves.add(end + n); 541 } 542 curves = curves_ref.widenArray(curves, end, end + n); 543 } 544 if (curveTypes.length <= numCurves) { 545 if (DO_STATS) { 546 stat_array_polystack_curveTypes.add(numCurves + 1); 547 } 548 curveTypes = curveTypes_ref.widenArray(curveTypes, 549 numCurves, 550 numCurves + 1); 551 } 552 } 553 554 void pushCubic(double x0, double y0, 555 double x1, double y1, 556 double x2, double y2) 557 { 558 ensureSpace(6); 559 curveTypes[numCurves++] = TYPE_CUBICTO; 560 // we reverse the coordinate order to make popping easier 561 final double[] _curves = curves; 562 int e = end; 563 _curves[e++] = x2; _curves[e++] = y2; 564 _curves[e++] = x1; _curves[e++] = y1; 565 _curves[e++] = x0; _curves[e++] = y0; 566 end = e; 567 } 568 569 void pushQuad(double x0, double y0, 570 double x1, double y1) 571 { 572 ensureSpace(4); 573 curveTypes[numCurves++] = TYPE_QUADTO; 574 final double[] _curves = curves; 575 int e = end; 576 _curves[e++] = x1; _curves[e++] = y1; 577 _curves[e++] = x0; _curves[e++] = y0; 578 end = e; 579 } 580 581 void pushLine(double x, double y) { 582 ensureSpace(2); 583 curveTypes[numCurves++] = TYPE_LINETO; 584 curves[end++] = x; curves[end++] = y; 585 } 586 587 void pullAll(final DPathConsumer2D io) { 588 final int nc = numCurves; 589 if (nc == 0) { 590 return; 591 } 592 if (DO_STATS) { 593 // update used marks: 594 if (numCurves > curveTypesUseMark) { 595 curveTypesUseMark = numCurves; 596 } 597 if (end > curvesUseMark) { 598 curvesUseMark = end; 599 } 600 } 601 final byte[] _curveTypes = curveTypes; 602 final double[] _curves = curves; 603 int e = 0; 604 605 for (int i = 0; i < nc; i++) { 606 switch(_curveTypes[i]) { 607 case TYPE_LINETO: 608 io.lineTo(_curves[e], _curves[e+1]); 609 e += 2; 610 continue; 611 case TYPE_QUADTO: 612 io.quadTo(_curves[e+0], _curves[e+1], 613 _curves[e+2], _curves[e+3]); 614 e += 4; 615 continue; 616 case TYPE_CUBICTO: 617 io.curveTo(_curves[e+0], _curves[e+1], 618 _curves[e+2], _curves[e+3], 619 _curves[e+4], _curves[e+5]); 620 e += 6; 621 continue; 622 default: 623 } 624 } 625 numCurves = 0; 626 end = 0; 627 } 628 629 void popAll(final DPathConsumer2D io) { 630 int nc = numCurves; 631 if (nc == 0) { 632 return; 633 } 634 if (DO_STATS) { 635 // update used marks: 636 if (numCurves > curveTypesUseMark) { 637 curveTypesUseMark = numCurves; 638 } 639 if (end > curvesUseMark) { 640 curvesUseMark = end; 641 } 642 } 643 final byte[] _curveTypes = curveTypes; 644 final double[] _curves = curves; 645 int e = end; 646 647 while (nc != 0) { 648 switch(_curveTypes[--nc]) { 649 case TYPE_LINETO: 650 e -= 2; 651 io.lineTo(_curves[e], _curves[e+1]); 652 continue; 653 case TYPE_QUADTO: 654 e -= 4; 655 io.quadTo(_curves[e+0], _curves[e+1], 656 _curves[e+2], _curves[e+3]); 657 continue; 658 case TYPE_CUBICTO: 659 e -= 6; 660 io.curveTo(_curves[e+0], _curves[e+1], 661 _curves[e+2], _curves[e+3], 662 _curves[e+4], _curves[e+5]); 663 continue; 664 default: 665 } 666 } 667 numCurves = 0; 668 end = 0; 669 } 670 671 @Override 672 public String toString() { 673 String ret = ""; 674 int nc = numCurves; 675 int last = end; 676 int len; 677 while (nc != 0) { 678 switch(curveTypes[--nc]) { 679 case TYPE_LINETO: 680 len = 2; 681 ret += "line: "; 682 break; 683 case TYPE_QUADTO: 684 len = 4; 685 ret += "quad: "; 686 break; 687 case TYPE_CUBICTO: 688 len = 6; 689 ret += "cubic: "; 690 break; 691 default: 692 len = 0; 693 } 694 last -= len; 695 ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len)) 696 + "\n"; 697 } 698 return ret; 699 } 700 } 701 702 // a stack of integer indices 703 static final class IndexStack { 704 705 // integer capacity = edges count / 4 ~ 1024 706 private static final int INITIAL_COUNT = INITIAL_EDGES_COUNT >> 2; 707 708 private int end; 709 private int[] indices; 710 711 // indices ref (dirty) 712 private final IntArrayCache.Reference indices_ref; 713 714 // used marks (stats only) 715 private int indicesUseMark; 716 717 private final StatLong stat_idxstack_indices; 718 private final Histogram hist_idxstack_indices; 719 private final StatLong stat_array_idxstack_indices; 720 721 IndexStack(final DRendererContext rdrCtx) { 722 this(rdrCtx, null, null, null); 723 } 724 725 IndexStack(final DRendererContext rdrCtx, 726 final StatLong stat_idxstack_indices, 727 final Histogram hist_idxstack_indices, 728 final StatLong stat_array_idxstack_indices) 729 { 730 indices_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_COUNT); // 4K 731 indices = indices_ref.initial; 732 end = 0; 733 734 if (DO_STATS) { 735 indicesUseMark = 0; 736 } 737 this.stat_idxstack_indices = stat_idxstack_indices; 738 this.hist_idxstack_indices = hist_idxstack_indices; 739 this.stat_array_idxstack_indices = stat_array_idxstack_indices; 740 } 741 742 /** 743 * Disposes this PolyStack: 744 * clean up before reusing this instance 745 */ 746 void dispose() { 747 end = 0; 748 749 if (DO_STATS) { 750 stat_idxstack_indices.add(indicesUseMark); 751 hist_idxstack_indices.add(indicesUseMark); 752 753 // reset marks 754 indicesUseMark = 0; 755 } 756 757 // Return arrays: 758 // values is kept dirty 759 indices = indices_ref.putArray(indices); 760 } 761 762 boolean isEmpty() { 763 return (end == 0); 764 } 765 766 void reset() { 767 end = 0; 768 } 769 770 void push(final int v) { 771 // remove redundant values (reverse order): 772 int[] _values = indices; 773 final int nc = end; 774 if (nc != 0) { 775 if (_values[nc - 1] == v) { 776 // remove both duplicated values: 777 end--; 778 return; 779 } 780 } 781 if (_values.length <= nc) { 782 if (DO_STATS) { 783 stat_array_idxstack_indices.add(nc + 1); 784 } 785 indices = _values = indices_ref.widenArray(_values, nc, nc + 1); 786 } 787 _values[end++] = v; 788 789 if (DO_STATS) { 790 // update used marks: 791 if (end > indicesUseMark) { 792 indicesUseMark = end; 793 } 794 } 795 } 796 797 void pullAll(final double[] points, final DPathConsumer2D io) { 798 final int nc = end; 799 if (nc == 0) { 800 return; 801 } 802 final int[] _values = indices; 803 804 for (int i = 0, j; i < nc; i++) { 805 j = _values[i] << 1; 806 io.lineTo(points[j], points[j + 1]); 807 } 808 end = 0; 809 } 810 } 811 }