< prev index next >

modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/MarlinPrismUtils.java

Print this page




  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.prism.impl.shape;
  27 
  28 
  29 import com.sun.javafx.geom.PathConsumer2D;
  30 import com.sun.javafx.geom.PathIterator;
  31 import com.sun.javafx.geom.Path2D;
  32 import com.sun.javafx.geom.Rectangle;
  33 import com.sun.javafx.geom.Shape;
  34 import com.sun.javafx.geom.transform.BaseTransform;
  35 import com.sun.marlin.MarlinConst;

  36 import com.sun.marlin.MarlinRenderer;
  37 import com.sun.marlin.RendererContext;

  38 import com.sun.marlin.TransformingPathConsumer2D;
  39 import com.sun.prism.BasicStroke;
  40 
  41 public final class MarlinPrismUtils {
  42 
  43     private static final boolean FORCE_NO_AA = false;
  44 
  45     static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
  46     static final float LOWER_BND = -UPPER_BND;
  47 



  48     /**
  49      * Private constructor to prevent instantiation.
  50      */
  51     private MarlinPrismUtils() {
  52     }
  53 
  54     private static PathConsumer2D initRenderer(
  55             final RendererContext rdrCtx,
  56             final BasicStroke stroke,
  57             final BaseTransform tx,
  58             final Rectangle clip,
  59             final int pirule,
  60             final MarlinRenderer renderer)
  61     {
  62         final int oprule = (stroke == null && pirule == PathIterator.WIND_EVEN_ODD) ?
  63             MarlinRenderer.WIND_EVEN_ODD : MarlinRenderer.WIND_NON_ZERO;
  64 
  65         // We use strokerat so that in Stroker and Dasher we can work only
  66         // with the pre-transformation coordinates. This will repeat a lot of
  67         // computations done in the path iterator, but the alternative is to
  68         // work with transformed paths and compute untransformed coordinates
  69         // as needed. This would be faster but I do not think the complexity
  70         // of working with both untransformed and transformed coordinates in
  71         // the same code is worth it.
  72         // However, if a path's width is constant after a transformation,
  73         // we can skip all this untransforming.
  74 
  75         // As pathTo() will check transformed coordinates for invalid values
  76         // (NaN / Infinity) to ignore such points, it is necessary to apply the
  77         // transformation before the path processing.
  78         BaseTransform strokerTx = null;
  79 
  80         int dashLen = -1;
  81         boolean recycleDashes = false;
  82 
  83         float width = 0.0f, dashphase = 0.0f;
  84         float[] dashes = null;
  85 
  86         if (stroke != null) {
  87             width = stroke.getLineWidth();
  88             dashes = stroke.getDashArray();
  89             dashphase = stroke.getDashPhase();
  90 
  91             if (tx != null && !tx.isIdentity()) {
  92                 final double a = tx.getMxx();
  93                 final double b = tx.getMxy();
  94                 final double c = tx.getMyx();
  95                 final double d = tx.getMyy();
  96 
  97                 // If the transform is a constant multiple of an orthogonal transformation
  98                 // then every length is just multiplied by a constant, so we just
  99                 // need to transform input paths to stroker and tell stroker
 100                 // the scaled width. This condition is satisfied if
 101                 // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
 102                 // leave a bit of room for error.
 103                 if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
 104                     final float scale = (float) Math.sqrt(a*a + c*c);
 105 
 106                     if (dashes != null) {
 107                         recycleDashes = true;
 108                         dashLen = dashes.length;
 109                         dashes = rdrCtx.dasher.copyDashArray(dashes);
 110                         for (int i = 0; i < dashLen; i++) {
 111                             dashes[i] *= scale;
 112                         }
 113                         dashphase *= scale;
 114                     }
 115                     width *= scale;
 116 
 117                     // by now strokerat == null. Input paths to
 118                     // stroker (and maybe dasher) will have the full transform tx
 119                     // applied to them and nothing will happen to the output paths.
 120                 } else {
 121                     strokerTx = tx;
 122 
 123                     // by now strokerat == tx. Input paths to
 124                     // stroker (and maybe dasher) will have the full transform tx
 125                     // applied to them, then they will be normalized, and then
 126                     // the inverse of *only the non translation part of tx* will
 127                     // be applied to the normalized paths. This won't cause problems
 128                     // in stroker, because, suppose tx = T*A, where T is just the
 129                     // translation part of tx, and A is the rest. T*A has already
 130                     // been applied to Stroker/Dasher's input. Then Ainv will be
 131                     // applied. Ainv*T*A is not equal to T, but it is a translation,
 132                     // which means that none of stroker's assumptions about its
 133                     // input will be violated. After all this, A will be applied
 134                     // to stroker's output.
 135                 }
 136             }
 137         }
 138 
 139         PathConsumer2D pc = renderer.init(clip.x, clip.y, clip.width, clip.height, oprule);


























 140 
 141         if (MarlinConst.USE_SIMPLIFIER) {
 142             // Use simplifier after stroker before Renderer
 143             // to remove collinear segments (notably due to cap square)
 144             pc = rdrCtx.simplifier.init(pc);
 145         }
 146 
 147         final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
 148         pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx);
 149 
 150         if (stroke != null) {

 151             pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(),
 152                     stroke.getLineJoin(), stroke.getMiterLimit());

 153 
 154             if (dashes != null) {
 155                 if (!recycleDashes) {
 156                     dashLen = dashes.length;
 157                 }
 158                 pc = rdrCtx.dasher.init(pc, dashes, dashLen, dashphase, recycleDashes);








 159             }
 160         }
 161 
 162         pc = transformerPC2D.inverseDeltaTransformConsumer(pc, strokerTx);
 163 




 164         /*
 165          * Pipeline seems to be:
 166          * shape.getPathIterator(tx)
 167          * -> (inverseDeltaTransformConsumer)
 168          * -> (Dasher)
 169          * -> Stroker
 170          * -> (deltaTransformConsumer)
 171          *
 172          * -> (CollinearSimplifier) to remove redundant segments
 173          *
 174          * -> pc2d = Renderer (bounding box)
 175          */
 176         return pc;
 177     }
 178 
 179     private static boolean nearZero(final double num) {
 180         return Math.abs(num) < 2.0d * Math.ulp(num);
 181     }
 182 
 183     public static MarlinRenderer setupRenderer(


 341 
 342         // mark context as CLEAN:
 343         rdrCtx.dirty = false;
 344     }
 345 
 346     private static void feedConsumer(final RendererContext rdrCtx,
 347                                      final Path2D p2d,
 348                                      final BaseTransform xform,
 349                                      final PathConsumer2D pc2d)
 350     {
 351         // mark context as DIRTY:
 352         rdrCtx.dirty = true;
 353 
 354         final float[] coords = rdrCtx.float6;
 355 
 356         // ported from DuctusRenderingEngine.feedConsumer() but simplified:
 357         // - removed skip flag = !subpathStarted
 358         // - removed pathClosed (ie subpathStarted not set to false)
 359         boolean subpathStarted = false;
 360 
 361         final float pCoords[] = p2d.getFloatCoordsNoClone();
 362         final byte pTypes[] = p2d.getCommandsNoClone();
 363         final int nsegs = p2d.getNumCommands();
 364 
 365         for (int i = 0, coff = 0; i < nsegs; i++) {
 366             switch (pTypes[i]) {
 367             case PathIterator.SEG_MOVETO:
 368                 if (xform == null) {
 369                     coords[0] = pCoords[coff];
 370                     coords[1] = pCoords[coff+1];
 371                 } else {
 372                     xform.transform(pCoords, coff, coords, 0, 1);
 373                 }
 374                 coff += 2;
 375                 /* Checking SEG_MOVETO coordinates if they are out of the
 376                  * [LOWER_BND, UPPER_BND] range. This check also handles NaN
 377                  * and Infinity values. Skipping next path segment in case of
 378                  * invalid data.
 379                  */
 380                 if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
 381                     coords[1] < UPPER_BND && coords[1] > LOWER_BND)
 382                 {




  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.prism.impl.shape;
  27 
  28 
  29 import com.sun.javafx.geom.PathConsumer2D;
  30 import com.sun.javafx.geom.PathIterator;
  31 import com.sun.javafx.geom.Path2D;
  32 import com.sun.javafx.geom.Rectangle;
  33 import com.sun.javafx.geom.Shape;
  34 import com.sun.javafx.geom.transform.BaseTransform;
  35 import com.sun.marlin.MarlinConst;
  36 import com.sun.marlin.MarlinProperties;
  37 import com.sun.marlin.MarlinRenderer;
  38 import com.sun.marlin.RendererContext;
  39 import com.sun.marlin.Stroker;
  40 import com.sun.marlin.TransformingPathConsumer2D;
  41 import com.sun.prism.BasicStroke;
  42 
  43 public final class MarlinPrismUtils {
  44 
  45     private static final boolean FORCE_NO_AA = false;
  46 
  47     static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
  48     static final float LOWER_BND = -UPPER_BND;
  49 
  50     static final boolean DO_CLIP = MarlinProperties.isDoClip();
  51     static final boolean DO_TRACE = false;
  52 
  53     /**
  54      * Private constructor to prevent instantiation.
  55      */
  56     private MarlinPrismUtils() {
  57     }
  58 
  59     private static PathConsumer2D initRenderer(
  60             final RendererContext rdrCtx,
  61             final BasicStroke stroke,
  62             final BaseTransform tx,
  63             final Rectangle clip,
  64             final int pirule,
  65             final MarlinRenderer renderer)
  66     {
  67         final int oprule = (stroke == null && pirule == PathIterator.WIND_EVEN_ODD) ?
  68             MarlinRenderer.WIND_EVEN_ODD : MarlinRenderer.WIND_NON_ZERO;
  69 
  70         // We use strokerat so that in Stroker and Dasher we can work only
  71         // with the pre-transformation coordinates. This will repeat a lot of
  72         // computations done in the path iterator, but the alternative is to
  73         // work with transformed paths and compute untransformed coordinates
  74         // as needed. This would be faster but I do not think the complexity
  75         // of working with both untransformed and transformed coordinates in
  76         // the same code is worth it.
  77         // However, if a path's width is constant after a transformation,
  78         // we can skip all this untransforming.
  79 
  80         // As pathTo() will check transformed coordinates for invalid values
  81         // (NaN / Infinity) to ignore such points, it is necessary to apply the
  82         // transformation before the path processing.
  83         BaseTransform strokerTx = null;
  84 
  85         int dashLen = -1;
  86         boolean recycleDashes = false;
  87         float scale = 1.0f;
  88         float width = 0.0f, dashphase = 0.0f;
  89         float[] dashes = null;
  90 
  91         if (stroke != null) {
  92             width = stroke.getLineWidth();
  93             dashes = stroke.getDashArray();
  94             dashphase = stroke.getDashPhase();
  95 
  96             if (tx != null && !tx.isIdentity()) {
  97                 final double a = tx.getMxx();
  98                 final double b = tx.getMxy();
  99                 final double c = tx.getMyx();
 100                 final double d = tx.getMyy();
 101 
 102                 // If the transform is a constant multiple of an orthogonal transformation
 103                 // then every length is just multiplied by a constant, so we just
 104                 // need to transform input paths to stroker and tell stroker
 105                 // the scaled width. This condition is satisfied if
 106                 // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
 107                 // leave a bit of room for error.
 108                 if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
 109                     scale = (float) Math.sqrt(a*a + c*c);
 110 
 111                     if (dashes != null) {
 112                         recycleDashes = true;
 113                         dashLen = dashes.length;
 114                         dashes = rdrCtx.dasher.copyDashArray(dashes);
 115                         for (int i = 0; i < dashLen; i++) {
 116                             dashes[i] *= scale;
 117                         }
 118                         dashphase *= scale;
 119                     }
 120                     width *= scale;
 121 
 122                     // by now strokerat == null. Input paths to
 123                     // stroker (and maybe dasher) will have the full transform tx
 124                     // applied to them and nothing will happen to the output paths.
 125                 } else {
 126                     strokerTx = tx;
 127 
 128                     // by now strokerat == tx. Input paths to
 129                     // stroker (and maybe dasher) will have the full transform tx
 130                     // applied to them, then they will be normalized, and then
 131                     // the inverse of *only the non translation part of tx* will
 132                     // be applied to the normalized paths. This won't cause problems
 133                     // in stroker, because, suppose tx = T*A, where T is just the
 134                     // translation part of tx, and A is the rest. T*A has already
 135                     // been applied to Stroker/Dasher's input. Then Ainv will be
 136                     // applied. Ainv*T*A is not equal to T, but it is a translation,
 137                     // which means that none of stroker's assumptions about its
 138                     // input will be violated. After all this, A will be applied
 139                     // to stroker's output.
 140                 }
 141             }
 142         }
 143 
 144         final MarlinRenderer rdr = renderer.init(clip.x, clip.y, clip.width, clip.height, oprule);
 145         PathConsumer2D pc = rdr;
 146 
 147         float rdrOffX = 0.0f, rdrOffY = 0.0f;
 148 
 149         if (DO_CLIP && stroke != null) {
 150             // Define the initial clip bounds:
 151             final float[] clipRect = rdrCtx.clipRect;
 152             clipRect[0] = clip.y;
 153             clipRect[1] = clip.y + clip.height;
 154             clipRect[2] = clip.x;
 155             clipRect[3] = clip.x + clip.width;
 156 
 157             // Get offsets:
 158             rdrOffX = rdr.getOffsetX();
 159             rdrOffY = rdr.getOffsetY();
 160 
 161             // Enable clipping:
 162             rdrCtx.doClip = true;
 163         }
 164 
 165         final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
 166 
 167         if (DO_TRACE) {
 168             // trace Stroker:
 169             pc = transformerPC2D.traceStroker(pc);
 170         }
 171 
 172         if (MarlinConst.USE_SIMPLIFIER) {
 173             // Use simplifier after stroker before Renderer
 174             // to remove collinear segments (notably due to cap square)
 175             pc = rdrCtx.simplifier.init(pc);
 176         }
 177 
 178         // deltaTransformConsumer may adjust the clip rectangle:
 179         pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx, rdrOffX, rdrOffY);
 180 
 181         if (stroke != null) {
 182             // stroker will adjust the clip rectangle (width / miter limit):
 183             pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(),
 184                     stroke.getLineJoin(), stroke.getMiterLimit(),
 185                     scale, rdrOffX, rdrOffY);
 186 
 187             if (dashes != null) {
 188                 if (!recycleDashes) {
 189                     dashLen = dashes.length;
 190                 }
 191                 pc = rdrCtx.dasher.init(pc, dashes, dashLen, dashphase, recycleDashes);
 192             } else if (rdrCtx.doClip && (stroke.getEndCap() != Stroker.CAP_BUTT)) {
 193                 if (DO_TRACE) {
 194                     pc = transformerPC2D.traceClosedPathDetector(pc);
 195                 }
 196 
 197                 // If no dash and clip is enabled:
 198                 // detect closedPaths (polygons) for caps
 199                 pc = transformerPC2D.detectClosedPath(pc);
 200             }
 201         }

 202         pc = transformerPC2D.inverseDeltaTransformConsumer(pc, strokerTx);
 203 
 204         if (DO_TRACE) {
 205             // trace Input:
 206             pc = transformerPC2D.traceInput(pc);
 207         }
 208         /*
 209          * Pipeline seems to be:
 210          * shape.getPathIterator(tx)
 211          * -> (inverseDeltaTransformConsumer)
 212          * -> (Dasher)
 213          * -> Stroker
 214          * -> (deltaTransformConsumer)
 215          *
 216          * -> (CollinearSimplifier) to remove redundant segments
 217          *
 218          * -> pc2d = Renderer (bounding box)
 219          */
 220         return pc;
 221     }
 222 
 223     private static boolean nearZero(final double num) {
 224         return Math.abs(num) < 2.0d * Math.ulp(num);
 225     }
 226 
 227     public static MarlinRenderer setupRenderer(


 385 
 386         // mark context as CLEAN:
 387         rdrCtx.dirty = false;
 388     }
 389 
 390     private static void feedConsumer(final RendererContext rdrCtx,
 391                                      final Path2D p2d,
 392                                      final BaseTransform xform,
 393                                      final PathConsumer2D pc2d)
 394     {
 395         // mark context as DIRTY:
 396         rdrCtx.dirty = true;
 397 
 398         final float[] coords = rdrCtx.float6;
 399 
 400         // ported from DuctusRenderingEngine.feedConsumer() but simplified:
 401         // - removed skip flag = !subpathStarted
 402         // - removed pathClosed (ie subpathStarted not set to false)
 403         boolean subpathStarted = false;
 404 
 405         final float[] pCoords = p2d.getFloatCoordsNoClone();
 406         final byte[] pTypes = p2d.getCommandsNoClone();
 407         final int nsegs = p2d.getNumCommands();
 408 
 409         for (int i = 0, coff = 0; i < nsegs; i++) {
 410             switch (pTypes[i]) {
 411             case PathIterator.SEG_MOVETO:
 412                 if (xform == null) {
 413                     coords[0] = pCoords[coff];
 414                     coords[1] = pCoords[coff+1];
 415                 } else {
 416                     xform.transform(pCoords, coff, coords, 0, 1);
 417                 }
 418                 coff += 2;
 419                 /* Checking SEG_MOVETO coordinates if they are out of the
 420                  * [LOWER_BND, UPPER_BND] range. This check also handles NaN
 421                  * and Infinity values. Skipping next path segment in case of
 422                  * invalid data.
 423                  */
 424                 if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
 425                     coords[1] < UPPER_BND && coords[1] > LOWER_BND)
 426                 {


< prev index next >