1 /* 2 * Copyright (c) 2007, 2013, 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.dc; 27 28 import java.awt.Shape; 29 import java.awt.BasicStroke; 30 import java.awt.geom.Path2D; 31 import java.awt.geom.PathIterator; 32 import java.awt.geom.AffineTransform; 33 34 import sun.awt.geom.PathConsumer2D; 35 import sun.java2d.pipe.Region; 36 import sun.java2d.pipe.AATileGenerator; 37 import sun.java2d.pipe.RenderingEngine; 38 39 import sun.dc.pr.Rasterizer; 40 import sun.dc.pr.PathStroker; 41 import sun.dc.pr.PathDasher; 42 import sun.dc.pr.PRException; 43 import sun.dc.path.PathConsumer; 44 import sun.dc.path.PathException; 45 import sun.dc.path.FastPathProducer; 46 47 public class DuctusRenderingEngine extends RenderingEngine { 48 static final float PenUnits = 0.01f; 49 static final int MinPenUnits = 100; 50 static final int MinPenUnitsAA = 20; 51 static final float MinPenSizeAA = PenUnits * MinPenUnitsAA; 52 53 static final float UPPER_BND = Float.MAX_VALUE / 2.0f; 54 static final float LOWER_BND = -UPPER_BND; 55 56 private static final int RasterizerCaps[] = { 57 Rasterizer.BUTT, Rasterizer.ROUND, Rasterizer.SQUARE 58 }; 59 60 private static final int RasterizerCorners[] = { 61 Rasterizer.MITER, Rasterizer.ROUND, Rasterizer.BEVEL 62 }; 63 64 static float[] getTransformMatrix(AffineTransform transform) { 65 float matrix[] = new float[4]; 66 double dmatrix[] = new double[6]; 67 transform.getMatrix(dmatrix); 68 for (int i = 0; i < 4; i++) { 69 matrix[i] = (float) dmatrix[i]; 70 } 71 return matrix; 72 } 73 74 /** 75 * {@inheritDoc} 76 */ 77 @Override 78 public Shape createStrokedShape(Shape src, 79 float width, 80 int caps, 81 int join, 82 float miterlimit, 83 float dashes[], 84 float dashphase) 85 { 86 FillAdapter filler = new FillAdapter(); 87 PathStroker stroker = new PathStroker(filler); 88 PathDasher dasher = null; 89 90 try { 91 PathConsumer consumer; 92 93 stroker.setPenDiameter(width); 94 stroker.setPenT4(null); 95 stroker.setCaps(RasterizerCaps[caps]); 96 stroker.setCorners(RasterizerCorners[join], miterlimit); 97 if (dashes != null) { 98 dasher = new PathDasher(stroker); 99 dasher.setDash(dashes, dashphase); 100 dasher.setDashT4(null); 101 consumer = dasher; 102 } else { 103 consumer = stroker; 104 } 105 106 feedConsumer(consumer, src.getPathIterator(null)); 107 } finally { 108 stroker.dispose(); 109 if (dasher != null) { 110 dasher.dispose(); 111 } 112 } 113 114 return filler.getShape(); 115 } 116 117 /** 118 * {@inheritDoc} 119 */ 120 @Override 121 public void strokeTo(Shape src, 122 AffineTransform transform, 123 BasicStroke bs, 124 boolean thin, 125 boolean normalize, 126 boolean antialias, 127 PathConsumer2D sr) 128 { 129 PathStroker stroker = new PathStroker(sr); 130 PathConsumer consumer = stroker; 131 132 float matrix[] = null; 133 if (!thin) { 134 stroker.setPenDiameter(bs.getLineWidth()); 135 if (transform != null) { 136 matrix = getTransformMatrix(transform); 137 } 138 stroker.setPenT4(matrix); 139 stroker.setPenFitting(PenUnits, MinPenUnits); 140 } 141 stroker.setCaps(RasterizerCaps[bs.getEndCap()]); 142 stroker.setCorners(RasterizerCorners[bs.getLineJoin()], 143 bs.getMiterLimit()); 144 float[] dashes = bs.getDashArray(); 145 if (dashes != null) { 146 PathDasher dasher = new PathDasher(stroker); 147 dasher.setDash(dashes, bs.getDashPhase()); 148 if (transform != null && matrix == null) { 149 matrix = getTransformMatrix(transform); 150 } 151 dasher.setDashT4(matrix); 152 consumer = dasher; 153 } 154 155 try { 156 PathIterator pi = src.getPathIterator(transform); 157 158 feedConsumer(pi, consumer, normalize, 0.25f); 159 } catch (PathException e) { 160 throw new InternalError("Unable to Stroke shape ("+ 161 e.getMessage()+")", e); 162 } finally { 163 while (consumer != null && consumer != sr) { 164 PathConsumer next = consumer.getConsumer(); 165 consumer.dispose(); 166 consumer = next; 167 } 168 } 169 } 170 171 /* 172 * Feed a path from a PathIterator to a Ductus PathConsumer. 173 */ 174 public static void feedConsumer(PathIterator pi, PathConsumer consumer, 175 boolean normalize, float norm) 176 throws PathException 177 { 178 consumer.beginPath(); 179 boolean pathClosed = false; 180 boolean skip = false; 181 boolean subpathStarted = false; 182 float mx = 0.0f; 183 float my = 0.0f; 184 float point[] = new float[6]; 185 float rnd = (0.5f - norm); 186 float ax = 0.0f; 187 float ay = 0.0f; 188 189 while (!pi.isDone()) { 190 int type = pi.currentSegment(point); 191 if (pathClosed == true) { 192 pathClosed = false; 193 if (type != PathIterator.SEG_MOVETO) { 194 // Force current point back to last moveto point 195 consumer.beginSubpath(mx, my); 196 subpathStarted = true; 197 } 198 } 199 if (normalize) { 200 int index; 201 switch (type) { 202 case PathIterator.SEG_CUBICTO: 203 index = 4; 204 break; 205 case PathIterator.SEG_QUADTO: 206 index = 2; 207 break; 208 case PathIterator.SEG_MOVETO: 209 case PathIterator.SEG_LINETO: 210 index = 0; 211 break; 212 case PathIterator.SEG_CLOSE: 213 default: 214 index = -1; 215 break; 216 } 217 if (index >= 0) { 218 float ox = point[index]; 219 float oy = point[index+1]; 220 float newax = (float) Math.floor(ox + rnd) + norm; 221 float neway = (float) Math.floor(oy + rnd) + norm; 222 point[index] = newax; 223 point[index+1] = neway; 224 newax -= ox; 225 neway -= oy; 226 switch (type) { 227 case PathIterator.SEG_CUBICTO: 228 point[0] += ax; 229 point[1] += ay; 230 point[2] += newax; 231 point[3] += neway; 232 break; 233 case PathIterator.SEG_QUADTO: 234 point[0] += (newax + ax) / 2; 235 point[1] += (neway + ay) / 2; 236 break; 237 case PathIterator.SEG_MOVETO: 238 case PathIterator.SEG_LINETO: 239 case PathIterator.SEG_CLOSE: 240 break; 241 } 242 ax = newax; 243 ay = neway; 244 } 245 } 246 switch (type) { 247 case PathIterator.SEG_MOVETO: 248 249 /* Checking SEG_MOVETO coordinates if they are out of the 250 * [LOWER_BND, UPPER_BND] range. This check also handles NaN 251 * and Infinity values. Skipping next path segment in case of 252 * invalid data. 253 */ 254 if (point[0] < UPPER_BND && point[0] > LOWER_BND && 255 point[1] < UPPER_BND && point[1] > LOWER_BND) 256 { 257 mx = point[0]; 258 my = point[1]; 259 consumer.beginSubpath(mx, my); 260 subpathStarted = true; 261 skip = false; 262 } else { 263 skip = true; 264 } 265 break; 266 case PathIterator.SEG_LINETO: 267 /* Checking SEG_LINETO coordinates if they are out of the 268 * [LOWER_BND, UPPER_BND] range. This check also handles NaN 269 * and Infinity values. Ignoring current path segment in case 270 * of invalid data. If segment is skipped its endpoint 271 * (if valid) is used to begin new subpath. 272 */ 273 if (point[0] < UPPER_BND && point[0] > LOWER_BND && 274 point[1] < UPPER_BND && point[1] > LOWER_BND) 275 { 276 if (skip) { 277 consumer.beginSubpath(point[0], point[1]); 278 subpathStarted = true; 279 skip = false; 280 } else { 281 consumer.appendLine(point[0], point[1]); 282 } 283 } 284 break; 285 case PathIterator.SEG_QUADTO: 286 // Quadratic curves take two points 287 288 /* Checking SEG_QUADTO coordinates if they are out of the 289 * [LOWER_BND, UPPER_BND] range. This check also handles NaN 290 * and Infinity values. Ignoring current path segment in case 291 * of invalid endpoints's data. Equivalent to the SEG_LINETO 292 * if endpoint coordinates are valid but there are invalid data 293 * among other coordinates 294 */ 295 if (point[2] < UPPER_BND && point[2] > LOWER_BND && 296 point[3] < UPPER_BND && point[3] > LOWER_BND) 297 { 298 if (skip) { 299 consumer.beginSubpath(point[2], point[3]); 300 subpathStarted = true; 301 skip = false; 302 } else { 303 if (point[0] < UPPER_BND && point[0] > LOWER_BND && 304 point[1] < UPPER_BND && point[1] > LOWER_BND) 305 { 306 consumer.appendQuadratic(point[0], point[1], 307 point[2], point[3]); 308 } else { 309 consumer.appendLine(point[2], point[3]); 310 } 311 } 312 } 313 break; 314 case PathIterator.SEG_CUBICTO: 315 // Cubic curves take three points 316 317 /* Checking SEG_CUBICTO coordinates if they are out of the 318 * [LOWER_BND, UPPER_BND] range. This check also handles NaN 319 * and Infinity values. Ignoring current path segment in case 320 * of invalid endpoints's data. Equivalent to the SEG_LINETO 321 * if endpoint coordinates are valid but there are invalid data 322 * among other coordinates 323 */ 324 if (point[4] < UPPER_BND && point[4] > LOWER_BND && 325 point[5] < UPPER_BND && point[5] > LOWER_BND) 326 { 327 if (skip) { 328 consumer.beginSubpath(point[4], point[5]); 329 subpathStarted = true; 330 skip = false; 331 } else { 332 if (point[0] < UPPER_BND && point[0] > LOWER_BND && 333 point[1] < UPPER_BND && point[1] > LOWER_BND && 334 point[2] < UPPER_BND && point[2] > LOWER_BND && 335 point[3] < UPPER_BND && point[3] > LOWER_BND) 336 { 337 consumer.appendCubic(point[0], point[1], 338 point[2], point[3], 339 point[4], point[5]); 340 } else { 341 consumer.appendLine(point[4], point[5]); 342 } 343 } 344 } 345 break; 346 case PathIterator.SEG_CLOSE: 347 if (subpathStarted) { 348 consumer.closedSubpath(); 349 subpathStarted = false; 350 pathClosed = true; 351 } 352 break; 353 } 354 pi.next(); 355 } 356 357 consumer.endPath(); 358 } 359 360 private static Rasterizer theRasterizer; 361 362 public synchronized static Rasterizer getRasterizer() { 363 Rasterizer r = theRasterizer; 364 if (r == null) { 365 r = new Rasterizer(); 366 } else { 367 theRasterizer = null; 368 } 369 return r; 370 } 371 372 public synchronized static void dropRasterizer(Rasterizer r) { 373 r.reset(); 374 theRasterizer = r; 375 } 376 377 /** 378 * {@inheritDoc} 379 */ 380 @Override 381 public float getMinimumAAPenSize() { 382 return MinPenSizeAA; 383 } 384 385 /** 386 * {@inheritDoc} 387 */ 388 @Override 389 public AATileGenerator getAATileGenerator(Shape s, 390 AffineTransform at, 391 Region clip, 392 BasicStroke bs, 393 boolean thin, 394 boolean normalize, 395 int bbox[]) 396 { 397 Rasterizer r = getRasterizer(); 398 PathIterator pi = s.getPathIterator(at); 399 400 if (bs != null) { 401 float matrix[] = null; 402 r.setUsage(Rasterizer.STROKE); 403 if (thin) { 404 r.setPenDiameter(MinPenSizeAA); 405 } else { 406 r.setPenDiameter(bs.getLineWidth()); 407 if (at != null) { 408 matrix = getTransformMatrix(at); 409 r.setPenT4(matrix); 410 } 411 r.setPenFitting(PenUnits, MinPenUnitsAA); 412 } 413 r.setCaps(RasterizerCaps[bs.getEndCap()]); 414 r.setCorners(RasterizerCorners[bs.getLineJoin()], 415 bs.getMiterLimit()); 416 float[] dashes = bs.getDashArray(); 417 if (dashes != null) { 418 r.setDash(dashes, bs.getDashPhase()); 419 if (at != null && matrix == null) { 420 matrix = getTransformMatrix(at); 421 } 422 r.setDashT4(matrix); 423 } 424 } else { 425 r.setUsage(pi.getWindingRule() == PathIterator.WIND_EVEN_ODD 426 ? Rasterizer.EOFILL 427 : Rasterizer.NZFILL); 428 } 429 430 r.beginPath(); 431 { 432 boolean pathClosed = false; 433 boolean skip = false; 434 boolean subpathStarted = false; 435 float mx = 0.0f; 436 float my = 0.0f; 437 float point[] = new float[6]; 438 float ax = 0.0f; 439 float ay = 0.0f; 440 441 while (!pi.isDone()) { 442 int type = pi.currentSegment(point); 443 if (pathClosed == true) { 444 pathClosed = false; 445 if (type != PathIterator.SEG_MOVETO) { 446 // Force current point back to last moveto point 447 r.beginSubpath(mx, my); 448 subpathStarted = true; 449 } 450 } 451 if (normalize) { 452 int index; 453 switch (type) { 454 case PathIterator.SEG_CUBICTO: 455 index = 4; 456 break; 457 case PathIterator.SEG_QUADTO: 458 index = 2; 459 break; 460 case PathIterator.SEG_MOVETO: 461 case PathIterator.SEG_LINETO: 462 index = 0; 463 break; 464 case PathIterator.SEG_CLOSE: 465 default: 466 index = -1; 467 break; 468 } 469 if (index >= 0) { 470 float ox = point[index]; 471 float oy = point[index+1]; 472 float newax = (float) Math.floor(ox) + 0.5f; 473 float neway = (float) Math.floor(oy) + 0.5f; 474 point[index] = newax; 475 point[index+1] = neway; 476 newax -= ox; 477 neway -= oy; 478 switch (type) { 479 case PathIterator.SEG_CUBICTO: 480 point[0] += ax; 481 point[1] += ay; 482 point[2] += newax; 483 point[3] += neway; 484 break; 485 case PathIterator.SEG_QUADTO: 486 point[0] += (newax + ax) / 2; 487 point[1] += (neway + ay) / 2; 488 break; 489 case PathIterator.SEG_MOVETO: 490 case PathIterator.SEG_LINETO: 491 case PathIterator.SEG_CLOSE: 492 break; 493 } 494 ax = newax; 495 ay = neway; 496 } 497 } 498 switch (type) { 499 case PathIterator.SEG_MOVETO: 500 501 /* Checking SEG_MOVETO coordinates if they are out of the 502 * [LOWER_BND, UPPER_BND] range. This check also handles NaN 503 * and Infinity values. Skipping next path segment in case 504 * of invalid data. 505 */ 506 507 if (point[0] < UPPER_BND && point[0] > LOWER_BND && 508 point[1] < UPPER_BND && point[1] > LOWER_BND) 509 { 510 mx = point[0]; 511 my = point[1]; 512 r.beginSubpath(mx, my); 513 subpathStarted = true; 514 skip = false; 515 } else { 516 skip = true; 517 } 518 break; 519 520 case PathIterator.SEG_LINETO: 521 /* Checking SEG_LINETO coordinates if they are out of the 522 * [LOWER_BND, UPPER_BND] range. This check also handles 523 * NaN and Infinity values. Ignoring current path segment 524 * in case of invalid data. If segment is skipped its 525 * endpoint (if valid) is used to begin new subpath. 526 */ 527 if (point[0] < UPPER_BND && point[0] > LOWER_BND && 528 point[1] < UPPER_BND && point[1] > LOWER_BND) 529 { 530 if (skip) { 531 r.beginSubpath(point[0], point[1]); 532 subpathStarted = true; 533 skip = false; 534 } else { 535 r.appendLine(point[0], point[1]); 536 } 537 } 538 break; 539 540 case PathIterator.SEG_QUADTO: 541 // Quadratic curves take two points 542 543 /* Checking SEG_QUADTO coordinates if they are out of the 544 * [LOWER_BND, UPPER_BND] range. This check also handles 545 * NaN and Infinity values. Ignoring current path segment 546 * in case of invalid endpoints's data. Equivalent to the 547 * SEG_LINETO if endpoint coordinates are valid but there 548 * are invalid data among other coordinates 549 */ 550 if (point[2] < UPPER_BND && point[2] > LOWER_BND && 551 point[3] < UPPER_BND && point[3] > LOWER_BND) 552 { 553 if (skip) { 554 r.beginSubpath(point[2], point[3]); 555 subpathStarted = true; 556 skip = false; 557 } else { 558 if (point[0] < UPPER_BND && point[0] > LOWER_BND && 559 point[1] < UPPER_BND && point[1] > LOWER_BND) 560 { 561 r.appendQuadratic(point[0], point[1], 562 point[2], point[3]); 563 } else { 564 r.appendLine(point[2], point[3]); 565 } 566 } 567 } 568 break; 569 case PathIterator.SEG_CUBICTO: 570 // Cubic curves take three points 571 572 /* Checking SEG_CUBICTO coordinates if they are out of the 573 * [LOWER_BND, UPPER_BND] range. This check also handles 574 * NaN and Infinity values. Ignoring current path segment 575 * in case of invalid endpoints's data. Equivalent to the 576 * SEG_LINETO if endpoint coordinates are valid but there 577 * are invalid data among other coordinates 578 */ 579 580 if (point[4] < UPPER_BND && point[4] > LOWER_BND && 581 point[5] < UPPER_BND && point[5] > LOWER_BND) 582 { 583 if (skip) { 584 r.beginSubpath(point[4], point[5]); 585 subpathStarted = true; 586 skip = false; 587 } else { 588 if (point[0] < UPPER_BND && point[0] > LOWER_BND && 589 point[1] < UPPER_BND && point[1] > LOWER_BND && 590 point[2] < UPPER_BND && point[2] > LOWER_BND && 591 point[3] < UPPER_BND && point[3] > LOWER_BND) 592 { 593 r.appendCubic(point[0], point[1], 594 point[2], point[3], 595 point[4], point[5]); 596 } else { 597 r.appendLine(point[4], point[5]); 598 } 599 } 600 } 601 break; 602 case PathIterator.SEG_CLOSE: 603 if (subpathStarted) { 604 r.closedSubpath(); 605 subpathStarted = false; 606 pathClosed = true; 607 } 608 break; 609 } 610 pi.next(); 611 } 612 } 613 614 try { 615 r.endPath(); 616 r.getAlphaBox(bbox); 617 clip.clipBoxToBounds(bbox); 618 if (bbox[0] >= bbox[2] || bbox[1] >= bbox[3]) { 619 dropRasterizer(r); 620 return null; 621 } 622 r.setOutputArea(bbox[0], bbox[1], 623 bbox[2] - bbox[0], 624 bbox[3] - bbox[1]); 625 } catch (PRException e) { 626 /* 627 * This exeption is thrown from the native part of the Ductus 628 * (only in case of a debug build) to indicate that some 629 * segments of the path have very large coordinates. 630 * See 4485298 for more info. 631 */ 632 System.err.println("DuctusRenderingEngine.getAATileGenerator: "+e); 633 } 634 635 return r; 636 } 637 638 /** 639 * {@inheritDoc} 640 */ 641 @Override 642 public AATileGenerator getAATileGenerator(double x, double y, 643 double dx1, double dy1, 644 double dx2, double dy2, 645 double lw1, double lw2, 646 Region clip, 647 int bbox[]) 648 { 649 // REMIND: Deal with large coordinates! 650 double ldx1, ldy1, ldx2, ldy2; 651 boolean innerpgram = (lw1 > 0 && lw2 > 0); 652 653 if (innerpgram) { 654 ldx1 = dx1 * lw1; 655 ldy1 = dy1 * lw1; 656 ldx2 = dx2 * lw2; 657 ldy2 = dy2 * lw2; 658 x -= (ldx1 + ldx2) / 2.0; 659 y -= (ldy1 + ldy2) / 2.0; 660 dx1 += ldx1; 661 dy1 += ldy1; 662 dx2 += ldx2; 663 dy2 += ldy2; 664 if (lw1 > 1 && lw2 > 1) { 665 // Inner parallelogram was entirely consumed by stroke... 666 innerpgram = false; 667 } 668 } else { 669 ldx1 = ldy1 = ldx2 = ldy2 = 0; 670 } 671 672 Rasterizer r = getRasterizer(); 673 674 r.setUsage(Rasterizer.EOFILL); 675 676 r.beginPath(); 677 r.beginSubpath((float) x, (float) y); 678 r.appendLine((float) (x+dx1), (float) (y+dy1)); 679 r.appendLine((float) (x+dx1+dx2), (float) (y+dy1+dy2)); 680 r.appendLine((float) (x+dx2), (float) (y+dy2)); 681 r.closedSubpath(); 682 if (innerpgram) { 683 x += ldx1 + ldx2; 684 y += ldy1 + ldy2; 685 dx1 -= 2.0 * ldx1; 686 dy1 -= 2.0 * ldy1; 687 dx2 -= 2.0 * ldx2; 688 dy2 -= 2.0 * ldy2; 689 r.beginSubpath((float) x, (float) y); 690 r.appendLine((float) (x+dx1), (float) (y+dy1)); 691 r.appendLine((float) (x+dx1+dx2), (float) (y+dy1+dy2)); 692 r.appendLine((float) (x+dx2), (float) (y+dy2)); 693 r.closedSubpath(); 694 } 695 696 try { 697 r.endPath(); 698 r.getAlphaBox(bbox); 699 clip.clipBoxToBounds(bbox); 700 if (bbox[0] >= bbox[2] || bbox[1] >= bbox[3]) { 701 dropRasterizer(r); 702 return null; 703 } 704 r.setOutputArea(bbox[0], bbox[1], 705 bbox[2] - bbox[0], 706 bbox[3] - bbox[1]); 707 } catch (PRException e) { 708 /* 709 * This exeption is thrown from the native part of the Ductus 710 * (only in case of a debug build) to indicate that some 711 * segments of the path have very large coordinates. 712 * See 4485298 for more info. 713 */ 714 System.err.println("DuctusRenderingEngine.getAATileGenerator: "+e); 715 } 716 717 return r; 718 } 719 720 private void feedConsumer(PathConsumer consumer, PathIterator pi) { 721 try { 722 consumer.beginPath(); 723 boolean pathClosed = false; 724 float mx = 0.0f; 725 float my = 0.0f; 726 float point[] = new float[6]; 727 728 while (!pi.isDone()) { 729 int type = pi.currentSegment(point); 730 if (pathClosed == true) { 731 pathClosed = false; 732 if (type != PathIterator.SEG_MOVETO) { 733 // Force current point back to last moveto point 734 consumer.beginSubpath(mx, my); 735 } 736 } 737 switch (type) { 738 case PathIterator.SEG_MOVETO: 739 mx = point[0]; 740 my = point[1]; 741 consumer.beginSubpath(point[0], point[1]); 742 break; 743 case PathIterator.SEG_LINETO: 744 consumer.appendLine(point[0], point[1]); 745 break; 746 case PathIterator.SEG_QUADTO: 747 consumer.appendQuadratic(point[0], point[1], 748 point[2], point[3]); 749 break; 750 case PathIterator.SEG_CUBICTO: 751 consumer.appendCubic(point[0], point[1], 752 point[2], point[3], 753 point[4], point[5]); 754 break; 755 case PathIterator.SEG_CLOSE: 756 consumer.closedSubpath(); 757 pathClosed = true; 758 break; 759 } 760 pi.next(); 761 } 762 763 consumer.endPath(); 764 } catch (PathException e) { 765 throw new InternalError("Unable to Stroke shape ("+ 766 e.getMessage()+")", e); 767 } 768 } 769 770 private class FillAdapter implements PathConsumer { 771 boolean closed; 772 Path2D.Float path; 773 774 public FillAdapter() { 775 // Ductus only supplies float coordinates so 776 // Path2D.Double is not necessary here. 777 path = new Path2D.Float(Path2D.WIND_NON_ZERO); 778 } 779 780 public Shape getShape() { 781 return path; 782 } 783 784 public void dispose() { 785 } 786 787 public PathConsumer getConsumer() { 788 return null; 789 } 790 791 public void beginPath() {} 792 793 public void beginSubpath(float x0, float y0) { 794 if (closed) { 795 path.closePath(); 796 closed = false; 797 } 798 path.moveTo(x0, y0); 799 } 800 801 public void appendLine(float x1, float y1) { 802 path.lineTo(x1, y1); 803 } 804 805 public void appendQuadratic(float xm, float ym, float x1, float y1) { 806 path.quadTo(xm, ym, x1, y1); 807 } 808 809 public void appendCubic(float xm, float ym, 810 float xn, float yn, 811 float x1, float y1) { 812 path.curveTo(xm, ym, xn, yn, x1, y1); 813 } 814 815 public void closedSubpath() { 816 closed = true; 817 } 818 819 public void endPath() { 820 if (closed) { 821 path.closePath(); 822 closed = false; 823 } 824 } 825 826 public void useProxy(FastPathProducer proxy) 827 throws PathException 828 { 829 proxy.sendTo(this); 830 } 831 832 public long getCPathConsumer() { 833 return 0; 834 } 835 } 836 }