< prev index next >

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

Print this page




  27 
  28 import java.awt.BasicStroke;
  29 import java.awt.Shape;
  30 import java.awt.geom.AffineTransform;
  31 import java.awt.geom.Path2D;
  32 import java.awt.geom.PathIterator;
  33 import java.security.AccessController;
  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 {
  51         ON_WITH_AA {
  52             @Override
  53             PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
  54                                                     final PathIterator src)
  55             {
  56                 // NormalizingPathIterator NearestPixelCenter:
  57                 return rdrCtx.nPCPathIterator.init(src);
  58             }
  59         },
  60         ON_NO_AA{
  61             @Override
  62             PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
  63                                                     final PathIterator src)
  64             {
  65                 // NearestPixel NormalizingPathIterator:
  66                 return rdrCtx.nPQPathIterator.init(src);
  67             }
  68         },
  69         OFF{
  70             @Override
  71             PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
  72                                                     final PathIterator src)
  73             {
  74                 // return original path iterator if normalization is disabled:
  75                 return src;
  76             }
  77         };
  78 
  79         abstract PathIterator getNormalizingPathIterator(RendererContext rdrCtx,
  80                                                          PathIterator src);
  81     }
  82 
  83     private static final float MIN_PEN_SIZE = 1f / NORM_SUBPIXELS;
  84 
  85     static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
  86     static final float LOWER_BND = -UPPER_BND;
  87 
  88     /**
  89      * Public constructor
  90      */
  91     public MarlinRenderingEngine() {
  92         super();
  93         logSettings(MarlinRenderingEngine.class.getName());
  94     }
  95 
  96     /**
  97      * Create a widened path as specified by the parameters.
  98      * <p>
  99      * The specified {@code src} {@link Shape} is widened according
 100      * to the specified attribute parameters as per the
 101      * {@link BasicStroke} specification.
 102      *
 103      * @param src the source path to be widened


 242             double C = at.getShearX();       // m01
 243             double B = at.getShearY();       // m10
 244             double D = at.getScaleY();       // m11
 245 
 246             /*
 247              * Given a 2 x 2 affine matrix [ A B ] such that
 248              *                             [ C D ]
 249              * v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to
 250              * find the maximum magnitude (norm) of the vector v'
 251              * with the constraint (x^2 + y^2 = 1).
 252              * The equation to maximize is
 253              *     |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2)
 254              * or  |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2).
 255              * Since sqrt is monotonic we can maximize |v'|^2
 256              * instead and plug in the substitution y = sqrt(1 - x^2).
 257              * Trigonometric equalities can then be used to get
 258              * rid of most of the sqrt terms.
 259              */
 260 
 261             double EA = A*A + B*B;          // x^2 coefficient
 262             double EB = 2.0*(A*C + B*D);    // xy coefficient
 263             double EC = C*C + D*D;          // y^2 coefficient
 264 
 265             /*
 266              * There is a lot of calculus omitted here.
 267              *
 268              * Conceptually, in the interests of understanding the
 269              * terms that the calculus produced we can consider
 270              * that EA and EC end up providing the lengths along
 271              * the major axes and the hypot term ends up being an
 272              * adjustment for the additional length along the off-axis
 273              * angle of rotated or sheared ellipses as well as an
 274              * adjustment for the fact that the equation below
 275              * averages the two major axis lengths.  (Notice that
 276              * the hypot term contains a part which resolves to the
 277              * difference of these two axis lengths in the absence
 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.0);
 291 
 292             widthScale = (float)Math.sqrt(widthsquared);
 293         }
 294 
 295         return (lw / widthScale);
 296     }
 297 
 298     final void strokeTo(final RendererContext rdrCtx,
 299                         Shape src,
 300                         AffineTransform at,
 301                         float width,
 302                         NormMode norm,
 303                         int caps,
 304                         int join,
 305                         float miterlimit,
 306                         float[] dashes,
 307                         float dashphase,
 308                         PathConsumer2D pc2d)
 309     {
 310         // We use strokerat so that in Stroker and Dasher we can work only


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


 428                                          src.getPathIterator(at));
 429 
 430         pathTo(rdrCtx, pi, pc2d);
 431 
 432         /*
 433          * Pipeline seems to be:
 434          * shape.getPathIterator(at)
 435          * -> (NormalizingPathIterator)
 436          * -> (inverseDeltaTransformConsumer)
 437          * -> (Dasher)
 438          * -> Stroker
 439          * -> (deltaTransformConsumer)
 440          *
 441          * -> (CollinearSimplifier) to remove redundant segments
 442          *
 443          * -> pc2d = Renderer (bounding box)
 444          */
 445     }
 446 
 447     private static boolean nearZero(final double num) {
 448         return Math.abs(num) < 2.0 * Math.ulp(num);
 449     }
 450 
 451     abstract static class NormalizingPathIterator implements PathIterator {
 452 
 453         private PathIterator src;
 454 
 455         // the adjustment applied to the current position.
 456         private float curx_adjust, cury_adjust;
 457         // the adjustment applied to the last moveTo position.
 458         private float movx_adjust, movy_adjust;
 459 
 460         private final float[] tmp;
 461 
 462         NormalizingPathIterator(final float[] tmp) {
 463             this.tmp = tmp;
 464         }
 465 
 466         final NormalizingPathIterator init(final PathIterator src) {
 467             this.src = src;
 468             return this; // fluent API


 507 
 508             coord = coords[lastCoord];
 509             x_adjust = normCoord(coord); // new coord
 510             coords[lastCoord] = x_adjust;
 511             x_adjust -= coord;
 512 
 513             coord = coords[lastCoord + 1];
 514             y_adjust = normCoord(coord); // new coord
 515             coords[lastCoord + 1] = y_adjust;
 516             y_adjust -= coord;
 517 
 518             // now that the end points are done, normalize the control points
 519             switch(type) {
 520                 case PathIterator.SEG_MOVETO:
 521                     movx_adjust = x_adjust;
 522                     movy_adjust = y_adjust;
 523                     break;
 524                 case PathIterator.SEG_LINETO:
 525                     break;
 526                 case PathIterator.SEG_QUADTO:
 527                     coords[0] += (curx_adjust + x_adjust) / 2f;
 528                     coords[1] += (cury_adjust + y_adjust) / 2f;
 529                     break;
 530                 case PathIterator.SEG_CUBICTO:
 531                     coords[0] += curx_adjust;
 532                     coords[1] += cury_adjust;
 533                     coords[2] += x_adjust;
 534                     coords[3] += y_adjust;
 535                     break;
 536                 case PathIterator.SEG_CLOSE:
 537                     // handled earlier
 538                 default:
 539             }
 540             curx_adjust = x_adjust;
 541             cury_adjust = y_adjust;
 542             return type;
 543         }
 544 
 545         abstract float normCoord(final float coord);
 546 
 547         @Override
 548         public final int currentSegment(final double[] coords) {


 807 
 808                 // TODO: subdivide quad/cubic curves into monotonic curves ?
 809                 pathTo(rdrCtx, pi, r);
 810             } else {
 811                 // draw shape with given stroke:
 812                 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
 813                                          clip.getWidth(), clip.getHeight(),
 814                                          PathIterator.WIND_NON_ZERO);
 815 
 816                 strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r);
 817             }
 818             if (r.endRendering()) {
 819                 ptg = rdrCtx.ptg.init();
 820                 ptg.getBbox(bbox);
 821                 // note: do not returnRendererContext(rdrCtx)
 822                 // as it will be called later by MarlinTileGenerator.dispose()
 823                 r = null;
 824             }
 825         } finally {
 826             if (r != null) {
 827                 // dispose renderer:
 828                 r.dispose();
 829                 // recycle the RendererContext instance
 830                 MarlinRenderingEngine.returnRendererContext(rdrCtx);
 831             }
 832         }
 833 
 834         // Return null to cancel AA tile generation (nothing to render)
 835         return ptg;
 836     }
 837 
 838     @Override
 839     public final AATileGenerator getAATileGenerator(double x, double y,
 840                                                     double dx1, double dy1,
 841                                                     double dx2, double dy2,
 842                                                     double lw1, double lw2,
 843                                                     Region clip,
 844                                                     int[] bbox)
 845     {
 846         // REMIND: Deal with large coordinates!
 847         double ldx1, ldy1, ldx2, ldy2;
 848         boolean innerpgram = (lw1 > 0.0 && lw2 > 0.0);
 849 
 850         if (innerpgram) {
 851             ldx1 = dx1 * lw1;
 852             ldy1 = dy1 * lw1;
 853             ldx2 = dx2 * lw2;
 854             ldy2 = dy2 * lw2;
 855             x -= (ldx1 + ldx2) / 2.0;
 856             y -= (ldy1 + ldy2) / 2.0;
 857             dx1 += ldx1;
 858             dy1 += ldy1;
 859             dx2 += ldx2;
 860             dy2 += ldy2;
 861             if (lw1 > 1.0 && lw2 > 1.0) {
 862                 // Inner parallelogram was entirely consumed by stroke...
 863                 innerpgram = false;
 864             }
 865         } else {
 866             ldx1 = ldy1 = ldx2 = ldy2 = 0.0;
 867         }
 868 
 869         MarlinTileGenerator ptg = null;
 870         Renderer r = null;
 871 
 872         final RendererContext rdrCtx = getRendererContext();
 873         try {
 874             r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
 875                                          clip.getWidth(), clip.getHeight(),
 876                                          Renderer.WIND_EVEN_ODD);
 877 
 878             r.moveTo((float) x, (float) y);
 879             r.lineTo((float) (x+dx1), (float) (y+dy1));
 880             r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
 881             r.lineTo((float) (x+dx2), (float) (y+dy2));
 882             r.closePath();
 883 
 884             if (innerpgram) {
 885                 x += ldx1 + ldx2;
 886                 y += ldy1 + ldy2;
 887                 dx1 -= 2.0 * ldx1;
 888                 dy1 -= 2.0 * ldy1;
 889                 dx2 -= 2.0 * ldx2;
 890                 dy2 -= 2.0 * ldy2;
 891                 r.moveTo((float) x, (float) y);
 892                 r.lineTo((float) (x+dx1), (float) (y+dy1));
 893                 r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
 894                 r.lineTo((float) (x+dx2), (float) (y+dy2));
 895                 r.closePath();
 896             }
 897             r.pathDone();
 898 
 899             if (r.endRendering()) {
 900                 ptg = rdrCtx.ptg.init();
 901                 ptg.getBbox(bbox);
 902                 // note: do not returnRendererContext(rdrCtx)
 903                 // as it will be called later by MarlinTileGenerator.dispose()
 904                 r = null;
 905             }
 906         } finally {
 907             if (r != null) {
 908                 // dispose renderer:
 909                 r.dispose();
 910                 // recycle the RendererContext instance
 911                 MarlinRenderingEngine.returnRendererContext(rdrCtx);
 912             }
 913         }
 914 
 915         // Return null to cancel AA tile generation (nothing to render)
 916         return ptg;
 917     }
 918 
 919     /**
 920      * Returns the minimum pen width that the antialiasing rasterizer
 921      * can represent without dropouts occuring.
 922      * @since 1.7
 923      */
 924     @Override
 925     public float getMinimumAAPenSize() {
 926         return MIN_PEN_SIZE;
 927     }
 928 
 929     static {
 930         if (PathIterator.WIND_NON_ZERO != Renderer.WIND_NON_ZERO ||
 931             PathIterator.WIND_EVEN_ODD != Renderer.WIND_EVEN_ODD ||


1018 
1019         logInfo("Marlin software rasterizer           = ENABLED");
1020         logInfo("Version                              = ["
1021                 + Version.getVersion() + "]");
1022         logInfo("sun.java2d.renderer                  = "
1023                 + reClass);
1024         logInfo("sun.java2d.renderer.useThreadLocal   = "
1025                 + USE_THREAD_LOCAL);
1026         logInfo("sun.java2d.renderer.useRef           = "
1027                 + refType);
1028 
1029         logInfo("sun.java2d.renderer.edges            = "
1030                 + MarlinConst.INITIAL_EDGES_COUNT);
1031         logInfo("sun.java2d.renderer.pixelsize        = "
1032                 + MarlinConst.INITIAL_PIXEL_DIM);
1033 
1034         logInfo("sun.java2d.renderer.subPixel_log2_X  = "
1035                 + MarlinConst.SUBPIXEL_LG_POSITIONS_X);
1036         logInfo("sun.java2d.renderer.subPixel_log2_Y  = "
1037                 + MarlinConst.SUBPIXEL_LG_POSITIONS_Y);
1038         logInfo("sun.java2d.renderer.tileSize_log2    = "
1039                 + MarlinConst.TILE_SIZE_LG);
1040 
1041         logInfo("sun.java2d.renderer.blockSize_log2   = "
1042                 + MarlinConst.BLOCK_SIZE_LG);
1043 




1044         logInfo("sun.java2d.renderer.blockSize_log2   = "
1045                 + MarlinConst.BLOCK_SIZE_LG);
1046 
1047         // RLE / blockFlags settings
1048 
1049         logInfo("sun.java2d.renderer.forceRLE         = "
1050                 + MarlinProperties.isForceRLE());
1051         logInfo("sun.java2d.renderer.forceNoRLE       = "
1052                 + MarlinProperties.isForceNoRLE());
1053         logInfo("sun.java2d.renderer.useTileFlags     = "
1054                 + MarlinProperties.isUseTileFlags());
1055         logInfo("sun.java2d.renderer.useTileFlags.useHeuristics = "
1056                 + MarlinProperties.isUseTileFlagsWithHeuristics());
1057         logInfo("sun.java2d.renderer.rleMinWidth      = "
1058                 + MarlinCache.RLE_MIN_WIDTH);
1059 
1060         // optimisation parameters
1061         logInfo("sun.java2d.renderer.useSimplifier    = "
1062                 + MarlinConst.USE_SIMPLIFIER);
1063 
1064         // debugging parameters
1065         logInfo("sun.java2d.renderer.doStats          = "
1066                 + MarlinConst.DO_STATS);
1067         logInfo("sun.java2d.renderer.doMonitors       = "
1068                 + MarlinConst.DO_MONITORS);
1069         logInfo("sun.java2d.renderer.doChecks         = "
1070                 + MarlinConst.DO_CHECKS);
1071 
1072         // logging parameters
1073         logInfo("sun.java2d.renderer.useLogger        = "
1074                 + MarlinConst.USE_LOGGER);
1075         logInfo("sun.java2d.renderer.logCreateContext = "
1076                 + MarlinConst.LOG_CREATE_CONTEXT);
1077         logInfo("sun.java2d.renderer.logUnsafeMalloc  = "
1078                 + MarlinConst.LOG_UNSAFE_MALLOC);
1079 
1080         // quality settings







1081         logInfo("Renderer settings:");
1082         logInfo("CUB_COUNT_LG = " + Renderer.CUB_COUNT_LG);
1083         logInfo("CUB_DEC_BND  = " + Renderer.CUB_DEC_BND);
1084         logInfo("CUB_INC_BND  = " + Renderer.CUB_INC_BND);
1085         logInfo("QUAD_DEC_BND = " + Renderer.QUAD_DEC_BND);
1086 
1087         logInfo("INITIAL_EDGES_CAPACITY               = "
1088                 + MarlinConst.INITIAL_EDGES_CAPACITY);
1089         logInfo("INITIAL_CROSSING_COUNT               = "
1090                 + Renderer.INITIAL_CROSSING_COUNT);
1091 
1092         logInfo("=========================================================="
1093                 + "=====================");
1094     }
1095 
1096     /**
1097      * Get the RendererContext instance dedicated to the current thread
1098      * @return RendererContext instance
1099      */
1100     @SuppressWarnings({"unchecked"})
1101     static RendererContext getRendererContext() {
1102         final RendererContext rdrCtx = RDR_CTX_PROVIDER.acquire();


  27 
  28 import java.awt.BasicStroke;
  29 import java.awt.Shape;
  30 import java.awt.geom.AffineTransform;
  31 import java.awt.geom.Path2D;
  32 import java.awt.geom.PathIterator;
  33 import java.security.AccessController;
  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 final class MarlinRenderingEngine extends RenderingEngine
  48                                          implements MarlinConst
  49 {
  50     private static enum NormMode {
  51         ON_WITH_AA {
  52             @Override
  53             PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
  54                                                     final PathIterator src)
  55             {
  56                 // NormalizingPathIterator NearestPixelCenter:
  57                 return rdrCtx.nPCPathIterator.init(src);
  58             }
  59         },
  60         ON_NO_AA{
  61             @Override
  62             PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
  63                                                     final PathIterator src)
  64             {
  65                 // NearestPixel NormalizingPathIterator:
  66                 return rdrCtx.nPQPathIterator.init(src);
  67             }
  68         },
  69         OFF{
  70             @Override
  71             PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
  72                                                     final PathIterator src)
  73             {
  74                 // return original path iterator if normalization is disabled:
  75                 return src;
  76             }
  77         };
  78 
  79         abstract PathIterator getNormalizingPathIterator(RendererContext rdrCtx,
  80                                                          PathIterator src);
  81     }
  82 
  83     private static final float MIN_PEN_SIZE = 1.0f / NORM_SUBPIXELS;
  84 
  85     static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
  86     static final float LOWER_BND = -UPPER_BND;
  87 
  88     /**
  89      * Public constructor
  90      */
  91     public MarlinRenderingEngine() {
  92         super();
  93         logSettings(MarlinRenderingEngine.class.getName());
  94     }
  95 
  96     /**
  97      * Create a widened path as specified by the parameters.
  98      * <p>
  99      * The specified {@code src} {@link Shape} is widened according
 100      * to the specified attribute parameters as per the
 101      * {@link BasicStroke} specification.
 102      *
 103      * @param src the source path to be widened


 242             double C = at.getShearX();       // m01
 243             double B = at.getShearY();       // m10
 244             double D = at.getScaleY();       // m11
 245 
 246             /*
 247              * Given a 2 x 2 affine matrix [ A B ] such that
 248              *                             [ C D ]
 249              * v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to
 250              * find the maximum magnitude (norm) of the vector v'
 251              * with the constraint (x^2 + y^2 = 1).
 252              * The equation to maximize is
 253              *     |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2)
 254              * or  |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2).
 255              * Since sqrt is monotonic we can maximize |v'|^2
 256              * instead and plug in the substitution y = sqrt(1 - x^2).
 257              * Trigonometric equalities can then be used to get
 258              * rid of most of the sqrt terms.
 259              */
 260 
 261             double EA = A*A + B*B;          // x^2 coefficient
 262             double EB = 2.0d * (A*C + B*D); // xy coefficient
 263             double EC = C*C + D*D;          // y^2 coefficient
 264 
 265             /*
 266              * There is a lot of calculus omitted here.
 267              *
 268              * Conceptually, in the interests of understanding the
 269              * terms that the calculus produced we can consider
 270              * that EA and EC end up providing the lengths along
 271              * the major axes and the hypot term ends up being an
 272              * adjustment for the additional length along the off-axis
 273              * angle of rotated or sheared ellipses as well as an
 274              * adjustment for the fact that the equation below
 275              * averages the two major axis lengths.  (Notice that
 276              * the hypot term contains a part which resolves to the
 277              * difference of these two axis lengths in the absence
 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 = (float)Math.sqrt(widthsquared);
 293         }
 294 
 295         return (lw / widthScale);
 296     }
 297 
 298     final void strokeTo(final RendererContext rdrCtx,
 299                         Shape src,
 300                         AffineTransform at,
 301                         float width,
 302                         NormMode norm,
 303                         int caps,
 304                         int join,
 305                         float miterlimit,
 306                         float[] dashes,
 307                         float dashphase,
 308                         PathConsumer2D pc2d)
 309     {
 310         // We use strokerat so that in Stroker and Dasher we can work only


 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 
 328         if (at != null && !at.isIdentity()) {
 329             final double a = at.getScaleX();
 330             final double b = at.getShearX();
 331             final double c = at.getShearY();
 332             final double d = at.getScaleY();
 333             final double det = a * d - c * b;
 334 
 335             if (Math.abs(det) <= (2.0f * Float.MIN_VALUE)) {
 336                 // this rendering engine takes one dimensional curves and turns
 337                 // them into 2D shapes by giving them width.
 338                 // However, if everything is to be passed through a singular
 339                 // transformation, these 2D shapes will be squashed down to 1D
 340                 // again so, nothing can be drawn.
 341 
 342                 // Every path needs an initial moveTo and a pathDone. If these
 343                 // are not there this causes a SIGSEGV in libawt.so (at the time
 344                 // of writing of this comment (September 16, 2010)). Actually,
 345                 // I am not sure if the moveTo is necessary to avoid the SIGSEGV
 346                 // but the pathDone is definitely needed.
 347                 pc2d.moveTo(0.0f, 0.0f);
 348                 pc2d.pathDone();
 349                 return;
 350             }
 351 
 352             // If the transform is a constant multiple of an orthogonal transformation
 353             // then every length is just multiplied by a constant, so we just
 354             // need to transform input paths to stroker and tell stroker
 355             // the scaled width. This condition is satisfied if
 356             // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
 357             // leave a bit of room for error.
 358             if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
 359                 final float scale = (float) Math.sqrt(a*a + c*c);
 360 
 361                 if (dashes != null) {
 362                     recycleDashes = true;
 363                     dashLen = dashes.length;
 364                     dashes = rdrCtx.dasher.copyDashArray(dashes);










 365                     for (int i = 0; i < dashLen; i++) {
 366                         dashes[i] *= scale;
 367                     }
 368                     dashphase *= scale;
 369                 }
 370                 width *= scale;
 371 
 372                 // by now strokerat == null. Input paths to
 373                 // stroker (and maybe dasher) will have the full transform at
 374                 // applied to them and nothing will happen to the output paths.
 375             } else {
 376                 strokerat = at;
 377 
 378                 // by now strokerat == at. Input paths to
 379                 // stroker (and maybe dasher) will have the full transform at
 380                 // applied to them, then they will be normalized, and then
 381                 // the inverse of *only the non translation part of at* will
 382                 // be applied to the normalized paths. This won't cause problems
 383                 // in stroker, because, suppose at = T*A, where T is just the
 384                 // translation part of at, and A is the rest. T*A has already


 418                                          src.getPathIterator(at));
 419 
 420         pathTo(rdrCtx, pi, pc2d);
 421 
 422         /*
 423          * Pipeline seems to be:
 424          * shape.getPathIterator(at)
 425          * -> (NormalizingPathIterator)
 426          * -> (inverseDeltaTransformConsumer)
 427          * -> (Dasher)
 428          * -> Stroker
 429          * -> (deltaTransformConsumer)
 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.0d * Math.ulp(num);
 439     }
 440 
 441     abstract static class NormalizingPathIterator implements PathIterator {
 442 
 443         private PathIterator src;
 444 
 445         // the adjustment applied to the current position.
 446         private float curx_adjust, cury_adjust;
 447         // the adjustment applied to the last moveTo position.
 448         private float movx_adjust, movy_adjust;
 449 
 450         private final float[] tmp;
 451 
 452         NormalizingPathIterator(final float[] tmp) {
 453             this.tmp = tmp;
 454         }
 455 
 456         final NormalizingPathIterator init(final PathIterator src) {
 457             this.src = src;
 458             return this; // fluent API


 497 
 498             coord = coords[lastCoord];
 499             x_adjust = normCoord(coord); // new coord
 500             coords[lastCoord] = x_adjust;
 501             x_adjust -= coord;
 502 
 503             coord = coords[lastCoord + 1];
 504             y_adjust = normCoord(coord); // new coord
 505             coords[lastCoord + 1] = y_adjust;
 506             y_adjust -= coord;
 507 
 508             // now that the end points are done, normalize the control points
 509             switch(type) {
 510                 case PathIterator.SEG_MOVETO:
 511                     movx_adjust = x_adjust;
 512                     movy_adjust = y_adjust;
 513                     break;
 514                 case PathIterator.SEG_LINETO:
 515                     break;
 516                 case PathIterator.SEG_QUADTO:
 517                     coords[0] += (curx_adjust + x_adjust) / 2.0f;
 518                     coords[1] += (cury_adjust + y_adjust) / 2.0f;
 519                     break;
 520                 case PathIterator.SEG_CUBICTO:
 521                     coords[0] += curx_adjust;
 522                     coords[1] += cury_adjust;
 523                     coords[2] += x_adjust;
 524                     coords[3] += y_adjust;
 525                     break;
 526                 case PathIterator.SEG_CLOSE:
 527                     // handled earlier
 528                 default:
 529             }
 530             curx_adjust = x_adjust;
 531             cury_adjust = y_adjust;
 532             return type;
 533         }
 534 
 535         abstract float normCoord(final float coord);
 536 
 537         @Override
 538         public final int currentSegment(final double[] coords) {


 797 
 798                 // TODO: subdivide quad/cubic curves into monotonic curves ?
 799                 pathTo(rdrCtx, pi, r);
 800             } else {
 801                 // draw shape with given stroke:
 802                 r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
 803                                          clip.getWidth(), clip.getHeight(),
 804                                          PathIterator.WIND_NON_ZERO);
 805 
 806                 strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r);
 807             }
 808             if (r.endRendering()) {
 809                 ptg = rdrCtx.ptg.init();
 810                 ptg.getBbox(bbox);
 811                 // note: do not returnRendererContext(rdrCtx)
 812                 // as it will be called later by MarlinTileGenerator.dispose()
 813                 r = null;
 814             }
 815         } finally {
 816             if (r != null) {
 817                 // dispose renderer and recycle the RendererContext instance:
 818                 r.dispose();


 819             }
 820         }
 821 
 822         // Return null to cancel AA tile generation (nothing to render)
 823         return ptg;
 824     }
 825 
 826     @Override
 827     public final AATileGenerator getAATileGenerator(double x, double y,
 828                                                     double dx1, double dy1,
 829                                                     double dx2, double dy2,
 830                                                     double lw1, double lw2,
 831                                                     Region clip,
 832                                                     int[] bbox)
 833     {
 834         // REMIND: Deal with large coordinates!
 835         double ldx1, ldy1, ldx2, ldy2;
 836         boolean innerpgram = (lw1 > 0.0d && lw2 > 0.0d);
 837 
 838         if (innerpgram) {
 839             ldx1 = dx1 * lw1;
 840             ldy1 = dy1 * lw1;
 841             ldx2 = dx2 * lw2;
 842             ldy2 = dy2 * lw2;
 843             x -= (ldx1 + ldx2) / 2.0d;
 844             y -= (ldy1 + ldy2) / 2.0d;
 845             dx1 += ldx1;
 846             dy1 += ldy1;
 847             dx2 += ldx2;
 848             dy2 += ldy2;
 849             if (lw1 > 1.0d && lw2 > 1.0d) {
 850                 // Inner parallelogram was entirely consumed by stroke...
 851                 innerpgram = false;
 852             }
 853         } else {
 854             ldx1 = ldy1 = ldx2 = ldy2 = 0.0d;
 855         }
 856 
 857         MarlinTileGenerator ptg = null;
 858         Renderer r = null;
 859 
 860         final RendererContext rdrCtx = getRendererContext();
 861         try {
 862             r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
 863                                          clip.getWidth(), clip.getHeight(),
 864                                          Renderer.WIND_EVEN_ODD);
 865 
 866             r.moveTo((float) x, (float) y);
 867             r.lineTo((float) (x+dx1), (float) (y+dy1));
 868             r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
 869             r.lineTo((float) (x+dx2), (float) (y+dy2));
 870             r.closePath();
 871 
 872             if (innerpgram) {
 873                 x += ldx1 + ldx2;
 874                 y += ldy1 + ldy2;
 875                 dx1 -= 2.0d * ldx1;
 876                 dy1 -= 2.0d * ldy1;
 877                 dx2 -= 2.0d * ldx2;
 878                 dy2 -= 2.0d * ldy2;
 879                 r.moveTo((float) x, (float) y);
 880                 r.lineTo((float) (x+dx1), (float) (y+dy1));
 881                 r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
 882                 r.lineTo((float) (x+dx2), (float) (y+dy2));
 883                 r.closePath();
 884             }
 885             r.pathDone();
 886 
 887             if (r.endRendering()) {
 888                 ptg = rdrCtx.ptg.init();
 889                 ptg.getBbox(bbox);
 890                 // note: do not returnRendererContext(rdrCtx)
 891                 // as it will be called later by MarlinTileGenerator.dispose()
 892                 r = null;
 893             }
 894         } finally {
 895             if (r != null) {
 896                 // dispose renderer and recycle the RendererContext instance:
 897                 r.dispose();


 898             }
 899         }
 900 
 901         // Return null to cancel AA tile generation (nothing to render)
 902         return ptg;
 903     }
 904 
 905     /**
 906      * Returns the minimum pen width that the antialiasing rasterizer
 907      * can represent without dropouts occuring.
 908      * @since 1.7
 909      */
 910     @Override
 911     public float getMinimumAAPenSize() {
 912         return MIN_PEN_SIZE;
 913     }
 914 
 915     static {
 916         if (PathIterator.WIND_NON_ZERO != Renderer.WIND_NON_ZERO ||
 917             PathIterator.WIND_EVEN_ODD != Renderer.WIND_EVEN_ODD ||


1004 
1005         logInfo("Marlin software rasterizer           = ENABLED");
1006         logInfo("Version                              = ["
1007                 + Version.getVersion() + "]");
1008         logInfo("sun.java2d.renderer                  = "
1009                 + reClass);
1010         logInfo("sun.java2d.renderer.useThreadLocal   = "
1011                 + USE_THREAD_LOCAL);
1012         logInfo("sun.java2d.renderer.useRef           = "
1013                 + refType);
1014 
1015         logInfo("sun.java2d.renderer.edges            = "
1016                 + MarlinConst.INITIAL_EDGES_COUNT);
1017         logInfo("sun.java2d.renderer.pixelsize        = "
1018                 + MarlinConst.INITIAL_PIXEL_DIM);
1019 
1020         logInfo("sun.java2d.renderer.subPixel_log2_X  = "
1021                 + MarlinConst.SUBPIXEL_LG_POSITIONS_X);
1022         logInfo("sun.java2d.renderer.subPixel_log2_Y  = "
1023                 + MarlinConst.SUBPIXEL_LG_POSITIONS_Y);





1024 
1025         logInfo("sun.java2d.renderer.tileSize_log2    = "
1026                 + MarlinConst.TILE_H_LG);
1027         logInfo("sun.java2d.renderer.tileWidth_log2   = "
1028                 + MarlinConst.TILE_W_LG);
1029         logInfo("sun.java2d.renderer.blockSize_log2   = "
1030                 + MarlinConst.BLOCK_SIZE_LG);
1031 
1032         // RLE / blockFlags settings
1033 
1034         logInfo("sun.java2d.renderer.forceRLE         = "
1035                 + MarlinProperties.isForceRLE());
1036         logInfo("sun.java2d.renderer.forceNoRLE       = "
1037                 + MarlinProperties.isForceNoRLE());
1038         logInfo("sun.java2d.renderer.useTileFlags     = "
1039                 + MarlinProperties.isUseTileFlags());
1040         logInfo("sun.java2d.renderer.useTileFlags.useHeuristics = "
1041                 + MarlinProperties.isUseTileFlagsWithHeuristics());
1042         logInfo("sun.java2d.renderer.rleMinWidth      = "
1043                 + MarlinCache.RLE_MIN_WIDTH);
1044 
1045         // optimisation parameters
1046         logInfo("sun.java2d.renderer.useSimplifier    = "
1047                 + MarlinConst.USE_SIMPLIFIER);
1048 
1049         // debugging parameters
1050         logInfo("sun.java2d.renderer.doStats          = "
1051                 + MarlinConst.DO_STATS);
1052         logInfo("sun.java2d.renderer.doMonitors       = "
1053                 + MarlinConst.DO_MONITORS);
1054         logInfo("sun.java2d.renderer.doChecks         = "
1055                 + MarlinConst.DO_CHECKS);
1056 
1057         // logging parameters
1058         logInfo("sun.java2d.renderer.useLogger        = "
1059                 + MarlinConst.USE_LOGGER);
1060         logInfo("sun.java2d.renderer.logCreateContext = "
1061                 + MarlinConst.LOG_CREATE_CONTEXT);
1062         logInfo("sun.java2d.renderer.logUnsafeMalloc  = "
1063                 + MarlinConst.LOG_UNSAFE_MALLOC);
1064 
1065         // quality settings
1066         logInfo("sun.java2d.renderer.cubic_dec_d2     = "
1067                 + MarlinProperties.getCubicDecD2());
1068         logInfo("sun.java2d.renderer.cubic_inc_d1     = "
1069                 + MarlinProperties.getCubicIncD1());
1070         logInfo("sun.java2d.renderer.quad_dec_d2      = "
1071                 + MarlinProperties.getQuadDecD2());
1072 
1073         logInfo("Renderer settings:");

1074         logInfo("CUB_DEC_BND  = " + Renderer.CUB_DEC_BND);
1075         logInfo("CUB_INC_BND  = " + Renderer.CUB_INC_BND);
1076         logInfo("QUAD_DEC_BND = " + Renderer.QUAD_DEC_BND);
1077 
1078         logInfo("INITIAL_EDGES_CAPACITY               = "
1079                 + MarlinConst.INITIAL_EDGES_CAPACITY);
1080         logInfo("INITIAL_CROSSING_COUNT               = "
1081                 + Renderer.INITIAL_CROSSING_COUNT);
1082 
1083         logInfo("=========================================================="
1084                 + "=====================");
1085     }
1086 
1087     /**
1088      * Get the RendererContext instance dedicated to the current thread
1089      * @return RendererContext instance
1090      */
1091     @SuppressWarnings({"unchecked"})
1092     static RendererContext getRendererContext() {
1093         final RendererContext rdrCtx = RDR_CTX_PROVIDER.acquire();
< prev index next >