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