< prev index next >

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

Print this page




  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     // slightly slower ~2% if enabled stroker clipping (lines) but skipping cap / join handling is few percents faster in specific cases
  48     static final boolean DISABLE_2ND_STROKER_CLIPPING = true;
  49 
  50     static final boolean DO_TRACE_PATH = false;
  51 
  52     static final boolean DO_CLIP = MarlinProperties.isDoClip();
  53     static final boolean DO_CLIP_FILL = true;
  54     static final boolean DO_CLIP_RUNTIME_ENABLE = MarlinProperties.isDoClipRuntimeFlag();
  55 
  56     static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
  57     static final float LOWER_BND = -UPPER_BND;
  58 
  59     /**
  60      * Private constructor to prevent instantiation.
  61      */


  69             BaseTransform tx,
  70             final DPathConsumer2D out)
  71     {
  72         // We use strokerat so that in Stroker and Dasher we can work only
  73         // with the pre-transformation coordinates. This will repeat a lot of
  74         // computations done in the path iterator, but the alternative is to
  75         // work with transformed paths and compute untransformed coordinates
  76         // as needed. This would be faster but I do not think the complexity
  77         // of working with both untransformed and transformed coordinates in
  78         // the same code is worth it.
  79         // However, if a path's width is constant after a transformation,
  80         // we can skip all this untransforming.
  81 
  82         // As pathTo() will check transformed coordinates for invalid values
  83         // (NaN / Infinity) to ignore such points, it is necessary to apply the
  84         // transformation before the path processing.
  85         BaseTransform strokerTx = null;
  86 
  87         int dashLen = -1;
  88         boolean recycleDashes = false;
  89         double scale = 1.0d;
  90         double width = lineWidth;
  91         float[] dashes = stroke.getDashArray();
  92         double[] dashesD = null;
  93         double dashphase = stroke.getDashPhase();
  94 
  95         // Ensure converting dashes to double precision:
  96         if (dashes != null) {
  97             recycleDashes = true;
  98             dashLen = dashes.length;
  99             dashesD = rdrCtx.dasher.copyDashArray(dashes);
 100         }
 101 
 102         if ((tx != null) && !tx.isIdentity()) {
 103             final double a = tx.getMxx();
 104             final double b = tx.getMxy();
 105             final double c = tx.getMyx();
 106             final double d = tx.getMyy();
 107 
 108             // If the transform is a constant multiple of an orthogonal transformation
 109             // then every length is just multiplied by a constant, so we just
 110             // need to transform input paths to stroker and tell stroker
 111             // the scaled width. This condition is satisfied if
 112             // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
 113             // leave a bit of room for error.
 114             if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
 115                 scale = Math.sqrt(a*a + c*c);
 116 
 117                 if (dashesD != null) {
 118                     for (int i = 0; i < dashLen; i++) {
 119                         dashesD[i] *= scale;
 120                     }
 121                     dashphase *= scale;
 122                 }
 123                 width *= scale;
 124 
 125                 // by now strokerat == null. Input paths to
 126                 // stroker (and maybe dasher) will have the full transform tx
 127                 // applied to them and nothing will happen to the output paths.
 128             } else {
 129                 strokerTx = tx;
 130 
 131                 // by now strokerat == tx. Input paths to
 132                 // stroker (and maybe dasher) will have the full transform tx
 133                 // applied to them, then they will be normalized, and then
 134                 // the inverse of *only the non translation part of tx* will
 135                 // be applied to the normalized paths. This won't cause problems
 136                 // in stroker, because, suppose tx = T*A, where T is just the
 137                 // translation part of tx, and A is the rest. T*A has already
 138                 // been applied to Stroker/Dasher's input. Then Ainv will be
 139                 // applied. Ainv*T*A is not equal to T, but it is a translation,
 140                 // which means that none of stroker's assumptions about its
 141                 // input will be violated. After all this, A will be applied
 142                 // to stroker's output.
 143             }
 144         } else {
 145             // either tx is null or it's the identity. In either case
 146             // we don't transform the path.
 147             tx = null;
 148         }
 149 
 150         // Get renderer offsets:
 151         double rdrOffX = 0.0d, rdrOffY = 0.0d;
 152 
 153         if (rdrCtx.doClip && (tx != null)) {
 154             final DMarlinRenderer renderer = (DMarlinRenderer)out;
 155             rdrOffX = renderer.getOffsetX();
 156             rdrOffY = renderer.getOffsetY();
 157         }
 158 
 159         // Prepare the pipeline:
 160         DPathConsumer2D pc = out;
 161 
 162         final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
 163 
 164         if (DO_TRACE_PATH) {
 165             // trace Stroker:
 166             pc = transformerPC2D.traceStroker(pc);
 167         }
 168 
 169         if (MarlinConst.USE_SIMPLIFIER) {
 170             // Use simplifier after stroker before Renderer
 171             // to remove collinear segments (notably due to cap square)
 172             pc = rdrCtx.simplifier.init(pc);
 173         }
 174 
 175         // deltaTransformConsumer may adjust the clip rectangle:
 176         pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx, rdrOffX, rdrOffY);
 177 
 178         // stroker will adjust the clip rectangle (width / miter limit):
 179         pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(),
 180                 stroke.getLineJoin(), stroke.getMiterLimit(),
 181                 scale, rdrOffX, rdrOffY, (dashesD == null));
 182 
 183         // Curve Monotizer:
 184         rdrCtx.monotonizer.init(width);
 185 
 186         if (dashesD != null) {
 187             if (DO_TRACE_PATH) {
 188                 pc = transformerPC2D.traceDasher(pc);
 189             }
 190             pc = rdrCtx.dasher.init(pc, dashesD, dashLen, dashphase,
 191                                     recycleDashes);
 192 
 193             if (DISABLE_2ND_STROKER_CLIPPING) {
 194                 // disable stoker clipping:
 195                 rdrCtx.stroker.disableClipping();
 196             }
 197 
 198         } else if (rdrCtx.doClip && (stroke.getEndCap() != DStroker.CAP_BUTT)) {
 199             if (DO_TRACE_PATH) {
 200                 pc = transformerPC2D.traceClosedPathDetector(pc);
 201             }


 224          */
 225         return pc;
 226     }
 227 
 228     private static boolean nearZero(final double num) {
 229         return Math.abs(num) < 2.0d * Math.ulp(num);
 230     }
 231 
 232     private static DPathConsumer2D initRenderer(
 233             final DRendererContext rdrCtx,
 234             final BasicStroke stroke,
 235             final BaseTransform tx,
 236             final Rectangle clip,
 237             final int piRule,
 238             final DMarlinRenderer renderer)
 239     {
 240         if (DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime())) {
 241             // Define the initial clip bounds:
 242             final double[] clipRect = rdrCtx.clipRect;
 243 
 244             clipRect[0] = clip.y;
 245             clipRect[1] = clip.y + clip.height;
 246             clipRect[2] = clip.x;
 247             clipRect[3] = clip.x + clip.width;
















 248 
 249             // Enable clipping:
 250             rdrCtx.doClip = true;
 251         }
 252 
 253         if (stroke != null) {
 254             renderer.init(clip.x, clip.y, clip.width, clip.height,
 255                           MarlinConst.WIND_NON_ZERO);
 256 
 257             return initStroker(rdrCtx, stroke, stroke.getLineWidth(), tx, renderer);
 258         } else {
 259             // Filler:
 260             final int oprule = (piRule == PathIterator.WIND_EVEN_ODD) ?
 261                 MarlinConst.WIND_EVEN_ODD : MarlinConst.WIND_NON_ZERO;
 262 
 263             renderer.init(clip.x, clip.y, clip.width, clip.height, oprule);
 264 
 265             DPathConsumer2D pc = renderer;
 266 
 267             final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
 268 
 269             if (DO_CLIP_FILL && rdrCtx.doClip) {
 270                 double rdrOffX = renderer.getOffsetX();
 271                 double rdrOffY = renderer.getOffsetY();
 272 
 273                 if (DO_TRACE_PATH) {
 274                     // trace Filler:
 275                     pc = rdrCtx.transformerPC2D.traceFiller(pc);
 276                 }
 277                 pc = rdrCtx.transformerPC2D.pathClipper(pc, rdrOffX, rdrOffY);
 278             }
 279 
 280             if (DO_TRACE_PATH) {
 281                 // trace Input:
 282                 pc = transformerPC2D.traceInput(pc);
 283             }
 284             return pc;
 285         }
 286     }
 287 
 288     public static DMarlinRenderer setupRenderer(
 289             final DRendererContext rdrCtx,
 290             final Shape shape,
 291             final BasicStroke stroke,
 292             final BaseTransform xform,
 293             final Rectangle rclip,
 294             final boolean antialiasedShape)
 295     {
 296         // Test if transform is identity:
 297         final BaseTransform tf = ((xform != null) && !xform.isIdentity()) ? xform : null;




  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.marlin.MarlinUtils;
  42 import com.sun.prism.BasicStroke;
  43 import java.util.Arrays;
  44 
  45 public final class DMarlinPrismUtils {
  46 
  47     private static final boolean FORCE_NO_AA = false;
  48 
  49     // slightly slower ~2% if enabled stroker clipping (lines) but skipping cap / join handling is few percents faster in specific cases
  50     static final boolean DISABLE_2ND_STROKER_CLIPPING = true;
  51 
  52     static final boolean DO_TRACE_PATH = false;
  53 
  54     static final boolean DO_CLIP = MarlinProperties.isDoClip();
  55     static final boolean DO_CLIP_FILL = true;
  56     static final boolean DO_CLIP_RUNTIME_ENABLE = MarlinProperties.isDoClipRuntimeFlag();
  57 
  58     static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
  59     static final float LOWER_BND = -UPPER_BND;
  60 
  61     /**
  62      * Private constructor to prevent instantiation.
  63      */


  71             BaseTransform tx,
  72             final DPathConsumer2D out)
  73     {
  74         // We use strokerat so that in Stroker and Dasher we can work only
  75         // with the pre-transformation coordinates. This will repeat a lot of
  76         // computations done in the path iterator, but the alternative is to
  77         // work with transformed paths and compute untransformed coordinates
  78         // as needed. This would be faster but I do not think the complexity
  79         // of working with both untransformed and transformed coordinates in
  80         // the same code is worth it.
  81         // However, if a path's width is constant after a transformation,
  82         // we can skip all this untransforming.
  83 
  84         // As pathTo() will check transformed coordinates for invalid values
  85         // (NaN / Infinity) to ignore such points, it is necessary to apply the
  86         // transformation before the path processing.
  87         BaseTransform strokerTx = null;
  88 
  89         int dashLen = -1;
  90         boolean recycleDashes = false;

  91         double width = lineWidth;
  92         float[] dashes = stroke.getDashArray();
  93         double[] dashesD = null;
  94         double 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                 final double 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         } else {
 146             // either tx is null or it's the identity. In either case
 147             // we don't transform the path.
 148             tx = null;
 149         }
 150 









 151         // Prepare the pipeline:
 152         DPathConsumer2D pc = out;
 153 
 154         final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
 155 
 156         if (DO_TRACE_PATH) {
 157             // trace Stroker:
 158             pc = transformerPC2D.traceStroker(pc);
 159         }
 160 
 161         if (MarlinConst.USE_SIMPLIFIER) {
 162             // Use simplifier after stroker before Renderer
 163             // to remove collinear segments (notably due to cap square)
 164             pc = rdrCtx.simplifier.init(pc);
 165         }
 166 
 167         // deltaTransformConsumer may adjust the clip rectangle:
 168         pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx);
 169 
 170         // stroker will adjust the clip rectangle (width / miter limit):
 171         pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(),
 172                 stroke.getLineJoin(), stroke.getMiterLimit(),
 173                 (dashesD == null));
 174 
 175         // Curve Monotizer:
 176         rdrCtx.monotonizer.init(width);
 177 
 178         if (dashesD != null) {
 179             if (DO_TRACE_PATH) {
 180                 pc = transformerPC2D.traceDasher(pc);
 181             }
 182             pc = rdrCtx.dasher.init(pc, dashesD, dashLen, dashphase,
 183                                     recycleDashes);
 184 
 185             if (DISABLE_2ND_STROKER_CLIPPING) {
 186                 // disable stoker clipping:
 187                 rdrCtx.stroker.disableClipping();
 188             }
 189 
 190         } else if (rdrCtx.doClip && (stroke.getEndCap() != DStroker.CAP_BUTT)) {
 191             if (DO_TRACE_PATH) {
 192                 pc = transformerPC2D.traceClosedPathDetector(pc);
 193             }


 216          */
 217         return pc;
 218     }
 219 
 220     private static boolean nearZero(final double num) {
 221         return Math.abs(num) < 2.0d * Math.ulp(num);
 222     }
 223 
 224     private static DPathConsumer2D initRenderer(
 225             final DRendererContext rdrCtx,
 226             final BasicStroke stroke,
 227             final BaseTransform tx,
 228             final Rectangle clip,
 229             final int piRule,
 230             final DMarlinRenderer renderer)
 231     {
 232         if (DO_CLIP || (DO_CLIP_RUNTIME_ENABLE && MarlinProperties.isDoClipAtRuntime())) {
 233             // Define the initial clip bounds:
 234             final double[] clipRect = rdrCtx.clipRect;
 235 
 236             // Adjust the clipping rectangle with the renderer offsets
 237             final double rdrOffX = renderer.getOffsetX();
 238             final double rdrOffY = renderer.getOffsetY();
 239 
 240             // add a small rounding error:
 241             final double margin = 1e-3d;
 242 
 243             clipRect[0] = clip.y
 244                             - margin + rdrOffY;
 245             clipRect[1] = clip.y + clip.height
 246                             + margin + rdrOffY;
 247             clipRect[2] = clip.x
 248                             - margin + rdrOffX;
 249             clipRect[3] = clip.x + clip.width
 250                             + margin + rdrOffX;
 251 
 252             if (MarlinConst.DO_LOG_CLIP) {
 253                 MarlinUtils.logInfo("clipRect (clip): "
 254                                     + Arrays.toString(rdrCtx.clipRect));
 255             }
 256 
 257             // Enable clipping:
 258             rdrCtx.doClip = true;
 259         }
 260 
 261         if (stroke != null) {
 262             renderer.init(clip.x, clip.y, clip.width, clip.height,
 263                           MarlinConst.WIND_NON_ZERO);
 264 
 265             return initStroker(rdrCtx, stroke, stroke.getLineWidth(), tx, renderer);
 266         } else {
 267             // Filler:
 268             final int oprule = (piRule == PathIterator.WIND_EVEN_ODD) ?
 269                 MarlinConst.WIND_EVEN_ODD : MarlinConst.WIND_NON_ZERO;
 270 
 271             renderer.init(clip.x, clip.y, clip.width, clip.height, oprule);
 272 
 273             DPathConsumer2D pc = renderer;
 274 
 275             final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
 276 
 277             if (DO_CLIP_FILL && rdrCtx.doClip) {



 278                 if (DO_TRACE_PATH) {
 279                     // trace Filler:
 280                     pc = rdrCtx.transformerPC2D.traceFiller(pc);
 281                 }
 282                 pc = rdrCtx.transformerPC2D.pathClipper(pc);
 283             }
 284 
 285             if (DO_TRACE_PATH) {
 286                 // trace Input:
 287                 pc = transformerPC2D.traceInput(pc);
 288             }
 289             return pc;
 290         }
 291     }
 292 
 293     public static DMarlinRenderer setupRenderer(
 294             final DRendererContext rdrCtx,
 295             final Shape shape,
 296             final BasicStroke stroke,
 297             final BaseTransform xform,
 298             final Rectangle rclip,
 299             final boolean antialiasedShape)
 300     {
 301         // Test if transform is identity:
 302         final BaseTransform tf = ((xform != null) && !xform.isIdentity()) ? xform : null;


< prev index next >