< prev index next >

src/java.desktop/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java

Print this page




  34 import static sun.java2d.marlin.MarlinUtils.logInfo;
  35 import sun.awt.geom.PathConsumer2D;
  36 import sun.java2d.ReentrantContextProvider;
  37 import sun.java2d.ReentrantContextProviderCLQ;
  38 import sun.java2d.ReentrantContextProviderTL;
  39 import sun.java2d.pipe.AATileGenerator;
  40 import sun.java2d.pipe.Region;
  41 import sun.java2d.pipe.RenderingEngine;
  42 import sun.security.action.GetPropertyAction;
  43 
  44 /**
  45  * Marlin RendererEngine implementation (derived from Pisces)
  46  */
  47 public class MarlinRenderingEngine extends RenderingEngine
  48                                    implements MarlinConst
  49 {
  50     private static enum NormMode {ON_WITH_AA, ON_NO_AA, OFF}
  51 
  52     private static final float MIN_PEN_SIZE = 1f / NORM_SUBPIXELS;
  53 



  54     /**
  55      * Public constructor
  56      */
  57     public MarlinRenderingEngine() {
  58         super();
  59         logSettings(MarlinRenderingEngine.class.getName());
  60     }
  61 
  62     /**
  63      * Create a widened path as specified by the parameters.
  64      * <p>
  65      * The specified {@code src} {@link Shape} is widened according
  66      * to the specified attribute parameters as per the
  67      * {@link BasicStroke} specification.
  68      *
  69      * @param src the source path to be widened
  70      * @param width the width of the widened path as per {@code BasicStroke}
  71      * @param caps the end cap decorations as per {@code BasicStroke}
  72      * @param join the segment join decorations as per {@code BasicStroke}
  73      * @param miterlimit the miter limit as per {@code BasicStroke}


 262             double widthsquared = ((EA + EC + hypot)/2.0);
 263 
 264             widthScale = (float)Math.sqrt(widthsquared);
 265         }
 266 
 267         return (lw / widthScale);
 268     }
 269 
 270     final void strokeTo(final RendererContext rdrCtx,
 271                         Shape src,
 272                         AffineTransform at,
 273                         float width,
 274                         NormMode normalize,
 275                         int caps,
 276                         int join,
 277                         float miterlimit,
 278                         float dashes[],
 279                         float dashphase,
 280                         PathConsumer2D pc2d)
 281     {
 282         // We use strokerat and outat so that in Stroker and Dasher we can work only
 283         // with the pre-transformation coordinates. This will repeat a lot of
 284         // computations done in the path iterator, but the alternative is to
 285         // work with transformed paths and compute untransformed coordinates
 286         // as needed. This would be faster but I do not think the complexity
 287         // of working with both untransformed and transformed coordinates in
 288         // the same code is worth it.
 289         // However, if a path's width is constant after a transformation,
 290         // we can skip all this untransforming.
 291 
 292         // If normalization is off we save some transformations by not
 293         // transforming the input to pisces. Instead, we apply the
 294         // transformation after the path processing has been done.
 295         // We can't do this if normalization is on, because it isn't a good
 296         // idea to normalize before the transformation is applied.
 297         AffineTransform strokerat = null;
 298         AffineTransform outat = null;
 299 
 300         PathIterator pi;
 301         int dashLen = -1;
 302         boolean recycleDashes = false;
 303 
 304         if (at != null && !at.isIdentity()) {
 305             final double a = at.getScaleX();
 306             final double b = at.getShearX();
 307             final double c = at.getShearY();
 308             final double d = at.getScaleY();
 309             final double det = a * d - c * b;
 310 
 311             if (Math.abs(det) <= (2f * Float.MIN_VALUE)) {
 312                 // this rendering engine takes one dimensional curves and turns
 313                 // them into 2D shapes by giving them width.
 314                 // However, if everything is to be passed through a singular
 315                 // transformation, these 2D shapes will be squashed down to 1D
 316                 // again so, nothing can be drawn.
 317 
 318                 // Every path needs an initial moveTo and a pathDone. If these
 319                 // are not there this causes a SIGSEGV in libawt.so (at the time
 320                 // of writing of this comment (September 16, 2010)). Actually,
 321                 // I am not sure if the moveTo is necessary to avoid the SIGSEGV
 322                 // but the pathDone is definitely needed.
 323                 pc2d.moveTo(0f, 0f);
 324                 pc2d.pathDone();
 325                 return;
 326             }
 327 
 328             // If the transform is a constant multiple of an orthogonal transformation
 329             // then every length is just multiplied by a constant, so we just
 330             // need to transform input paths to stroker and tell stroker
 331             // the scaled width. This condition is satisfied if
 332             // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
 333             // leave a bit of room for error.
 334             if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
 335                 final float scale = (float) Math.sqrt(a*a + c*c);

 336                 if (dashes != null) {
 337                     recycleDashes = true;
 338                     dashLen = dashes.length;
 339                     final float[] newDashes;
 340                     if (dashLen <= INITIAL_ARRAY) {
 341                         newDashes = rdrCtx.dasher.dashes_initial;
 342                     } else {
 343                         if (doStats) {
 344                             RendererContext.stats.stat_array_dasher_dasher
 345                                 .add(dashLen);
 346                         }
 347                         newDashes = rdrCtx.getDirtyFloatArray(dashLen);
 348                     }
 349                     System.arraycopy(dashes, 0, newDashes, 0, dashLen);
 350                     dashes = newDashes;
 351                     for (int i = 0; i < dashLen; i++) {
 352                         dashes[i] = scale * dashes[i];
 353                     }
 354                     dashphase = scale * dashphase;
 355                 }
 356                 width = scale * width;
 357                 pi = getNormalizingPathIterator(rdrCtx, normalize,
 358                                                 src.getPathIterator(at));
 359 
 360                 // by now strokerat == null && outat == null. Input paths to
 361                 // stroker (and maybe dasher) will have the full transform at
 362                 // applied to them and nothing will happen to the output paths.
 363             } else {
 364                 if (normalize != NormMode.OFF) {
 365                     strokerat = at;
 366                     pi = getNormalizingPathIterator(rdrCtx, normalize,
 367                                                     src.getPathIterator(at));
 368 
 369                     // by now strokerat == at && outat == null. Input paths to
 370                     // stroker (and maybe dasher) will have the full transform at
 371                     // applied to them, then they will be normalized, and then
 372                     // the inverse of *only the non translation part of at* will
 373                     // be applied to the normalized paths. This won't cause problems
 374                     // in stroker, because, suppose at = T*A, where T is just the
 375                     // translation part of at, and A is the rest. T*A has already
 376                     // been applied to Stroker/Dasher's input. Then Ainv will be
 377                     // applied. Ainv*T*A is not equal to T, but it is a translation,
 378                     // which means that none of stroker's assumptions about its
 379                     // input will be violated. After all this, A will be applied
 380                     // to stroker's output.
 381                 } else {
 382                     outat = at;
 383                     pi = src.getPathIterator(null);
 384                     // outat == at && strokerat == null. This is because if no
 385                     // normalization is done, we can just apply all our
 386                     // transformations to stroker's output.
 387                 }
 388             }
 389         } else {
 390             // either at is null or it's the identity. In either case
 391             // we don't transform the path.
 392             pi = getNormalizingPathIterator(rdrCtx, normalize,
 393                                             src.getPathIterator(null));
 394         }
 395 
 396         if (useSimplifier) {
 397             // Use simplifier after stroker before Renderer
 398             // to remove collinear segments (notably due to cap square)
 399             pc2d = rdrCtx.simplifier.init(pc2d);
 400         }
 401 
 402         // by now, at least one of outat and strokerat will be null. Unless at is not
 403         // a constant multiple of an orthogonal transformation, they will both be
 404         // null. In other cases, outat == at if normalization is off, and if
 405         // normalization is on, strokerat == at.
 406         final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
 407         pc2d = transformerPC2D.transformConsumer(pc2d, outat);
 408         pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
 409 
 410         pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit);
 411 
 412         if (dashes != null) {
 413             if (!recycleDashes) {
 414                 dashLen = dashes.length;
 415             }
 416             pc2d = rdrCtx.dasher.init(pc2d, dashes, dashLen, dashphase,
 417                                       recycleDashes);
 418         }
 419         pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);




 420         pathTo(rdrCtx, pi, pc2d);
 421 
 422         /*
 423          * Pipeline seems to be:
 424          *    shape.getPathIterator
 425          * -> NormalizingPathIterator
 426          * -> inverseDeltaTransformConsumer
 427          * -> Dasher
 428          * -> Stroker
 429          * -> deltaTransformConsumer OR transformConsumer
 430          *
 431          * -> CollinearSimplifier to remove redundant segments
 432          *
 433          * -> pc2d = Renderer (bounding box)
 434          */
 435     }
 436 
 437     private static boolean nearZero(final double num) {
 438         return Math.abs(num) < 2.0 * Math.ulp(num);
 439     }
 440 
 441     PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
 442                                             final NormMode mode,
 443                                             final PathIterator src)
 444     {
 445         switch (mode) {
 446             case ON_WITH_AA:
 447                 // NormalizingPathIterator NearestPixelCenter:
 448                 return rdrCtx.nPCPathIterator.init(src);
 449             case ON_NO_AA:
 450                 // NearestPixel NormalizingPathIterator:
 451                 return rdrCtx.nPQPathIterator.init(src);


 625         }
 626     }
 627 
 628     private static void pathTo(final RendererContext rdrCtx, final PathIterator pi,
 629                                final PathConsumer2D pc2d)
 630     {
 631         // mark context as DIRTY:
 632         rdrCtx.dirty = true;
 633 
 634         final float[] coords = rdrCtx.float6;
 635 
 636         pathToLoop(coords, pi, pc2d);
 637 
 638         // mark context as CLEAN:
 639         rdrCtx.dirty = false;
 640     }
 641 
 642     private static void pathToLoop(final float[] coords, final PathIterator pi,
 643                                    final PathConsumer2D pc2d)
 644     {






 645         for (; !pi.isDone(); pi.next()) {
 646             switch (pi.currentSegment(coords)) {
 647                 case PathIterator.SEG_MOVETO:








 648                     pc2d.moveTo(coords[0], coords[1]);
 649                     continue;
 650                 case PathIterator.SEG_LINETO:
 651                     pc2d.lineTo(coords[0], coords[1]);
 652                     continue;
 653                 case PathIterator.SEG_QUADTO:
 654                     pc2d.quadTo(coords[0], coords[1],
 655                                 coords[2], coords[3]);
 656                     continue;
 657                 case PathIterator.SEG_CUBICTO:
 658                     pc2d.curveTo(coords[0], coords[1],
 659                                  coords[2], coords[3],
 660                                  coords[4], coords[5]);
 661                     continue;
 662                 case PathIterator.SEG_CLOSE:


































































 663                     pc2d.closePath();
 664                     continue;
 665                 default:


 666             }
 667         }
 668         pc2d.pathDone();
 669     }
 670 
 671     /**
 672      * Construct an antialiased tile generator for the given shape with
 673      * the given rendering attributes and store the bounds of the tile
 674      * iteration in the bbox parameter.
 675      * The {@code at} parameter specifies a transform that should affect
 676      * both the shape and the {@code BasicStroke} attributes.
 677      * The {@code clip} parameter specifies the current clip in effect
 678      * in device coordinates and can be used to prune the data for the
 679      * operation, but the renderer is not required to perform any
 680      * clipping.
 681      * If the {@code BasicStroke} parameter is null then the shape
 682      * should be filled as is, otherwise the attributes of the
 683      * {@code BasicStroke} should be used to specify a draw operation.
 684      * The {@code thin} parameter indicates whether or not the
 685      * transformed {@code BasicStroke} represents coordinates smaller




  34 import static sun.java2d.marlin.MarlinUtils.logInfo;
  35 import sun.awt.geom.PathConsumer2D;
  36 import sun.java2d.ReentrantContextProvider;
  37 import sun.java2d.ReentrantContextProviderCLQ;
  38 import sun.java2d.ReentrantContextProviderTL;
  39 import sun.java2d.pipe.AATileGenerator;
  40 import sun.java2d.pipe.Region;
  41 import sun.java2d.pipe.RenderingEngine;
  42 import sun.security.action.GetPropertyAction;
  43 
  44 /**
  45  * Marlin RendererEngine implementation (derived from Pisces)
  46  */
  47 public class MarlinRenderingEngine extends RenderingEngine
  48                                    implements MarlinConst
  49 {
  50     private static enum NormMode {ON_WITH_AA, ON_NO_AA, OFF}
  51 
  52     private static final float MIN_PEN_SIZE = 1f / NORM_SUBPIXELS;
  53 
  54     static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
  55     static final float LOWER_BND = -UPPER_BND;
  56 
  57     /**
  58      * Public constructor
  59      */
  60     public MarlinRenderingEngine() {
  61         super();
  62         logSettings(MarlinRenderingEngine.class.getName());
  63     }
  64 
  65     /**
  66      * Create a widened path as specified by the parameters.
  67      * <p>
  68      * The specified {@code src} {@link Shape} is widened according
  69      * to the specified attribute parameters as per the
  70      * {@link BasicStroke} specification.
  71      *
  72      * @param src the source path to be widened
  73      * @param width the width of the widened path as per {@code BasicStroke}
  74      * @param caps the end cap decorations as per {@code BasicStroke}
  75      * @param join the segment join decorations as per {@code BasicStroke}
  76      * @param miterlimit the miter limit as per {@code BasicStroke}


 265             double widthsquared = ((EA + EC + hypot)/2.0);
 266 
 267             widthScale = (float)Math.sqrt(widthsquared);
 268         }
 269 
 270         return (lw / widthScale);
 271     }
 272 
 273     final void strokeTo(final RendererContext rdrCtx,
 274                         Shape src,
 275                         AffineTransform at,
 276                         float width,
 277                         NormMode normalize,
 278                         int caps,
 279                         int join,
 280                         float miterlimit,
 281                         float dashes[],
 282                         float dashphase,
 283                         PathConsumer2D pc2d)
 284     {
 285         // We use strokerat so that in Stroker and Dasher we can work only
 286         // with the pre-transformation coordinates. This will repeat a lot of
 287         // computations done in the path iterator, but the alternative is to
 288         // work with transformed paths and compute untransformed coordinates
 289         // as needed. This would be faster but I do not think the complexity
 290         // of working with both untransformed and transformed coordinates in
 291         // the same code is worth it.
 292         // However, if a path's width is constant after a transformation,
 293         // we can skip all this untransforming.
 294 
 295         // As pathTo() will check transformed coordinates for invalid values
 296         // (NaN / Infinity) to ignore such points, it is necessary to apply the
 297         // transformation before the path processing.


 298         AffineTransform strokerat = null;

 299 

 300         int dashLen = -1;
 301         boolean recycleDashes = false;
 302 
 303         if (at != null && !at.isIdentity()) {
 304             final double a = at.getScaleX();
 305             final double b = at.getShearX();
 306             final double c = at.getShearY();
 307             final double d = at.getScaleY();
 308             final double det = a * d - c * b;
 309 
 310             if (Math.abs(det) <= (2f * Float.MIN_VALUE)) {
 311                 // this rendering engine takes one dimensional curves and turns
 312                 // them into 2D shapes by giving them width.
 313                 // However, if everything is to be passed through a singular
 314                 // transformation, these 2D shapes will be squashed down to 1D
 315                 // again so, nothing can be drawn.
 316 
 317                 // Every path needs an initial moveTo and a pathDone. If these
 318                 // are not there this causes a SIGSEGV in libawt.so (at the time
 319                 // of writing of this comment (September 16, 2010)). Actually,
 320                 // I am not sure if the moveTo is necessary to avoid the SIGSEGV
 321                 // but the pathDone is definitely needed.
 322                 pc2d.moveTo(0f, 0f);
 323                 pc2d.pathDone();
 324                 return;
 325             }
 326 
 327             // If the transform is a constant multiple of an orthogonal transformation
 328             // then every length is just multiplied by a constant, so we just
 329             // need to transform input paths to stroker and tell stroker
 330             // the scaled width. This condition is satisfied if
 331             // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
 332             // leave a bit of room for error.
 333             if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
 334                 final float scale = (float) Math.sqrt(a*a + c*c);
 335 
 336                 if (dashes != null) {
 337                     recycleDashes = true;
 338                     dashLen = dashes.length;
 339                     final float[] newDashes;
 340                     if (dashLen <= INITIAL_ARRAY) {
 341                         newDashes = rdrCtx.dasher.dashes_initial;
 342                     } else {
 343                         if (doStats) {
 344                             RendererContext.stats.stat_array_dasher_dasher
 345                                 .add(dashLen);
 346                         }
 347                         newDashes = rdrCtx.getDirtyFloatArray(dashLen);
 348                     }
 349                     System.arraycopy(dashes, 0, newDashes, 0, dashLen);
 350                     dashes = newDashes;
 351                     for (int i = 0; i < dashLen; i++) {
 352                         dashes[i] *= scale;
 353                     }
 354                     dashphase *= scale;
 355                 }
 356                 width *= scale;


 357 
 358                 // by now strokerat == null. Input paths to
 359                 // stroker (and maybe dasher) will have the full transform at
 360                 // applied to them and nothing will happen to the output paths.
 361             } else {
 362                 strokerat = at;
 363 
 364                 // by now strokerat == at. Input paths to
 365                 // stroker (and maybe dasher) will have the full transform at
 366                 // applied to them, then they will be normalized, and then
 367                 // the inverse of *only the non translation part of at* will
 368                 // be applied to the normalized paths. This won't cause problems
 369                 // in stroker, because, suppose at = T*A, where T is just the
 370                 // translation part of at, and A is the rest. T*A has already
 371                 // been applied to Stroker/Dasher's input. Then Ainv will be
 372                 // applied. Ainv*T*A is not equal to T, but it is a translation,
 373                 // which means that none of stroker's assumptions about its
 374                 // input will be violated. After all this, A will be applied
 375                 // to stroker's output.










 376             }
 377         } else {
 378             // either at is null or it's the identity. In either case
 379             // we don't transform the path.
 380             at = null;

 381         }
 382 
 383         if (useSimplifier) {
 384             // Use simplifier after stroker before Renderer
 385             // to remove collinear segments (notably due to cap square)
 386             pc2d = rdrCtx.simplifier.init(pc2d);
 387         }
 388 




 389         final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;

 390         pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
 391 
 392         pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit);
 393 
 394         if (dashes != null) {
 395             if (!recycleDashes) {
 396                 dashLen = dashes.length;
 397             }
 398             pc2d = rdrCtx.dasher.init(pc2d, dashes, dashLen, dashphase,
 399                                       recycleDashes);
 400         }
 401         pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);
 402 
 403         final PathIterator pi = getNormalizingPathIterator(rdrCtx, normalize,
 404                                     src.getPathIterator(at));
 405 
 406         pathTo(rdrCtx, pi, pc2d);
 407 
 408         /*
 409          * Pipeline seems to be:
 410          * shape.getPathIterator(at)
 411          * -> (NormalizingPathIterator)
 412          * -> (inverseDeltaTransformConsumer)
 413          * -> (Dasher)
 414          * -> Stroker
 415          * -> (deltaTransformConsumer)
 416          *
 417          * -> (CollinearSimplifier) to remove redundant segments
 418          *
 419          * -> pc2d = Renderer (bounding box)
 420          */
 421     }
 422 
 423     private static boolean nearZero(final double num) {
 424         return Math.abs(num) < 2.0 * Math.ulp(num);
 425     }
 426 
 427     PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
 428                                             final NormMode mode,
 429                                             final PathIterator src)
 430     {
 431         switch (mode) {
 432             case ON_WITH_AA:
 433                 // NormalizingPathIterator NearestPixelCenter:
 434                 return rdrCtx.nPCPathIterator.init(src);
 435             case ON_NO_AA:
 436                 // NearestPixel NormalizingPathIterator:
 437                 return rdrCtx.nPQPathIterator.init(src);


 611         }
 612     }
 613 
 614     private static void pathTo(final RendererContext rdrCtx, final PathIterator pi,
 615                                final PathConsumer2D pc2d)
 616     {
 617         // mark context as DIRTY:
 618         rdrCtx.dirty = true;
 619 
 620         final float[] coords = rdrCtx.float6;
 621 
 622         pathToLoop(coords, pi, pc2d);
 623 
 624         // mark context as CLEAN:
 625         rdrCtx.dirty = false;
 626     }
 627 
 628     private static void pathToLoop(final float[] coords, final PathIterator pi,
 629                                    final PathConsumer2D pc2d)
 630     {
 631         // ported from DuctusRenderingEngine.feedConsumer() but simplified:
 632         // - removed skip flag = !subpathStarted
 633         // - removed pathClosed flag because the new approach always calls
 634         //     moveTo() if !subpathStarted
 635         boolean subpathStarted = false;
 636 
 637         for (; !pi.isDone(); pi.next()) {
 638             switch (pi.currentSegment(coords)) {
 639             case PathIterator.SEG_MOVETO:
 640                 /* Checking SEG_MOVETO coordinates if they are out of the
 641                  * [LOWER_BND, UPPER_BND] range. This check also handles NaN
 642                  * and Infinity values. Skipping next path segment in case of
 643                  * invalid data.
 644                  */
 645                 if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
 646                     coords[1] < UPPER_BND && coords[1] > LOWER_BND)
 647                 {
 648                     pc2d.moveTo(coords[0], coords[1]);
 649                     subpathStarted = true;
 650                 }
 651                 break;
 652             case PathIterator.SEG_LINETO:
 653                 /* Checking SEG_LINETO coordinates if they are out of the
 654                  * [LOWER_BND, UPPER_BND] range. This check also handles NaN
 655                  * and Infinity values. Ignoring current path segment in case
 656                  * of invalid data. If segment is skipped its endpoint
 657                  * (if valid) is used to begin new subpath.
 658                  */
 659                 if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
 660                     coords[1] < UPPER_BND && coords[1] > LOWER_BND)
 661                 {
 662                     if (subpathStarted) {
 663                         pc2d.lineTo(coords[0], coords[1]);
 664                     } else {
 665                         pc2d.moveTo(coords[0], coords[1]);
 666                         subpathStarted = true;
 667                     }
 668                 }
 669                 break;
 670             case PathIterator.SEG_QUADTO:
 671                 // Quadratic curves take two points
 672                 /* Checking SEG_QUADTO coordinates if they are out of the
 673                  * [LOWER_BND, UPPER_BND] range. This check also handles NaN
 674                  * and Infinity values. Ignoring current path segment in case
 675                  * of invalid endpoints's data. Equivalent to the SEG_LINETO
 676                  * if endpoint coordinates are valid but there are invalid data
 677                  * among other coordinates
 678                  */
 679                 if (coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
 680                     coords[3] < UPPER_BND && coords[3] > LOWER_BND)
 681                 {
 682                     if (subpathStarted) {
 683                         if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
 684                             coords[1] < UPPER_BND && coords[1] > LOWER_BND)
 685                         {
 686                             pc2d.quadTo(coords[0], coords[1],
 687                                         coords[2], coords[3]);
 688                         } else {
 689                             pc2d.lineTo(coords[2], coords[3]);
 690                         }
 691                     } else {
 692                         pc2d.moveTo(coords[2], coords[3]);
 693                         subpathStarted = true;
 694                     }
 695                 }
 696                 break;
 697             case PathIterator.SEG_CUBICTO:
 698                 // Cubic curves take three points
 699                 /* Checking SEG_CUBICTO coordinates if they are out of the
 700                  * [LOWER_BND, UPPER_BND] range. This check also handles NaN
 701                  * and Infinity values. Ignoring current path segment in case
 702                  * of invalid endpoints's data. Equivalent to the SEG_LINETO
 703                  * if endpoint coordinates are valid but there are invalid data
 704                  * among other coordinates
 705                  */
 706                 if (coords[4] < UPPER_BND && coords[4] > LOWER_BND &&
 707                     coords[5] < UPPER_BND && coords[5] > LOWER_BND)
 708                 {
 709                     if (subpathStarted) {
 710                         if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
 711                             coords[1] < UPPER_BND && coords[1] > LOWER_BND &&
 712                             coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
 713                             coords[3] < UPPER_BND && coords[3] > LOWER_BND)
 714                         {
 715                             pc2d.curveTo(coords[0], coords[1],
 716                                          coords[2], coords[3],
 717                                          coords[4], coords[5]);
 718                         } else {
 719                             pc2d.lineTo(coords[4], coords[5]);
 720                         }
 721                     } else {
 722                         pc2d.moveTo(coords[4], coords[5]);
 723                         subpathStarted = true;
 724                     }
 725                 }
 726                 break;
 727             case PathIterator.SEG_CLOSE:
 728                 if (subpathStarted) {
 729                     pc2d.closePath();
 730                     subpathStarted = false;
 731                 }
 732                 break;
 733             default:
 734             }
 735         }
 736         pc2d.pathDone();
 737     }
 738 
 739     /**
 740      * Construct an antialiased tile generator for the given shape with
 741      * the given rendering attributes and store the bounds of the tile
 742      * iteration in the bbox parameter.
 743      * The {@code at} parameter specifies a transform that should affect
 744      * both the shape and the {@code BasicStroke} attributes.
 745      * The {@code clip} parameter specifies the current clip in effect
 746      * in device coordinates and can be used to prune the data for the
 747      * operation, but the renderer is not required to perform any
 748      * clipping.
 749      * If the {@code BasicStroke} parameter is null then the shape
 750      * should be filled as is, otherwise the attributes of the
 751      * {@code BasicStroke} should be used to specify a draw operation.
 752      * The {@code thin} parameter indicates whether or not the
 753      * transformed {@code BasicStroke} represents coordinates smaller


< prev index next >