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 }