< prev index next >

modules/javafx.graphics/src/main/java/com/sun/marlin/TransformingPathConsumer2D.java

Print this page

        

@@ -25,46 +25,156 @@
 
 package com.sun.marlin;
 
 import com.sun.javafx.geom.PathConsumer2D;
 import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.marlin.Helpers.PolyStack;
 
 public final class TransformingPathConsumer2D {
 
-    TransformingPathConsumer2D() {
-        // used by RendererContext
-    }
+    private final RendererContext rdrCtx;
+
+    // recycled ClosedPathDetector instance from detectClosedPath()
+    private final ClosedPathDetector   cpDetector;
 
     // recycled PathConsumer2D instances from deltaTransformConsumer()
     private final DeltaScaleFilter     dt_DeltaScaleFilter     = new DeltaScaleFilter();
     private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
 
+    // recycled PathConsumer2D instances from inverseDeltaTransformConsumer()
+    private final DeltaScaleFilter     iv_DeltaScaleFilter     = new DeltaScaleFilter();
+    private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
+
+    // recycled PathTracer instances from tracer...() methods
+    private final PathTracer tracerInput      = new PathTracer("[Input]");
+    private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector");
+    private final PathTracer tracerStroker    = new PathTracer("Stroker");
+
+    TransformingPathConsumer2D(final RendererContext rdrCtx) {
+        // used by RendererContext
+        this.rdrCtx = rdrCtx;
+        this.cpDetector = new ClosedPathDetector(rdrCtx);
+    }
+
+    public PathConsumer2D traceInput(PathConsumer2D out) {
+        return tracerInput.init(out);
+    }
+
+    public PathConsumer2D traceClosedPathDetector(PathConsumer2D out) {
+        return tracerCPDetector.init(out);
+    }
+
+    public PathConsumer2D traceStroker(PathConsumer2D out) {
+        return tracerStroker.init(out);
+    }
+
+    public PathConsumer2D detectClosedPath(PathConsumer2D out)
+    {
+        return cpDetector.init(out);
+    }
+
     public PathConsumer2D deltaTransformConsumer(PathConsumer2D out,
-                                                 BaseTransform at)
+                                                 BaseTransform at,
+                                                 final float rdrOffX,
+                                                 final float rdrOffY)
     {
         if (at == null) {
             return out;
         }
-        float mxx = (float) at.getMxx();
-        float mxy = (float) at.getMxy();
-        float myx = (float) at.getMyx();
-        float myy = (float) at.getMyy();
+        final float mxx = (float) at.getMxx();
+        final float mxy = (float) at.getMxy();
+        final float myx = (float) at.getMyx();
+        final float myy = (float) at.getMyy();
 
         if (mxy == 0.0f && myx == 0.0f) {
             if (mxx == 1.0f && myy == 1.0f) {
                 return out;
             } else {
+                // Scale only
+                if (rdrCtx.doClip) {
+                    // adjust clip rectangle (ymin, ymax, xmin, xmax):
+                    adjustClipOffset(rdrCtx.clipRect, rdrOffX, rdrOffY);
+                    adjustClipScale(rdrCtx.clipRect, mxx, myy);
+                }
                 return dt_DeltaScaleFilter.init(out, mxx, myy);
             }
         } else {
+            if (rdrCtx.doClip) {
+                // adjust clip rectangle (ymin, ymax, xmin, xmax):
+                adjustClipOffset(rdrCtx.clipRect, rdrOffX, rdrOffY);
+                adjustClipInverseDelta(rdrCtx.clipRect, mxx, mxy, myx, myy);
+            }
             return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
         }
     }
 
-    // recycled PathConsumer2D instances from inverseDeltaTransformConsumer()
-    private final DeltaScaleFilter     iv_DeltaScaleFilter     = new DeltaScaleFilter();
-    private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
+    private static void adjustClipOffset(final float[] clipRect,
+                                         final float rdrOffX,
+                                         final float rdrOffY)
+    {
+        clipRect[0] += rdrOffY;
+        clipRect[1] += rdrOffY;
+        clipRect[2] += rdrOffX;
+        clipRect[3] += rdrOffX;
+    }
+
+    private static void adjustClipScale(final float[] clipRect,
+                                        final float mxx, final float myy)
+    {
+        // Adjust the clipping rectangle (iv_DeltaScaleFilter):
+        clipRect[0] /= myy;
+        clipRect[1] /= myy;
+        clipRect[2] /= mxx;
+        clipRect[3] /= mxx;
+    }
+
+    private static void adjustClipInverseDelta(final float[] clipRect,
+                                               final float mxx, final float mxy,
+                                               final float myx, final float myy)
+    {
+        // Adjust the clipping rectangle (iv_DeltaTransformFilter):
+        final float det = mxx * myy - mxy * myx;
+        final float imxx =  myy / det;
+        final float imxy = -mxy / det;
+        final float imyx = -myx / det;
+        final float imyy =  mxx / det;
+
+        float xmin, xmax, ymin, ymax;
+        float x, y;
+        // xmin, ymin:
+        x = clipRect[2] * imxx + clipRect[0] * imxy;
+        y = clipRect[2] * imyx + clipRect[0] * imyy;
+
+        xmin = xmax = x;
+        ymin = ymax = y;
+
+        // xmax, ymin:
+        x = clipRect[3] * imxx + clipRect[0] * imxy;
+        y = clipRect[3] * imyx + clipRect[0] * imyy;
+
+        if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+        if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+        // xmin, ymax:
+        x = clipRect[2] * imxx + clipRect[1] * imxy;
+        y = clipRect[2] * imyx + clipRect[1] * imyy;
+
+        if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+        if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+        // xmax, ymax:
+        x = clipRect[3] * imxx + clipRect[1] * imxy;
+        y = clipRect[3] * imyx + clipRect[1] * imyy;
+
+        if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+        if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+        clipRect[0] = ymin;
+        clipRect[1] = ymax;
+        clipRect[2] = xmin;
+        clipRect[3] = xmax;
+    }
 
     public PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out,
                                                         BaseTransform at)
     {
         if (at == null) {

@@ -80,11 +190,11 @@
                 return out;
             } else {
                 return iv_DeltaScaleFilter.init(out, 1.0f/mxx, 1.0f/myy);
             }
         } else {
-            float det = mxx * myy - mxy * myx;
+            final float det = mxx * myy - mxy * myx;
             return iv_DeltaTransformFilter.init(out,
                                                 myy / det,
                                                -mxy / det,
                                                -myx / det,
                                                 mxx / det);

@@ -207,6 +317,144 @@
         @Override
         public void pathDone() {
             out.pathDone();
         }
     }
+
+    static final class ClosedPathDetector implements PathConsumer2D {
+
+        private final RendererContext rdrCtx;
+        private final PolyStack stack;
+
+        private PathConsumer2D out;
+
+        ClosedPathDetector(final RendererContext rdrCtx) {
+            this.rdrCtx = rdrCtx;
+            this.stack = (rdrCtx.stats != null) ?
+                new PolyStack(rdrCtx,
+                        rdrCtx.stats.stat_cpd_polystack_types,
+                        rdrCtx.stats.stat_cpd_polystack_curves,
+                        rdrCtx.stats.hist_cpd_polystack_curves,
+                        rdrCtx.stats.stat_array_cpd_polystack_curves,
+                        rdrCtx.stats.stat_array_cpd_polystack_types)
+                : new PolyStack(rdrCtx);
+        }
+
+        ClosedPathDetector init(PathConsumer2D out) {
+            this.out = out;
+            return this; // fluent API
+        }
+
+        /**
+         * Disposes this instance:
+         * clean up before reusing this instance
+         */
+        void dispose() {
+            stack.dispose();
+        }
+
+        @Override
+        public void pathDone() {
+            // previous path is not closed:
+            finish(false);
+            out.pathDone();
+
+            // TODO: fix possible leak if exception happened
+            // Dispose this instance:
+            dispose();
+        }
+
+        @Override
+        public void closePath() {
+            // path is closed
+            finish(true);
+            out.closePath();
+        }
+
+        @Override
+        public void moveTo(float x0, float y0) {
+            // previous path is not closed:
+            finish(false);
+            out.moveTo(x0, y0);
+        }
+
+        private void finish(final boolean closed) {
+            rdrCtx.closedPath = closed;
+            stack.pullAll(out);
+        }
+
+        @Override
+        public void lineTo(float x1, float y1) {
+            stack.pushLine(x1, y1);
+        }
+
+        @Override
+        public void curveTo(float x3, float y3,
+                            float x2, float y2,
+                            float x1, float y1)
+        {
+            stack.pushCubic(x1, y1, x2, y2, x3, y3);
+        }
+
+        @Override
+        public void quadTo(float x2, float y2, float x1, float y1) {
+            stack.pushQuad(x1, y1, x2, y2);
+        }
+    }
+
+    static final class PathTracer implements PathConsumer2D {
+        private final String prefix;
+        private PathConsumer2D out;
+
+        PathTracer(String name) {
+            this.prefix = name + ": ";
+        }
+
+        PathTracer init(PathConsumer2D out) {
+            this.out = out;
+            return this; // fluent API
+        }
+
+        @Override
+        public void moveTo(float x0, float y0) {
+            log("moveTo (" + x0 + ", " + y0 + ')');
+            out.moveTo(x0, y0);
+        }
+
+        @Override
+        public void lineTo(float x1, float y1) {
+            log("lineTo (" + x1 + ", " + y1 + ')');
+            out.lineTo(x1, y1);
+        }
+
+        @Override
+        public void curveTo(float x1, float y1,
+                            float x2, float y2,
+                            float x3, float y3)
+        {
+            log("curveTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2  + ") P3(" + x3 + ", " + y3 + ')');
+            out.curveTo(x1, y1, x2, y2, x3, y3);
+        }
+
+        @Override
+        public void quadTo(float x1, float y1, float x2, float y2) {
+            log("quadTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2  + ')');
+            out.quadTo(x1, y1, x2, y2);
+        }
+
+        @Override
+        public void closePath() {
+            log("closePath");
+            out.closePath();
+        }
+
+        @Override
+        public void pathDone() {
+            log("pathDone");
+            out.pathDone();
+        }
+
+        private void log(final String message) {
+            System.out.println(prefix + message);
+        }
+    }
 }
< prev index next >