< prev index next >

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

Print this page




  67         },
  68         OFF{
  69             @Override
  70             PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
  71                                                     final PathIterator src)
  72             {
  73                 // return original path iterator if normalization is disabled:
  74                 return src;
  75             }
  76         };
  77 
  78         abstract PathIterator getNormalizingPathIterator(DRendererContext rdrCtx,
  79                                                          PathIterator src);
  80     }
  81 
  82     private static final float MIN_PEN_SIZE = 1.0f / NORM_SUBPIXELS;
  83 
  84     static final double UPPER_BND = Float.MAX_VALUE / 2.0d;
  85     static final double LOWER_BND = -UPPER_BND;
  86 







  87     /**
  88      * Public constructor
  89      */
  90     public DMarlinRenderingEngine() {
  91         super();
  92         logSettings(DMarlinRenderingEngine.class.getName());
  93     }
  94 
  95     /**
  96      * Create a widened path as specified by the parameters.
  97      * <p>
  98      * The specified {@code src} {@link Shape} is widened according
  99      * to the specified attribute parameters as per the
 100      * {@link BasicStroke} specification.
 101      *
 102      * @param src the source path to be widened
 103      * @param width the width of the widened path as per {@code BasicStroke}
 104      * @param caps the end cap decorations as per {@code BasicStroke}
 105      * @param join the segment join decorations as per {@code BasicStroke}
 106      * @param miterlimit the miter limit as per {@code BasicStroke}


 116                                     int join,
 117                                     float miterlimit,
 118                                     float[] dashes,
 119                                     float dashphase)
 120     {
 121         final DRendererContext rdrCtx = getRendererContext();
 122         try {
 123             // initialize a large copyable Path2D to avoid a lot of array growing:
 124             final Path2D.Double p2d = rdrCtx.getPath2D();
 125 
 126             strokeTo(rdrCtx,
 127                      src,
 128                      null,
 129                      width,
 130                      NormMode.OFF,
 131                      caps,
 132                      join,
 133                      miterlimit,
 134                      dashes,
 135                      dashphase,
 136                      rdrCtx.transformerPC2D.wrapPath2d(p2d)
 137                     );
 138 
 139             // Use Path2D copy constructor (trim)
 140             return new Path2D.Double(p2d);
 141 
 142         } finally {
 143             // recycle the DRendererContext instance
 144             returnRendererContext(rdrCtx);
 145         }
 146     }
 147 
 148     /**
 149      * Sends the geometry for a widened path as specified by the parameters
 150      * to the specified consumer.
 151      * <p>
 152      * The specified {@code src} {@link Shape} is widened according
 153      * to the parameters specified by the {@link BasicStroke} object.
 154      * Adjustments are made to the path as appropriate for the
 155      * {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
 156      * {@code normalize} boolean parameter is true.


 178                          BasicStroke bs,
 179                          boolean thin,
 180                          boolean normalize,
 181                          boolean antialias,
 182                          final sun.awt.geom.PathConsumer2D consumer)
 183     {
 184         final NormMode norm = (normalize) ?
 185                 ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
 186                 : NormMode.OFF;
 187 
 188         final DRendererContext rdrCtx = getRendererContext();
 189         try {
 190             strokeTo(rdrCtx, src, at, bs, thin, norm, antialias,
 191                      rdrCtx.p2dAdapter.init(consumer));
 192         } finally {
 193             // recycle the DRendererContext instance
 194             returnRendererContext(rdrCtx);
 195         }
 196     }
 197 
 198     final void strokeTo(final DRendererContext rdrCtx,
 199                         Shape src,
 200                         AffineTransform at,
 201                         BasicStroke bs,
 202                         boolean thin,
 203                         NormMode normalize,
 204                         boolean antialias,
 205                         DPathConsumer2D pc2d)
 206     {
 207         double lw;
 208         if (thin) {
 209             if (antialias) {
 210                 lw = userSpaceLineWidth(at, MIN_PEN_SIZE);
 211             } else {
 212                 lw = userSpaceLineWidth(at, 1.0d);
 213             }
 214         } else {
 215             lw = bs.getLineWidth();
 216         }
 217         strokeTo(rdrCtx,
 218                  src,
 219                  at,
 220                  lw,
 221                  normalize,
 222                  bs.getEndCap(),
 223                  bs.getLineJoin(),
 224                  bs.getMiterLimit(),
 225                  bs.getDashArray(),


 278              * of rotation.)
 279              *
 280              * In the calculus, the ratio of the EB and (EA-EC) terms
 281              * ends up being the tangent of 2*theta where theta is
 282              * the angle that the long axis of the ellipse makes
 283              * with the horizontal axis.  Thus, this equation is
 284              * calculating the length of the hypotenuse of a triangle
 285              * along that axis.
 286              */
 287 
 288             double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC));
 289             // sqrt omitted, compare to squared limits below.
 290             double widthsquared = ((EA + EC + hypot) / 2.0d);
 291 
 292             widthScale = Math.sqrt(widthsquared);
 293         }
 294 
 295         return (lw / widthScale);
 296     }
 297 
 298     final void strokeTo(final DRendererContext rdrCtx,
 299                         Shape src,
 300                         AffineTransform at,
 301                         double width,
 302                         NormMode norm,
 303                         int caps,
 304                         int join,
 305                         float miterlimit,
 306                         float[] dashes,
 307                         float dashphase,
 308                         DPathConsumer2D pc2d)
 309     {
 310         // We use strokerat so that in Stroker and Dasher we can work only
 311         // with the pre-transformation coordinates. This will repeat a lot of
 312         // computations done in the path iterator, but the alternative is to
 313         // work with transformed paths and compute untransformed coordinates
 314         // as needed. This would be faster but I do not think the complexity
 315         // of working with both untransformed and transformed coordinates in
 316         // the same code is worth it.
 317         // However, if a path's width is constant after a transformation,
 318         // we can skip all this untransforming.
 319 
 320         // As pathTo() will check transformed coordinates for invalid values
 321         // (NaN / Infinity) to ignore such points, it is necessary to apply the
 322         // transformation before the path processing.
 323         AffineTransform strokerat = null;
 324 
 325         int dashLen = -1;
 326         boolean recycleDashes = false;

 327         double[] dashesD = null;
 328 
 329         // Ensure converting dashes to double precision:
 330         if (dashes != null) {
 331             recycleDashes = true;
 332             dashLen = dashes.length;
 333             dashesD = rdrCtx.dasher.copyDashArray(dashes);
 334         }
 335 
 336         if (at != null && !at.isIdentity()) {
 337             final double a = at.getScaleX();
 338             final double b = at.getShearX();
 339             final double c = at.getShearY();
 340             final double d = at.getScaleY();
 341             final double det = a * d - c * b;
 342 
 343             if (Math.abs(det) <= (2.0d * Double.MIN_VALUE)) {
 344                 // this rendering engine takes one dimensional curves and turns
 345                 // them into 2D shapes by giving them width.
 346                 // However, if everything is to be passed through a singular
 347                 // transformation, these 2D shapes will be squashed down to 1D
 348                 // again so, nothing can be drawn.
 349 
 350                 // Every path needs an initial moveTo and a pathDone. If these
 351                 // are not there this causes a SIGSEGV in libawt.so (at the time
 352                 // of writing of this comment (September 16, 2010)). Actually,
 353                 // I am not sure if the moveTo is necessary to avoid the SIGSEGV
 354                 // but the pathDone is definitely needed.
 355                 pc2d.moveTo(0.0d, 0.0d);
 356                 pc2d.pathDone();
 357                 return;
 358             }
 359 
 360             // If the transform is a constant multiple of an orthogonal transformation
 361             // then every length is just multiplied by a constant, so we just
 362             // need to transform input paths to stroker and tell stroker
 363             // the scaled width. This condition is satisfied if
 364             // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
 365             // leave a bit of room for error.
 366             if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
 367                 final double scale =  Math.sqrt(a*a + c*c);
 368 
 369                 if (dashesD != null) {
 370                     for (int i = 0; i < dashLen; i++) {
 371                         dashesD[i] *= scale;
 372                     }
 373                     dashphase *= scale;
 374                 }
 375                 width *= scale;
 376 
 377                 // by now strokerat == null. Input paths to
 378                 // stroker (and maybe dasher) will have the full transform at
 379                 // applied to them and nothing will happen to the output paths.
 380             } else {
 381                 strokerat = at;
 382 
 383                 // by now strokerat == at. Input paths to
 384                 // stroker (and maybe dasher) will have the full transform at
 385                 // applied to them, then they will be normalized, and then
 386                 // the inverse of *only the non translation part of at* will
 387                 // be applied to the normalized paths. This won't cause problems
 388                 // in stroker, because, suppose at = T*A, where T is just the
 389                 // translation part of at, and A is the rest. T*A has already
 390                 // been applied to Stroker/Dasher's input. Then Ainv will be
 391                 // applied. Ainv*T*A is not equal to T, but it is a translation,
 392                 // which means that none of stroker's assumptions about its
 393                 // input will be violated. After all this, A will be applied
 394                 // to stroker's output.
 395             }
 396         } else {
 397             // either at is null or it's the identity. In either case
 398             // we don't transform the path.
 399             at = null;
 400         }
 401 







 402         if (USE_SIMPLIFIER) {
 403             // Use simplifier after stroker before Renderer
 404             // to remove collinear segments (notably due to cap square)
 405             pc2d = rdrCtx.simplifier.init(pc2d);
 406         }
 407 
 408         final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
 409         pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
 410 
 411         pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit);

 412 
 413         if (dashesD != null) {
 414             pc2d = rdrCtx.dasher.init(pc2d, dashesD, dashLen, dashphase,
 415                                       recycleDashes);








 416         }
 417         pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);
 418 





 419         final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
 420                                          src.getPathIterator(at));
 421 
 422         pathTo(rdrCtx, pi, pc2d);
 423 
 424         /*
 425          * Pipeline seems to be:
 426          * shape.getPathIterator(at)
 427          * -> (NormalizingPathIterator)
 428          * -> (inverseDeltaTransformConsumer)
 429          * -> (Dasher)
 430          * -> Stroker
 431          * -> (deltaTransformConsumer)
 432          *
 433          * -> (CollinearSimplifier) to remove redundant segments
 434          *
 435          * -> pc2d = Renderer (bounding box)
 436          */
 437     }
 438 


 579                 return Math.floor(coord) + 0.5d;
 580             }
 581         }
 582 
 583         static final class NearestPixelQuarter
 584                                 extends NormalizingPathIterator
 585         {
 586             NearestPixelQuarter(final double[] tmp) {
 587                 super(tmp);
 588             }
 589 
 590             @Override
 591             double normCoord(final double coord) {
 592                 // round to nearest (0.25, 0.25) pixel quarter
 593                 return Math.floor(coord + 0.25d) + 0.25d;
 594             }
 595         }
 596     }
 597 
 598     private static void pathTo(final DRendererContext rdrCtx, final PathIterator pi,
 599                                final DPathConsumer2D pc2d)
 600     {
 601         // mark context as DIRTY:
 602         rdrCtx.dirty = true;
 603 
 604         final double[] coords = rdrCtx.double6;
 605 
 606         pathToLoop(coords, pi, pc2d);
 607 
 608         // mark context as CLEAN:
 609         rdrCtx.dirty = false;
 610     }
 611 
 612     private static void pathToLoop(final double[] coords, final PathIterator pi,
 613                                    final DPathConsumer2D pc2d)
 614     {
 615         // ported from DuctusRenderingEngine.feedConsumer() but simplified:
 616         // - removed skip flag = !subpathStarted
 617         // - removed pathClosed (ie subpathStarted not set to false)
 618         boolean subpathStarted = false;
 619 
 620         for (; !pi.isDone(); pi.next()) {
 621             switch (pi.currentSegment(coords)) {
 622             case PathIterator.SEG_MOVETO:
 623                 /* Checking SEG_MOVETO coordinates if they are out of the
 624                  * [LOWER_BND, UPPER_BND] range. This check also handles NaN
 625                  * and Infinity values. Skipping next path segment in case of
 626                  * invalid data.


 764      *                  {@code RenderingHint} is in effect
 765      * @param bbox returns the bounds of the iteration
 766      * @return the {@code AATileGenerator} instance to be consulted
 767      *         for tile coverages, or null if there is no output to render
 768      * @since 1.7
 769      */
 770     @Override
 771     public AATileGenerator getAATileGenerator(Shape s,
 772                                               AffineTransform at,
 773                                               Region clip,
 774                                               BasicStroke bs,
 775                                               boolean thin,
 776                                               boolean normalize,
 777                                               int[] bbox)
 778     {
 779         MarlinTileGenerator ptg = null;
 780         DRenderer r = null;
 781 
 782         final DRendererContext rdrCtx = getRendererContext();
 783         try {













 784             // Test if at is identity:
 785             final AffineTransform _at = (at != null && !at.isIdentity()) ? at
 786                                         : null;
 787 
 788             final NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
 789 
 790             if (bs == null) {
 791                 // fill shape:
 792                 final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
 793                                                  s.getPathIterator(_at));
 794 
 795                 // note: Winding rule may be EvenOdd ONLY for fill operations !
 796                 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
 797                                          clip.getWidth(), clip.getHeight(),
 798                                          pi.getWindingRule());
 799 















 800                 // TODO: subdivide quad/cubic curves into monotonic curves ?
 801                 pathTo(rdrCtx, pi, r);

 802             } else {
 803                 // draw shape with given stroke:
 804                 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
 805                                          clip.getWidth(), clip.getHeight(),
 806                                          PathIterator.WIND_NON_ZERO);
 807 
 808                 strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r);
 809             }
 810             if (r.endRendering()) {
 811                 ptg = rdrCtx.ptg.init();
 812                 ptg.getBbox(bbox);
 813                 // note: do not returnRendererContext(rdrCtx)
 814                 // as it will be called later by MarlinTileGenerator.dispose()
 815                 r = null;
 816             }
 817         } finally {
 818             if (r != null) {
 819                 // dispose renderer and recycle the RendererContext instance:
 820                 r.dispose();
 821             }
 822         }
 823 
 824         // Return null to cancel AA tile generation (nothing to render)
 825         return ptg;
 826     }
 827 
 828     @Override
 829     public final AATileGenerator getAATileGenerator(double x, double y,
 830                                                     double dx1, double dy1,
 831                                                     double dx2, double dy2,
 832                                                     double lw1, double lw2,
 833                                                     Region clip,
 834                                                     int[] bbox)
 835     {
 836         // REMIND: Deal with large coordinates!
 837         double ldx1, ldy1, ldx2, ldy2;
 838         boolean innerpgram = (lw1 > 0.0d && lw2 > 0.0d);
 839 
 840         if (innerpgram) {
 841             ldx1 = dx1 * lw1;
 842             ldy1 = dy1 * lw1;
 843             ldx2 = dx2 * lw2;
 844             ldy2 = dy2 * lw2;
 845             x -= (ldx1 + ldx2) / 2.0d;
 846             y -= (ldy1 + ldy2) / 2.0d;
 847             dx1 += ldx1;
 848             dy1 += ldy1;
 849             dx2 += ldx2;
 850             dy2 += ldy2;
 851             if (lw1 > 1.0d && lw2 > 1.0d) {
 852                 // Inner parallelogram was entirely consumed by stroke...
 853                 innerpgram = false;
 854             }
 855         } else {
 856             ldx1 = ldy1 = ldx2 = ldy2 = 0.0d;
 857         }
 858 
 859         MarlinTileGenerator ptg = null;
 860         DRenderer r = null;
 861 
 862         final DRendererContext rdrCtx = getRendererContext();
 863         try {
 864             r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
 865                                          clip.getWidth(), clip.getHeight(),
 866                                          DRenderer.WIND_EVEN_ODD);
 867 
 868             r.moveTo( x,  y);
 869             r.lineTo( (x+dx1),  (y+dy1));
 870             r.lineTo( (x+dx1+dx2),  (y+dy1+dy2));
 871             r.lineTo( (x+dx2),  (y+dy2));
 872             r.closePath();
 873 
 874             if (innerpgram) {
 875                 x += ldx1 + ldx2;
 876                 y += ldy1 + ldy2;
 877                 dx1 -= 2.0d * ldx1;
 878                 dy1 -= 2.0d * ldy1;
 879                 dx2 -= 2.0d * ldx2;
 880                 dy2 -= 2.0d * ldy2;
 881                 r.moveTo( x,  y);
 882                 r.lineTo( (x+dx1),  (y+dy1));
 883                 r.lineTo( (x+dx1+dx2),  (y+dy1+dy2));
 884                 r.lineTo( (x+dx2),  (y+dy2));
 885                 r.closePath();
 886             }


 898                 // dispose renderer and recycle the RendererContext instance:
 899                 r.dispose();
 900             }
 901         }
 902 
 903         // Return null to cancel AA tile generation (nothing to render)
 904         return ptg;
 905     }
 906 
 907     /**
 908      * Returns the minimum pen width that the antialiasing rasterizer
 909      * can represent without dropouts occuring.
 910      * @since 1.7
 911      */
 912     @Override
 913     public float getMinimumAAPenSize() {
 914         return MIN_PEN_SIZE;
 915     }
 916 
 917     static {
 918         if (PathIterator.WIND_NON_ZERO != DRenderer.WIND_NON_ZERO ||
 919             PathIterator.WIND_EVEN_ODD != DRenderer.WIND_EVEN_ODD ||
 920             BasicStroke.JOIN_MITER != DStroker.JOIN_MITER ||
 921             BasicStroke.JOIN_ROUND != DStroker.JOIN_ROUND ||
 922             BasicStroke.JOIN_BEVEL != DStroker.JOIN_BEVEL ||
 923             BasicStroke.CAP_BUTT != DStroker.CAP_BUTT ||
 924             BasicStroke.CAP_ROUND != DStroker.CAP_ROUND ||
 925             BasicStroke.CAP_SQUARE != DStroker.CAP_SQUARE)
 926         {
 927             throw new InternalError("mismatched renderer constants");
 928         }
 929     }
 930 
 931     // --- DRendererContext handling ---
 932     // use ThreadLocal or ConcurrentLinkedQueue to get one DRendererContext
 933     private static final boolean USE_THREAD_LOCAL;
 934 
 935     // reference type stored in either TL or CLQ
 936     static final int REF_TYPE;
 937 
 938     // Per-thread DRendererContext
 939     private static final ReentrantContextProvider<DRendererContext> RDR_CTX_PROVIDER;
 940 
 941     // Static initializer to use TL or CLQ mode
 942     static {
 943         USE_THREAD_LOCAL = MarlinProperties.isUseThreadLocal();
 944 
 945         // Soft reference by default:


1027                 + MarlinConst.TILE_W_LG);
1028         logInfo("sun.java2d.renderer.blockSize_log2   = "
1029                 + MarlinConst.BLOCK_SIZE_LG);
1030 
1031         // RLE / blockFlags settings
1032 
1033         logInfo("sun.java2d.renderer.forceRLE         = "
1034                 + MarlinProperties.isForceRLE());
1035         logInfo("sun.java2d.renderer.forceNoRLE       = "
1036                 + MarlinProperties.isForceNoRLE());
1037         logInfo("sun.java2d.renderer.useTileFlags     = "
1038                 + MarlinProperties.isUseTileFlags());
1039         logInfo("sun.java2d.renderer.useTileFlags.useHeuristics = "
1040                 + MarlinProperties.isUseTileFlagsWithHeuristics());
1041         logInfo("sun.java2d.renderer.rleMinWidth      = "
1042                 + MarlinCache.RLE_MIN_WIDTH);
1043 
1044         // optimisation parameters
1045         logInfo("sun.java2d.renderer.useSimplifier    = "
1046                 + MarlinConst.USE_SIMPLIFIER);




1047 
1048         // debugging parameters
1049         logInfo("sun.java2d.renderer.doStats          = "
1050                 + MarlinConst.DO_STATS);
1051         logInfo("sun.java2d.renderer.doMonitors       = "
1052                 + MarlinConst.DO_MONITORS);
1053         logInfo("sun.java2d.renderer.doChecks         = "
1054                 + MarlinConst.DO_CHECKS);
1055 
1056         // logging parameters
1057         logInfo("sun.java2d.renderer.useLogger        = "
1058                 + MarlinConst.USE_LOGGER);
1059         logInfo("sun.java2d.renderer.logCreateContext = "
1060                 + MarlinConst.LOG_CREATE_CONTEXT);
1061         logInfo("sun.java2d.renderer.logUnsafeMalloc  = "
1062                 + MarlinConst.LOG_UNSAFE_MALLOC);
1063 
1064         // quality settings
1065         logInfo("sun.java2d.renderer.cubic_dec_d2     = "
1066                 + MarlinProperties.getCubicDecD2());




  67         },
  68         OFF{
  69             @Override
  70             PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
  71                                                     final PathIterator src)
  72             {
  73                 // return original path iterator if normalization is disabled:
  74                 return src;
  75             }
  76         };
  77 
  78         abstract PathIterator getNormalizingPathIterator(DRendererContext rdrCtx,
  79                                                          PathIterator src);
  80     }
  81 
  82     private static final float MIN_PEN_SIZE = 1.0f / NORM_SUBPIXELS;
  83 
  84     static final double UPPER_BND = Float.MAX_VALUE / 2.0d;
  85     static final double LOWER_BND = -UPPER_BND;
  86 
  87     static final boolean DO_CLIP = MarlinProperties.isDoClip();
  88     static final boolean DO_CLIP_FILL = true;
  89 
  90     static final boolean DO_TRACE_PATH = false;
  91 
  92     static final boolean DO_CLIP_RUNTIME_ENABLE = MarlinProperties.isDoClipRuntimeFlag();
  93 
  94     /**
  95      * Public constructor
  96      */
  97     public DMarlinRenderingEngine() {
  98         super();
  99         logSettings(DMarlinRenderingEngine.class.getName());
 100     }
 101 
 102     /**
 103      * Create a widened path as specified by the parameters.
 104      * <p>
 105      * The specified {@code src} {@link Shape} is widened according
 106      * to the specified attribute parameters as per the
 107      * {@link BasicStroke} specification.
 108      *
 109      * @param src the source path to be widened
 110      * @param width the width of the widened path as per {@code BasicStroke}
 111      * @param caps the end cap decorations as per {@code BasicStroke}
 112      * @param join the segment join decorations as per {@code BasicStroke}
 113      * @param miterlimit the miter limit as per {@code BasicStroke}


 123                                     int join,
 124                                     float miterlimit,
 125                                     float[] dashes,
 126                                     float dashphase)
 127     {
 128         final DRendererContext rdrCtx = getRendererContext();
 129         try {
 130             // initialize a large copyable Path2D to avoid a lot of array growing:
 131             final Path2D.Double p2d = rdrCtx.getPath2D();
 132 
 133             strokeTo(rdrCtx,
 134                      src,
 135                      null,
 136                      width,
 137                      NormMode.OFF,
 138                      caps,
 139                      join,
 140                      miterlimit,
 141                      dashes,
 142                      dashphase,
 143                      rdrCtx.transformerPC2D.wrapPath2D(p2d)
 144                     );
 145 
 146             // Use Path2D copy constructor (trim)
 147             return new Path2D.Double(p2d);
 148 
 149         } finally {
 150             // recycle the DRendererContext instance
 151             returnRendererContext(rdrCtx);
 152         }
 153     }
 154 
 155     /**
 156      * Sends the geometry for a widened path as specified by the parameters
 157      * to the specified consumer.
 158      * <p>
 159      * The specified {@code src} {@link Shape} is widened according
 160      * to the parameters specified by the {@link BasicStroke} object.
 161      * Adjustments are made to the path as appropriate for the
 162      * {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the
 163      * {@code normalize} boolean parameter is true.


 185                          BasicStroke bs,
 186                          boolean thin,
 187                          boolean normalize,
 188                          boolean antialias,
 189                          final sun.awt.geom.PathConsumer2D consumer)
 190     {
 191         final NormMode norm = (normalize) ?
 192                 ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
 193                 : NormMode.OFF;
 194 
 195         final DRendererContext rdrCtx = getRendererContext();
 196         try {
 197             strokeTo(rdrCtx, src, at, bs, thin, norm, antialias,
 198                      rdrCtx.p2dAdapter.init(consumer));
 199         } finally {
 200             // recycle the DRendererContext instance
 201             returnRendererContext(rdrCtx);
 202         }
 203     }
 204 
 205     void strokeTo(final DRendererContext rdrCtx,
 206                   Shape src,
 207                   AffineTransform at,
 208                   BasicStroke bs,
 209                   boolean thin,
 210                   NormMode normalize,
 211                   boolean antialias,
 212                   DPathConsumer2D pc2d)
 213     {
 214         double lw;
 215         if (thin) {
 216             if (antialias) {
 217                 lw = userSpaceLineWidth(at, MIN_PEN_SIZE);
 218             } else {
 219                 lw = userSpaceLineWidth(at, 1.0d);
 220             }
 221         } else {
 222             lw = bs.getLineWidth();
 223         }
 224         strokeTo(rdrCtx,
 225                  src,
 226                  at,
 227                  lw,
 228                  normalize,
 229                  bs.getEndCap(),
 230                  bs.getLineJoin(),
 231                  bs.getMiterLimit(),
 232                  bs.getDashArray(),


 285              * of rotation.)
 286              *
 287              * In the calculus, the ratio of the EB and (EA-EC) terms
 288              * ends up being the tangent of 2*theta where theta is
 289              * the angle that the long axis of the ellipse makes
 290              * with the horizontal axis.  Thus, this equation is
 291              * calculating the length of the hypotenuse of a triangle
 292              * along that axis.
 293              */
 294 
 295             double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC));
 296             // sqrt omitted, compare to squared limits below.
 297             double widthsquared = ((EA + EC + hypot) / 2.0d);
 298 
 299             widthScale = Math.sqrt(widthsquared);
 300         }
 301 
 302         return (lw / widthScale);
 303     }
 304 
 305     void strokeTo(final DRendererContext rdrCtx,
 306                   Shape src,
 307                   AffineTransform at,
 308                   double width,
 309                   NormMode norm,
 310                   int caps,
 311                   int join,
 312                   float miterlimit,
 313                   float[] dashes,
 314                   float dashphase,
 315                   DPathConsumer2D pc2d)
 316     {
 317         // We use strokerat so that in Stroker and Dasher we can work only
 318         // with the pre-transformation coordinates. This will repeat a lot of
 319         // computations done in the path iterator, but the alternative is to
 320         // work with transformed paths and compute untransformed coordinates
 321         // as needed. This would be faster but I do not think the complexity
 322         // of working with both untransformed and transformed coordinates in
 323         // the same code is worth it.
 324         // However, if a path's width is constant after a transformation,
 325         // we can skip all this untransforming.
 326 
 327         // As pathTo() will check transformed coordinates for invalid values
 328         // (NaN / Infinity) to ignore such points, it is necessary to apply the
 329         // transformation before the path processing.
 330         AffineTransform strokerat = null;
 331 
 332         int dashLen = -1;
 333         boolean recycleDashes = false;
 334         double scale = 1.0d;
 335         double[] dashesD = null;
 336 
 337         // Ensure converting dashes to double precision:
 338         if (dashes != null) {
 339             recycleDashes = true;
 340             dashLen = dashes.length;
 341             dashesD = rdrCtx.dasher.copyDashArray(dashes);
 342         }
 343 
 344         if (at != null && !at.isIdentity()) {
 345             final double a = at.getScaleX();
 346             final double b = at.getShearX();
 347             final double c = at.getShearY();
 348             final double d = at.getScaleY();
 349             final double det = a * d - c * b;
 350 
 351             if (Math.abs(det) <= (2.0d * Double.MIN_VALUE)) {
 352                 // this rendering engine takes one dimensional curves and turns
 353                 // them into 2D shapes by giving them width.
 354                 // However, if everything is to be passed through a singular
 355                 // transformation, these 2D shapes will be squashed down to 1D
 356                 // again so, nothing can be drawn.
 357 
 358                 // Every path needs an initial moveTo and a pathDone. If these
 359                 // are not there this causes a SIGSEGV in libawt.so (at the time
 360                 // of writing of this comment (September 16, 2010)). Actually,
 361                 // I am not sure if the moveTo is necessary to avoid the SIGSEGV
 362                 // but the pathDone is definitely needed.
 363                 pc2d.moveTo(0.0d, 0.0d);
 364                 pc2d.pathDone();
 365                 return;
 366             }
 367 
 368             // If the transform is a constant multiple of an orthogonal transformation
 369             // then every length is just multiplied by a constant, so we just
 370             // need to transform input paths to stroker and tell stroker
 371             // the scaled width. This condition is satisfied if
 372             // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
 373             // leave a bit of room for error.
 374             if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
 375                 scale =  Math.sqrt(a*a + c*c);
 376 
 377                 if (dashesD != null) {
 378                     for (int i = 0; i < dashLen; i++) {
 379                         dashesD[i] *= scale;
 380                     }
 381                     dashphase *= scale;
 382                 }
 383                 width *= scale;
 384 
 385                 // by now strokerat == null. Input paths to
 386                 // stroker (and maybe dasher) will have the full transform at
 387                 // applied to them and nothing will happen to the output paths.
 388             } else {
 389                 strokerat = at;
 390 
 391                 // by now strokerat == at. Input paths to
 392                 // stroker (and maybe dasher) will have the full transform at
 393                 // applied to them, then they will be normalized, and then
 394                 // the inverse of *only the non translation part of at* will
 395                 // be applied to the normalized paths. This won't cause problems
 396                 // in stroker, because, suppose at = T*A, where T is just the
 397                 // translation part of at, and A is the rest. T*A has already
 398                 // been applied to Stroker/Dasher's input. Then Ainv will be
 399                 // applied. Ainv*T*A is not equal to T, but it is a translation,
 400                 // which means that none of stroker's assumptions about its
 401                 // input will be violated. After all this, A will be applied
 402                 // to stroker's output.
 403             }
 404         } else {
 405             // either at is null or it's the identity. In either case
 406             // we don't transform the path.
 407             at = null;
 408         }
 409 
 410         final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
 411 
 412         if (DO_TRACE_PATH) {
 413             // trace Stroker:
 414             pc2d = transformerPC2D.traceStroker(pc2d);
 415         }
 416 
 417         if (USE_SIMPLIFIER) {
 418             // Use simplifier after stroker before Renderer
 419             // to remove collinear segments (notably due to cap square)
 420             pc2d = rdrCtx.simplifier.init(pc2d);
 421         }
 422 
 423         // deltaTransformConsumer may adjust the clip rectangle:
 424         pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
 425 
 426         // stroker will adjust the clip rectangle (width / miter limit):
 427         pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, scale);
 428 
 429         if (dashesD != null) {
 430             pc2d = rdrCtx.dasher.init(pc2d, dashesD, dashLen, dashphase,
 431                                       recycleDashes);
 432         } else if (rdrCtx.doClip && (caps != Stroker.CAP_BUTT)) {
 433             if (DO_TRACE_PATH) {
 434                 pc2d = transformerPC2D.traceClosedPathDetector(pc2d);
 435             }
 436 
 437             // If no dash and clip is enabled:
 438             // detect closedPaths (polygons) for caps
 439             pc2d = transformerPC2D.detectClosedPath(pc2d);
 440         }
 441         pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);
 442 
 443         if (DO_TRACE_PATH) {
 444             // trace Input:
 445             pc2d = transformerPC2D.traceInput(pc2d);
 446         }
 447 
 448         final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
 449                                          src.getPathIterator(at));
 450 
 451         pathTo(rdrCtx, pi, pc2d);
 452 
 453         /*
 454          * Pipeline seems to be:
 455          * shape.getPathIterator(at)
 456          * -> (NormalizingPathIterator)
 457          * -> (inverseDeltaTransformConsumer)
 458          * -> (Dasher)
 459          * -> Stroker
 460          * -> (deltaTransformConsumer)
 461          *
 462          * -> (CollinearSimplifier) to remove redundant segments
 463          *
 464          * -> pc2d = Renderer (bounding box)
 465          */
 466     }
 467 


 608                 return Math.floor(coord) + 0.5d;
 609             }
 610         }
 611 
 612         static final class NearestPixelQuarter
 613                                 extends NormalizingPathIterator
 614         {
 615             NearestPixelQuarter(final double[] tmp) {
 616                 super(tmp);
 617             }
 618 
 619             @Override
 620             double normCoord(final double coord) {
 621                 // round to nearest (0.25, 0.25) pixel quarter
 622                 return Math.floor(coord + 0.25d) + 0.25d;
 623             }
 624         }
 625     }
 626 
 627     private static void pathTo(final DRendererContext rdrCtx, final PathIterator pi,
 628                                DPathConsumer2D pc2d)
 629     {
 630         // mark context as DIRTY:
 631         rdrCtx.dirty = true;
 632 
 633         pathToLoop(rdrCtx.double6, pi, pc2d);


 634 
 635         // mark context as CLEAN:
 636         rdrCtx.dirty = false;
 637     }
 638 
 639     private static void pathToLoop(final double[] coords, final PathIterator pi,
 640                                    final DPathConsumer2D pc2d)
 641     {
 642         // ported from DuctusRenderingEngine.feedConsumer() but simplified:
 643         // - removed skip flag = !subpathStarted
 644         // - removed pathClosed (ie subpathStarted not set to false)
 645         boolean subpathStarted = false;
 646 
 647         for (; !pi.isDone(); pi.next()) {
 648             switch (pi.currentSegment(coords)) {
 649             case PathIterator.SEG_MOVETO:
 650                 /* Checking SEG_MOVETO coordinates if they are out of the
 651                  * [LOWER_BND, UPPER_BND] range. This check also handles NaN
 652                  * and Infinity values. Skipping next path segment in case of
 653                  * invalid data.


 791      *                  {@code RenderingHint} is in effect
 792      * @param bbox returns the bounds of the iteration
 793      * @return the {@code AATileGenerator} instance to be consulted
 794      *         for tile coverages, or null if there is no output to render
 795      * @since 1.7
 796      */
 797     @Override
 798     public AATileGenerator getAATileGenerator(Shape s,
 799                                               AffineTransform at,
 800                                               Region clip,
 801                                               BasicStroke bs,
 802                                               boolean thin,
 803                                               boolean normalize,
 804                                               int[] bbox)
 805     {
 806         MarlinTileGenerator ptg = null;
 807         DRenderer r = null;
 808 
 809         final DRendererContext rdrCtx = getRendererContext();
 810         try {
 811             if (DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime())) {
 812                 // Define the initial clip bounds:
 813                 final double[] clipRect = rdrCtx.clipRect;
 814 
 815                 clipRect[0] = clip.getLoY();
 816                 clipRect[1] = clip.getLoY() + clip.getHeight();
 817                 clipRect[2] = clip.getLoX();
 818                 clipRect[3] = clip.getLoX() + clip.getWidth();
 819 
 820                 // Enable clipping:
 821                 rdrCtx.doClip = true;
 822             }
 823 
 824             // Test if at is identity:
 825             final AffineTransform _at = (at != null && !at.isIdentity()) ? at
 826                                         : null;
 827 
 828             final NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
 829 
 830             if (bs == null) {
 831                 // fill shape:
 832                 final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
 833                                                  s.getPathIterator(_at));
 834 
 835                 // note: Winding rule may be EvenOdd ONLY for fill operations !
 836                 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
 837                                          clip.getWidth(), clip.getHeight(),
 838                                          pi.getWindingRule());
 839 
 840                 DPathConsumer2D pc2d = r;
 841 
 842                 if (DO_CLIP_FILL && rdrCtx.doClip) {
 843                     if (DO_TRACE_PATH) {
 844                         // trace Filler:
 845                         pc2d = rdrCtx.transformerPC2D.traceFiller(pc2d);
 846                     }
 847                     pc2d = rdrCtx.transformerPC2D.pathClipper(pc2d);
 848                 }
 849 
 850                 if (DO_TRACE_PATH) {
 851                     // trace Input:
 852                     pc2d = rdrCtx.transformerPC2D.traceInput(pc2d);
 853                 }
 854 
 855                 // TODO: subdivide quad/cubic curves into monotonic curves ?
 856                 pathTo(rdrCtx, pi, pc2d);
 857 
 858             } else {
 859                 // draw shape with given stroke:
 860                 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
 861                                          clip.getWidth(), clip.getHeight(),
 862                                          WIND_NON_ZERO);
 863 
 864                 strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r);
 865             }
 866             if (r.endRendering()) {
 867                 ptg = rdrCtx.ptg.init();
 868                 ptg.getBbox(bbox);
 869                 // note: do not returnRendererContext(rdrCtx)
 870                 // as it will be called later by MarlinTileGenerator.dispose()
 871                 r = null;
 872             }
 873         } finally {
 874             if (r != null) {
 875                 // dispose renderer and recycle the RendererContext instance:
 876                 r.dispose();
 877             }
 878         }
 879 
 880         // Return null to cancel AA tile generation (nothing to render)
 881         return ptg;
 882     }
 883 
 884     @Override
 885     public AATileGenerator getAATileGenerator(double x, double y,
 886                                               double dx1, double dy1,
 887                                               double dx2, double dy2,
 888                                               double lw1, double lw2,
 889                                               Region clip,
 890                                               int[] bbox)
 891     {
 892         // REMIND: Deal with large coordinates!
 893         double ldx1, ldy1, ldx2, ldy2;
 894         boolean innerpgram = (lw1 > 0.0d && lw2 > 0.0d);
 895 
 896         if (innerpgram) {
 897             ldx1 = dx1 * lw1;
 898             ldy1 = dy1 * lw1;
 899             ldx2 = dx2 * lw2;
 900             ldy2 = dy2 * lw2;
 901             x -= (ldx1 + ldx2) / 2.0d;
 902             y -= (ldy1 + ldy2) / 2.0d;
 903             dx1 += ldx1;
 904             dy1 += ldy1;
 905             dx2 += ldx2;
 906             dy2 += ldy2;
 907             if (lw1 > 1.0d && lw2 > 1.0d) {
 908                 // Inner parallelogram was entirely consumed by stroke...
 909                 innerpgram = false;
 910             }
 911         } else {
 912             ldx1 = ldy1 = ldx2 = ldy2 = 0.0d;
 913         }
 914 
 915         MarlinTileGenerator ptg = null;
 916         DRenderer r = null;
 917 
 918         final DRendererContext rdrCtx = getRendererContext();
 919         try {
 920             r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
 921                                      clip.getWidth(), clip.getHeight(),
 922                                      WIND_EVEN_ODD);
 923 
 924             r.moveTo( x,  y);
 925             r.lineTo( (x+dx1),  (y+dy1));
 926             r.lineTo( (x+dx1+dx2),  (y+dy1+dy2));
 927             r.lineTo( (x+dx2),  (y+dy2));
 928             r.closePath();
 929 
 930             if (innerpgram) {
 931                 x += ldx1 + ldx2;
 932                 y += ldy1 + ldy2;
 933                 dx1 -= 2.0d * ldx1;
 934                 dy1 -= 2.0d * ldy1;
 935                 dx2 -= 2.0d * ldx2;
 936                 dy2 -= 2.0d * ldy2;
 937                 r.moveTo( x,  y);
 938                 r.lineTo( (x+dx1),  (y+dy1));
 939                 r.lineTo( (x+dx1+dx2),  (y+dy1+dy2));
 940                 r.lineTo( (x+dx2),  (y+dy2));
 941                 r.closePath();
 942             }


 954                 // dispose renderer and recycle the RendererContext instance:
 955                 r.dispose();
 956             }
 957         }
 958 
 959         // Return null to cancel AA tile generation (nothing to render)
 960         return ptg;
 961     }
 962 
 963     /**
 964      * Returns the minimum pen width that the antialiasing rasterizer
 965      * can represent without dropouts occuring.
 966      * @since 1.7
 967      */
 968     @Override
 969     public float getMinimumAAPenSize() {
 970         return MIN_PEN_SIZE;
 971     }
 972 
 973     static {
 974         if (PathIterator.WIND_NON_ZERO != WIND_NON_ZERO ||
 975             PathIterator.WIND_EVEN_ODD != WIND_EVEN_ODD ||
 976             BasicStroke.JOIN_MITER != JOIN_MITER ||
 977             BasicStroke.JOIN_ROUND != JOIN_ROUND ||
 978             BasicStroke.JOIN_BEVEL != JOIN_BEVEL ||
 979             BasicStroke.CAP_BUTT != CAP_BUTT ||
 980             BasicStroke.CAP_ROUND != CAP_ROUND ||
 981             BasicStroke.CAP_SQUARE != CAP_SQUARE)
 982         {
 983             throw new InternalError("mismatched renderer constants");
 984         }
 985     }
 986 
 987     // --- DRendererContext handling ---
 988     // use ThreadLocal or ConcurrentLinkedQueue to get one DRendererContext
 989     private static final boolean USE_THREAD_LOCAL;
 990 
 991     // reference type stored in either TL or CLQ
 992     static final int REF_TYPE;
 993 
 994     // Per-thread DRendererContext
 995     private static final ReentrantContextProvider<DRendererContext> RDR_CTX_PROVIDER;
 996 
 997     // Static initializer to use TL or CLQ mode
 998     static {
 999         USE_THREAD_LOCAL = MarlinProperties.isUseThreadLocal();
1000 
1001         // Soft reference by default:


1083                 + MarlinConst.TILE_W_LG);
1084         logInfo("sun.java2d.renderer.blockSize_log2   = "
1085                 + MarlinConst.BLOCK_SIZE_LG);
1086 
1087         // RLE / blockFlags settings
1088 
1089         logInfo("sun.java2d.renderer.forceRLE         = "
1090                 + MarlinProperties.isForceRLE());
1091         logInfo("sun.java2d.renderer.forceNoRLE       = "
1092                 + MarlinProperties.isForceNoRLE());
1093         logInfo("sun.java2d.renderer.useTileFlags     = "
1094                 + MarlinProperties.isUseTileFlags());
1095         logInfo("sun.java2d.renderer.useTileFlags.useHeuristics = "
1096                 + MarlinProperties.isUseTileFlagsWithHeuristics());
1097         logInfo("sun.java2d.renderer.rleMinWidth      = "
1098                 + MarlinCache.RLE_MIN_WIDTH);
1099 
1100         // optimisation parameters
1101         logInfo("sun.java2d.renderer.useSimplifier    = "
1102                 + MarlinConst.USE_SIMPLIFIER);
1103         logInfo("sun.java2d.renderer.clip             = "
1104                 + MarlinProperties.isDoClip());
1105         logInfo("sun.java2d.renderer.clip.runtime.enable = "
1106                 + MarlinProperties.isDoClipRuntimeFlag());
1107 
1108         // debugging parameters
1109         logInfo("sun.java2d.renderer.doStats          = "
1110                 + MarlinConst.DO_STATS);
1111         logInfo("sun.java2d.renderer.doMonitors       = "
1112                 + MarlinConst.DO_MONITORS);
1113         logInfo("sun.java2d.renderer.doChecks         = "
1114                 + MarlinConst.DO_CHECKS);
1115 
1116         // logging parameters
1117         logInfo("sun.java2d.renderer.useLogger        = "
1118                 + MarlinConst.USE_LOGGER);
1119         logInfo("sun.java2d.renderer.logCreateContext = "
1120                 + MarlinConst.LOG_CREATE_CONTEXT);
1121         logInfo("sun.java2d.renderer.logUnsafeMalloc  = "
1122                 + MarlinConst.LOG_UNSAFE_MALLOC);
1123 
1124         // quality settings
1125         logInfo("sun.java2d.renderer.cubic_dec_d2     = "
1126                 + MarlinProperties.getCubicDecD2());


< prev index next >