< prev index next >

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

Print this page




  15  * accompanied this code).
  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.PathIterator;
  30 import com.sun.javafx.geom.Path2D;
  31 import com.sun.javafx.geom.Rectangle;
  32 import com.sun.javafx.geom.Shape;
  33 import com.sun.javafx.geom.transform.BaseTransform;
  34 import com.sun.marlin.MarlinConst;

  35 import com.sun.marlin.DMarlinRenderer;
  36 import com.sun.marlin.DPathConsumer2D;
  37 import com.sun.marlin.DRendererContext;

  38 import com.sun.marlin.DTransformingPathConsumer2D;
  39 import com.sun.prism.BasicStroke;
  40 
  41 public final class DMarlinPrismUtils {
  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 DMarlinPrismUtils() {
  52     }
  53 
  54     private static DPathConsumer2D initRenderer(
  55             final DRendererContext rdrCtx,
  56             final BasicStroke stroke,
  57             final BaseTransform tx,
  58             final Rectangle clip,
  59             final int pirule,
  60             final DMarlinRenderer renderer)
  61     {
  62         final int oprule = (stroke == null && pirule == PathIterator.WIND_EVEN_ODD) ?
  63             DMarlinRenderer.WIND_EVEN_ODD : DMarlinRenderer.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         double width = 0.0f, dashphase = 0.0f;
  84         double[] dashesD = null;
  85 
  86         if (stroke != null) {
  87             width = stroke.getLineWidth();
  88             final float[] dashes = stroke.getDashArray();
  89             dashphase = stroke.getDashPhase();
  90 
  91             // Ensure converting dashes to double precision:
  92             if (dashes != null) {
  93                 recycleDashes = true;
  94                 dashLen = dashes.length;
  95                 dashesD = rdrCtx.dasher.copyDashArray(dashes);
  96             }
  97 
  98             if (tx != null && !tx.isIdentity()) {
  99                 final double a = tx.getMxx();
 100                 final double b = tx.getMxy();
 101                 final double c = tx.getMyx();
 102                 final double d = tx.getMyy();
 103 
 104                 // If the transform is a constant multiple of an orthogonal transformation
 105                 // then every length is just multiplied by a constant, so we just
 106                 // need to transform input paths to stroker and tell stroker
 107                 // the scaled width. This condition is satisfied if
 108                 // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
 109                 // leave a bit of room for error.
 110                 if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
 111                     final double scale = Math.sqrt(a*a + c*c);
 112 
 113                     if (dashesD != null) {
 114                         for (int i = 0; i < dashLen; i++) {
 115                             dashesD[i] *= scale;
 116                         }
 117                         dashphase *= scale;
 118                     }
 119                     width *= scale;
 120 
 121                     // by now strokerat == null. Input paths to
 122                     // stroker (and maybe dasher) will have the full transform tx
 123                     // applied to them and nothing will happen to the output paths.
 124                 } else {
 125                     strokerTx = tx;
 126 
 127                     // by now strokerat == tx. Input paths to
 128                     // stroker (and maybe dasher) will have the full transform tx
 129                     // applied to them, then they will be normalized, and then
 130                     // the inverse of *only the non translation part of tx* will
 131                     // be applied to the normalized paths. This won't cause problems
 132                     // in stroker, because, suppose tx = T*A, where T is just the
 133                     // translation part of tx, and A is the rest. T*A has already
 134                     // been applied to Stroker/Dasher's input. Then Ainv will be
 135                     // applied. Ainv*T*A is not equal to T, but it is a translation,
 136                     // which means that none of stroker's assumptions about its
 137                     // input will be violated. After all this, A will be applied
 138                     // to stroker's output.
 139                 }
 140             }
 141         }
 142 
 143         DPathConsumer2D pc = renderer.init(clip.x, clip.y, clip.width, clip.height, oprule);


























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

 155             pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(),
 156                     stroke.getLineJoin(), stroke.getMiterLimit());

 157 
 158             if (dashesD != null) {
 159                 pc = rdrCtx.dasher.init(pc, dashesD, dashLen, dashphase, recycleDashes);








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




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


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




  15  * accompanied this code).
  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.PathIterator;
  30 import com.sun.javafx.geom.Path2D;
  31 import com.sun.javafx.geom.Rectangle;
  32 import com.sun.javafx.geom.Shape;
  33 import com.sun.javafx.geom.transform.BaseTransform;
  34 import com.sun.marlin.MarlinConst;
  35 import com.sun.marlin.MarlinProperties;
  36 import com.sun.marlin.DMarlinRenderer;
  37 import com.sun.marlin.DPathConsumer2D;
  38 import com.sun.marlin.DRendererContext;
  39 import com.sun.marlin.DStroker;
  40 import com.sun.marlin.DTransformingPathConsumer2D;
  41 import com.sun.prism.BasicStroke;
  42 
  43 public final class DMarlinPrismUtils {
  44 
  45     private static final boolean FORCE_NO_AA = false;
  46 
  47     static final double UPPER_BND = Float.MAX_VALUE / 2.0d;
  48     static final double 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 DMarlinPrismUtils() {
  57     }
  58 
  59     private static DPathConsumer2D initRenderer(
  60             final DRendererContext rdrCtx,
  61             final BasicStroke stroke,
  62             final BaseTransform tx,
  63             final Rectangle clip,
  64             final int pirule,
  65             final DMarlinRenderer renderer)
  66     {
  67         final int oprule = (stroke == null && pirule == PathIterator.WIND_EVEN_ODD) ?
  68             DMarlinRenderer.WIND_EVEN_ODD : DMarlinRenderer.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         double scale = 1.0d;
  88         double width = 0.0f, dashphase = 0.0f;
  89         double[] dashesD = null;
  90 
  91         if (stroke != null) {
  92             width = stroke.getLineWidth();
  93             final float[] dashes = stroke.getDashArray();
  94             dashphase = stroke.getDashPhase();
  95 
  96             // Ensure converting dashes to double precision:
  97             if (dashes != null) {
  98                 recycleDashes = true;
  99                 dashLen = dashes.length;
 100                 dashesD = rdrCtx.dasher.copyDashArray(dashes);
 101             }
 102 
 103             if (tx != null && !tx.isIdentity()) {
 104                 final double a = tx.getMxx();
 105                 final double b = tx.getMxy();
 106                 final double c = tx.getMyx();
 107                 final double d = tx.getMyy();
 108 
 109                 // If the transform is a constant multiple of an orthogonal transformation
 110                 // then every length is just multiplied by a constant, so we just
 111                 // need to transform input paths to stroker and tell stroker
 112                 // the scaled width. This condition is satisfied if
 113                 // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
 114                 // leave a bit of room for error.
 115                 if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
 116                     scale = Math.sqrt(a*a + c*c);
 117 
 118                     if (dashesD != null) {
 119                         for (int i = 0; i < dashLen; i++) {
 120                             dashesD[i] *= scale;
 121                         }
 122                         dashphase *= scale;
 123                     }
 124                     width *= scale;
 125 
 126                     // by now strokerat == null. Input paths to
 127                     // stroker (and maybe dasher) will have the full transform tx
 128                     // applied to them and nothing will happen to the output paths.
 129                 } else {
 130                     strokerTx = tx;
 131 
 132                     // by now strokerat == tx. Input paths to
 133                     // stroker (and maybe dasher) will have the full transform tx
 134                     // applied to them, then they will be normalized, and then
 135                     // the inverse of *only the non translation part of tx* will
 136                     // be applied to the normalized paths. This won't cause problems
 137                     // in stroker, because, suppose tx = T*A, where T is just the
 138                     // translation part of tx, and A is the rest. T*A has already
 139                     // been applied to Stroker/Dasher's input. Then Ainv will be
 140                     // applied. Ainv*T*A is not equal to T, but it is a translation,
 141                     // which means that none of stroker's assumptions about its
 142                     // input will be violated. After all this, A will be applied
 143                     // to stroker's output.
 144                 }
 145             }
 146         }
 147 
 148         final DMarlinRenderer rdr = renderer.init(clip.x, clip.y, clip.width, clip.height, oprule);
 149         DPathConsumer2D pc = rdr;
 150 
 151         double rdrOffX = 0.0d, rdrOffY = 0.0d;
 152 
 153         if (DO_CLIP && stroke != null) {
 154             // Define the initial clip bounds:
 155             final double[] clipRect = rdrCtx.clipRect;
 156             clipRect[0] = clip.y;
 157             clipRect[1] = clip.y + clip.height;
 158             clipRect[2] = clip.x;
 159             clipRect[3] = clip.x + clip.width;
 160 
 161             // Get offsets:
 162             rdrOffX = rdr.getOffsetX();
 163             rdrOffY = rdr.getOffsetY();
 164 
 165             // Enable clipping:
 166             rdrCtx.doClip = true;
 167         }
 168 
 169         final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
 170 
 171         if (DO_TRACE) {
 172             // trace Stroker:
 173             pc = transformerPC2D.traceStroker(pc);
 174         }
 175 
 176         if (MarlinConst.USE_SIMPLIFIER) {
 177             // Use simplifier after stroker before Renderer
 178             // to remove collinear segments (notably due to cap square)
 179             pc = rdrCtx.simplifier.init(pc);
 180         }
 181 
 182         // deltaTransformConsumer may adjust the clip rectangle:
 183         pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx, rdrOffX, rdrOffY);
 184 
 185         if (stroke != null) {
 186             // stroker will adjust the clip rectangle (width / miter limit):
 187             pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(),
 188                     stroke.getLineJoin(), stroke.getMiterLimit(),
 189                     scale, rdrOffX, rdrOffY);
 190 
 191             if (dashesD != null) {
 192                 pc = rdrCtx.dasher.init(pc, dashesD, dashLen, dashphase, recycleDashes);
 193             } else if (rdrCtx.doClip && (stroke.getEndCap() != DStroker.CAP_BUTT)) {
 194                 if (DO_TRACE) {
 195                     pc = transformerPC2D.traceClosedPathDetector(pc);
 196                 }
 197 
 198                 // If no dash and clip is enabled:
 199                 // detect closedPaths (polygons) for caps
 200                 pc = transformerPC2D.detectClosedPath(pc);
 201             }
 202         }

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


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


< prev index next >