jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java
changeset 6997 3642614e2282
parent 6285 96a57de47def
child 7668 d4a77089c587
child 7745 ebd6382e93fd
equal deleted inserted replaced
6996:5122ee0dcc92 6997:3642614e2282
    25 
    25 
    26 package sun.java2d.pisces;
    26 package sun.java2d.pisces;
    27 
    27 
    28 import java.awt.Shape;
    28 import java.awt.Shape;
    29 import java.awt.BasicStroke;
    29 import java.awt.BasicStroke;
    30 import java.awt.geom.FlatteningPathIterator;
    30 import java.awt.geom.NoninvertibleTransformException;
    31 import java.awt.geom.Path2D;
    31 import java.awt.geom.Path2D;
    32 import java.awt.geom.AffineTransform;
    32 import java.awt.geom.AffineTransform;
    33 import java.awt.geom.PathIterator;
    33 import java.awt.geom.PathIterator;
    34 
    34 
    35 import sun.awt.geom.PathConsumer2D;
    35 import sun.awt.geom.PathConsumer2D;
    36 import sun.java2d.pipe.Region;
    36 import sun.java2d.pipe.Region;
    37 import sun.java2d.pipe.RenderingEngine;
    37 import sun.java2d.pipe.RenderingEngine;
    38 import sun.java2d.pipe.AATileGenerator;
    38 import sun.java2d.pipe.AATileGenerator;
    39 
    39 
    40 public class PiscesRenderingEngine extends RenderingEngine {
    40 public class PiscesRenderingEngine extends RenderingEngine {
    41     public static double defaultFlat = 0.1;
       
    42 
       
    43     private static enum NormMode {OFF, ON_NO_AA, ON_WITH_AA}
    41     private static enum NormMode {OFF, ON_NO_AA, ON_WITH_AA}
    44 
    42 
    45     /**
    43     /**
    46      * Create a widened path as specified by the parameters.
    44      * Create a widened path as specified by the parameters.
    47      * <p>
    45      * <p>
    76                  caps,
    74                  caps,
    77                  join,
    75                  join,
    78                  miterlimit,
    76                  miterlimit,
    79                  dashes,
    77                  dashes,
    80                  dashphase,
    78                  dashphase,
    81                  new LineSink() {
    79                  new PathConsumer2D() {
    82                      public void moveTo(float x0, float y0) {
    80                      public void moveTo(float x0, float y0) {
    83                          p2d.moveTo(x0, y0);
    81                          p2d.moveTo(x0, y0);
    84                      }
    82                      }
    85                      public void lineJoin() {}
       
    86                      public void lineTo(float x1, float y1) {
    83                      public void lineTo(float x1, float y1) {
    87                          p2d.lineTo(x1, y1);
    84                          p2d.lineTo(x1, y1);
    88                      }
    85                      }
    89                      public void close() {
    86                      public void closePath() {
    90                          p2d.closePath();
    87                          p2d.closePath();
    91                      }
    88                      }
    92                      public void end() {}
    89                      public void pathDone() {}
       
    90                      public void curveTo(float x1, float y1,
       
    91                                          float x2, float y2,
       
    92                                          float x3, float y3) {
       
    93                          p2d.curveTo(x1, y1, x2, y2, x3, y3);
       
    94                      }
       
    95                      public void quadTo(float x1, float y1, float x2, float y2) {
       
    96                          p2d.quadTo(x1, y1, x2, y2);
       
    97                      }
       
    98                      public long getNativeConsumer() {
       
    99                          throw new InternalError("Not using a native peer");
       
   100                      }
    93                  });
   101                  });
    94 
       
    95         return p2d;
   102         return p2d;
    96     }
   103     }
    97 
   104 
    98     /**
   105     /**
    99      * Sends the geometry for a widened path as specified by the parameters
   106      * Sends the geometry for a widened path as specified by the parameters
   131                          final PathConsumer2D consumer)
   138                          final PathConsumer2D consumer)
   132     {
   139     {
   133         NormMode norm = (normalize) ?
   140         NormMode norm = (normalize) ?
   134                 ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
   141                 ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
   135                 : NormMode.OFF;
   142                 : NormMode.OFF;
   136         strokeTo(src, at, bs, thin, norm, antialias,
   143         strokeTo(src, at, bs, thin, norm, antialias, consumer);
   137                  new LineSink() {
       
   138                      public void moveTo(float x0, float y0) {
       
   139                          consumer.moveTo(x0, y0);
       
   140                      }
       
   141                      public void lineJoin() {}
       
   142                      public void lineTo(float x1, float y1) {
       
   143                          consumer.lineTo(x1, y1);
       
   144                      }
       
   145                      public void close() {
       
   146                          consumer.closePath();
       
   147                      }
       
   148                      public void end() {
       
   149                          consumer.pathDone();
       
   150                      }
       
   151                  });
       
   152     }
   144     }
   153 
   145 
   154     void strokeTo(Shape src,
   146     void strokeTo(Shape src,
   155                   AffineTransform at,
   147                   AffineTransform at,
   156                   BasicStroke bs,
   148                   BasicStroke bs,
   157                   boolean thin,
   149                   boolean thin,
   158                   NormMode normalize,
   150                   NormMode normalize,
   159                   boolean antialias,
   151                   boolean antialias,
   160                   LineSink lsink)
   152                   PathConsumer2D pc2d)
   161     {
   153     {
   162         float lw;
   154         float lw;
   163         if (thin) {
   155         if (thin) {
   164             if (antialias) {
   156             if (antialias) {
   165                 lw = userSpaceLineWidth(at, 0.5f);
   157                 lw = userSpaceLineWidth(at, 0.5f);
   176                  bs.getEndCap(),
   168                  bs.getEndCap(),
   177                  bs.getLineJoin(),
   169                  bs.getLineJoin(),
   178                  bs.getMiterLimit(),
   170                  bs.getMiterLimit(),
   179                  bs.getDashArray(),
   171                  bs.getDashArray(),
   180                  bs.getDashPhase(),
   172                  bs.getDashPhase(),
   181                  lsink);
   173                  pc2d);
   182     }
   174     }
   183 
   175 
   184     private float userSpaceLineWidth(AffineTransform at, float lw) {
   176     private float userSpaceLineWidth(AffineTransform at, float lw) {
   185 
   177 
   186         double widthScale;
   178         double widthScale;
   254                   int caps,
   246                   int caps,
   255                   int join,
   247                   int join,
   256                   float miterlimit,
   248                   float miterlimit,
   257                   float dashes[],
   249                   float dashes[],
   258                   float dashphase,
   250                   float dashphase,
   259                   LineSink lsink)
   251                   PathConsumer2D pc2d)
   260     {
   252     {
   261         float a00 = 1f, a01 = 0f, a10 = 0f, a11 = 1f;
   253         // We use inat and outat so that in Stroker and Dasher we can work only
       
   254         // with the pre-transformation coordinates. This will repeat a lot of
       
   255         // computations done in the path iterator, but the alternative is to
       
   256         // work with transformed paths and compute untransformed coordinates
       
   257         // as needed. This would be faster but I do not think the complexity
       
   258         // of working with both untransformed and transformed coordinates in
       
   259         // the same code is worth it.
       
   260         // However, if a path's width is constant after a transformation,
       
   261         // we can skip all this untransforming.
       
   262 
       
   263         // If normalization is off we save some transformations by not
       
   264         // transforming the input to pisces. Instead, we apply the
       
   265         // transformation after the path processing has been done.
       
   266         // We can't do this if normalization is on, because it isn't a good
       
   267         // idea to normalize before the transformation is applied.
       
   268         AffineTransform inat = null;
       
   269         AffineTransform outat = null;
       
   270 
       
   271         PathIterator pi = null;
       
   272 
   262         if (at != null && !at.isIdentity()) {
   273         if (at != null && !at.isIdentity()) {
   263             a00 = (float)at.getScaleX();
   274             final double a = at.getScaleX();
   264             a01 = (float)at.getShearX();
   275             final double b = at.getShearX();
   265             a10 = (float)at.getShearY();
   276             final double c = at.getShearY();
   266             a11 = (float)at.getScaleY();
   277             final double d = at.getScaleY();
   267         }
   278             final double det = a * d - c * b;
   268         lsink = new Stroker(lsink, width, caps, join, miterlimit, a00, a01, a10, a11);
   279             if (Math.abs(det) <= 2 * Float.MIN_VALUE) {
       
   280                 // this rendering engine takes one dimensional curves and turns
       
   281                 // them into 2D shapes by giving them width.
       
   282                 // However, if everything is to be passed through a singular
       
   283                 // transformation, these 2D shapes will be squashed down to 1D
       
   284                 // again so, nothing can be drawn.
       
   285 
       
   286                 // Every path needs an initial moveTo and a pathDone. If these
       
   287                 // aren't there this causes a SIGSEV in libawt.so (at the time
       
   288                 // of writing of this comment (September 16, 2010)). Actually,
       
   289                 // I'm not sure if the moveTo is necessary to avoid the SIGSEV
       
   290                 // but the pathDone is definitely needed.
       
   291                 pc2d.moveTo(0, 0);
       
   292                 pc2d.pathDone();
       
   293                 return;
       
   294             }
       
   295 
       
   296             // If the transform is a constant multiple of an orthogonal transformation
       
   297             // then every length is just multiplied by a constant, so we just
       
   298             // need to transform input paths to stroker and tell stroker
       
   299             // the scaled width. This condition is satisfied if
       
   300             // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
       
   301             // leave a bit of room for error.
       
   302             if (nearZero(a*b + c*d, 2) && nearZero(a*a+c*c - (b*b+d*d), 2)) {
       
   303                 double scale = Math.sqrt(a*a + c*c);
       
   304                 if (dashes != null) {
       
   305                     dashes = java.util.Arrays.copyOf(dashes, dashes.length);
       
   306                     for (int i = 0; i < dashes.length; i++) {
       
   307                         dashes[i] = (float)(scale * dashes[i]);
       
   308                     }
       
   309                     dashphase = (float)(scale * dashphase);
       
   310                 }
       
   311                 width = (float)(scale * width);
       
   312                 pi = src.getPathIterator(at);
       
   313                 if (normalize != NormMode.OFF) {
       
   314                     pi = new NormalizingPathIterator(pi, normalize);
       
   315                 }
       
   316                 // leave inat and outat null.
       
   317             } else {
       
   318                 // We only need the inverse if normalization is on. Otherwise
       
   319                 // we just don't transform the input paths, do all the stroking
       
   320                 // and then transform out output (instead of making PathIterator
       
   321                 // apply the transformation, us applying the inverse, and then
       
   322                 // us applying the transform again to our output).
       
   323                 outat = at;
       
   324                 if (normalize != NormMode.OFF) {
       
   325                     try {
       
   326                         inat = outat.createInverse();
       
   327                     } catch (NoninvertibleTransformException e) {
       
   328                         // we made sure this can't happen
       
   329                         e.printStackTrace();
       
   330                     }
       
   331                     pi = src.getPathIterator(at);
       
   332                     pi = new NormalizingPathIterator(pi, normalize);
       
   333                 } else {
       
   334                     pi = src.getPathIterator(null);
       
   335                 }
       
   336             }
       
   337         } else {
       
   338             // either at is null or it's the identity. In either case
       
   339             // we don't transform the path.
       
   340             pi = src.getPathIterator(null);
       
   341             if (normalize != NormMode.OFF) {
       
   342                 pi = new NormalizingPathIterator(pi, normalize);
       
   343             }
       
   344         }
       
   345 
       
   346         pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, outat);
       
   347         pc2d = new Stroker(pc2d, width, caps, join, miterlimit);
   269         if (dashes != null) {
   348         if (dashes != null) {
   270             lsink = new Dasher(lsink, dashes, dashphase, a00, a01, a10, a11);
   349             pc2d = new Dasher(pc2d, dashes, dashphase);
   271         }
   350         }
   272         PathIterator pi;
   351         pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, inat);
   273         if (normalize != NormMode.OFF) {
   352 
   274             pi = new FlatteningPathIterator(
   353         pathTo(pi, pc2d);
   275                     new NormalizingPathIterator(src.getPathIterator(at), normalize),
   354     }
   276                     defaultFlat);
   355 
   277         } else {
   356     private static boolean nearZero(double num, int nulps) {
   278             pi = src.getPathIterator(at, defaultFlat);
   357         return Math.abs(num) < nulps * Math.ulp(num);
   279         }
       
   280         pathTo(pi, lsink);
       
   281     }
   358     }
   282 
   359 
   283     private static class NormalizingPathIterator implements PathIterator {
   360     private static class NormalizingPathIterator implements PathIterator {
   284 
   361 
   285         private final PathIterator src;
   362         private final PathIterator src;
   335             default:
   412             default:
   336                 throw new InternalError("Unrecognized curve type");
   413                 throw new InternalError("Unrecognized curve type");
   337             }
   414             }
   338 
   415 
   339             // normalize endpoint
   416             // normalize endpoint
   340             float x_adjust = (float)Math.floor(coords[lastCoord] + lval) + rval -
   417             float x_adjust = (float)Math.floor(coords[lastCoord] + lval) +
   341                          coords[lastCoord];
   418                          rval - coords[lastCoord];
   342             float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) + rval -
   419             float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) +
   343                          coords[lastCoord + 1];
   420                          rval - coords[lastCoord + 1];
   344 
   421 
   345             coords[lastCoord    ] += x_adjust;
   422             coords[lastCoord    ] += x_adjust;
   346             coords[lastCoord + 1] += y_adjust;
   423             coords[lastCoord + 1] += y_adjust;
   347 
   424 
   348             // now that the end points are done, normalize the control points
   425             // now that the end points are done, normalize the control points
   391         public void next() {
   468         public void next() {
   392             src.next();
   469             src.next();
   393         }
   470         }
   394     }
   471     }
   395 
   472 
   396     void pathTo(PathIterator pi, LineSink lsink) {
   473     static void pathTo(PathIterator pi, PathConsumer2D pc2d) {
   397         float coords[] = new float[2];
   474         RenderingEngine.feedConsumer(pi, pc2d);
   398         while (!pi.isDone()) {
   475         pc2d.pathDone();
   399             switch (pi.currentSegment(coords)) {
       
   400             case PathIterator.SEG_MOVETO:
       
   401                 lsink.moveTo(coords[0], coords[1]);
       
   402                 break;
       
   403             case PathIterator.SEG_LINETO:
       
   404                 lsink.lineJoin();
       
   405                 lsink.lineTo(coords[0], coords[1]);
       
   406                 break;
       
   407             case PathIterator.SEG_CLOSE:
       
   408                 lsink.lineJoin();
       
   409                 lsink.close();
       
   410                 break;
       
   411             default:
       
   412                 throw new InternalError("unknown flattened segment type");
       
   413             }
       
   414             pi.next();
       
   415         }
       
   416         lsink.end();
       
   417     }
   476     }
   418 
   477 
   419     /**
   478     /**
   420      * Construct an antialiased tile generator for the given shape with
   479      * Construct an antialiased tile generator for the given shape with
   421      * the given rendering attributes and store the bounds of the tile
   480      * the given rendering attributes and store the bounds of the tile
   469                                               BasicStroke bs,
   528                                               BasicStroke bs,
   470                                               boolean thin,
   529                                               boolean thin,
   471                                               boolean normalize,
   530                                               boolean normalize,
   472                                               int bbox[])
   531                                               int bbox[])
   473     {
   532     {
   474         PiscesCache pc = PiscesCache.createInstance();
       
   475         Renderer r;
   533         Renderer r;
   476         NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
   534         NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
   477         if (bs == null) {
   535         if (bs == null) {
   478             PathIterator pi;
   536             PathIterator pi;
   479             if (normalize) {
   537             if (normalize) {
   480                 pi = new FlatteningPathIterator(
   538                 pi = new NormalizingPathIterator(s.getPathIterator(at), norm);
   481                         new NormalizingPathIterator(s.getPathIterator(at), norm),
       
   482                         defaultFlat);
       
   483             } else {
   539             } else {
   484                 pi = s.getPathIterator(at, defaultFlat);
   540                 pi = s.getPathIterator(at);
   485             }
   541             }
   486             r = new Renderer(3, 3,
   542             r = new Renderer(3, 3,
   487                              clip.getLoX(), clip.getLoY(),
   543                              clip.getLoX(), clip.getLoY(),
   488                              clip.getWidth(), clip.getHeight(),
   544                              clip.getWidth(), clip.getHeight(),
   489                              pi.getWindingRule(), pc);
   545                              pi.getWindingRule());
   490             pathTo(pi, r);
   546             pathTo(pi, r);
   491         } else {
   547         } else {
   492             r = new Renderer(3, 3,
   548             r = new Renderer(3, 3,
   493                              clip.getLoX(), clip.getLoY(),
   549                              clip.getLoX(), clip.getLoY(),
   494                              clip.getWidth(), clip.getHeight(),
   550                              clip.getWidth(), clip.getHeight(),
   495                              PathIterator.WIND_NON_ZERO, pc);
   551                              PathIterator.WIND_NON_ZERO);
   496             strokeTo(s, at, bs, thin, norm, true, r);
   552             strokeTo(s, at, bs, thin, norm, true, r);
   497         }
   553         }
   498         r.endRendering();
   554         r.endRendering();
   499         PiscesTileGenerator ptg = new PiscesTileGenerator(pc, r.MAX_AA_ALPHA);
   555         PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA);
   500         ptg.getBbox(bbox);
   556         ptg.getBbox(bbox);
   501         return ptg;
   557         return ptg;
   502     }
   558     }
   503 
   559 
   504     /**
   560     /**