1 /*
   2  * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  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.marlin;
  27 
  28 
  29 import com.sun.javafx.geom.transform.BaseTransform;
  30 import com.sun.marlin.DHelpers.PolyStack;
  31 
  32 public final class DTransformingPathConsumer2D {
  33 
  34     private final DRendererContext rdrCtx;
  35 
  36     // recycled ClosedPathDetector instance from detectClosedPath()
  37     private final ClosedPathDetector   cpDetector;
  38 
  39     // recycled DPathConsumer2D instances from deltaTransformConsumer()
  40     private final DeltaScaleFilter     dt_DeltaScaleFilter     = new DeltaScaleFilter();
  41     private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
  42 
  43     // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer()
  44     private final DeltaScaleFilter     iv_DeltaScaleFilter     = new DeltaScaleFilter();
  45     private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
  46 
  47     // recycled PathTracer instances from tracer...() methods
  48     private final PathTracer tracerInput      = new PathTracer("[Input]");
  49     private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector");
  50     private final PathTracer tracerStroker    = new PathTracer("Stroker");
  51 
  52     DTransformingPathConsumer2D(final DRendererContext rdrCtx) {
  53         // used by RendererContext
  54         this.rdrCtx = rdrCtx;
  55         this.cpDetector = new ClosedPathDetector(rdrCtx);
  56     }
  57 
  58     public DPathConsumer2D traceInput(DPathConsumer2D out) {
  59         return tracerInput.init(out);
  60     }
  61 
  62     public DPathConsumer2D traceClosedPathDetector(DPathConsumer2D out) {
  63         return tracerCPDetector.init(out);
  64     }
  65 
  66     public DPathConsumer2D traceStroker(DPathConsumer2D out) {
  67         return tracerStroker.init(out);
  68     }
  69 
  70     public DPathConsumer2D detectClosedPath(DPathConsumer2D out)
  71     {
  72         return cpDetector.init(out);
  73     }
  74 
  75     public DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out,
  76                                                   BaseTransform at,
  77                                                   final double rdrOffX,
  78                                                   final double rdrOffY)
  79     {
  80         if (at == null) {
  81             return out;
  82         }
  83         final double mxx = at.getMxx();
  84         final double mxy = at.getMxy();
  85         final double myx = at.getMyx();
  86         final double myy = at.getMyy();
  87 
  88         if (mxy == 0.0d && myx == 0.0d) {
  89             if (mxx == 1.0d && myy == 1.0d) {
  90                 return out;
  91             } else {
  92                 // Scale only
  93                 if (rdrCtx.doClip) {
  94                     // adjust clip rectangle (ymin, ymax, xmin, xmax):
  95                     adjustClipOffset(rdrCtx.clipRect, rdrOffX, rdrOffY);
  96                     adjustClipScale(rdrCtx.clipRect, mxx, myy);
  97                 }
  98                 return dt_DeltaScaleFilter.init(out, mxx, myy);
  99             }
 100         } else {
 101             if (rdrCtx.doClip) {
 102                 // adjust clip rectangle (ymin, ymax, xmin, xmax):
 103                 adjustClipOffset(rdrCtx.clipRect, rdrOffX, rdrOffY);
 104                 adjustClipInverseDelta(rdrCtx.clipRect, mxx, mxy, myx, myy);
 105             }
 106             return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
 107         }
 108     }
 109 
 110     private static void adjustClipOffset(final double[] clipRect,
 111                                          final double rdrOffX,
 112                                          final double rdrOffY)
 113     {
 114         clipRect[0] += rdrOffY;
 115         clipRect[1] += rdrOffY;
 116         clipRect[2] += rdrOffX;
 117         clipRect[3] += rdrOffX;
 118     }
 119 
 120     private static void adjustClipScale(final double[] clipRect,
 121                                         final double mxx, final double myy)
 122     {
 123         // Adjust the clipping rectangle (iv_DeltaScaleFilter):
 124         clipRect[0] /= myy;
 125         clipRect[1] /= myy;
 126         clipRect[2] /= mxx;
 127         clipRect[3] /= mxx;
 128     }
 129 
 130     private static void adjustClipInverseDelta(final double[] clipRect,
 131                                                final double mxx, final double mxy,
 132                                                final double myx, final double myy)
 133     {
 134         // Adjust the clipping rectangle (iv_DeltaTransformFilter):
 135         final double det = mxx * myy - mxy * myx;
 136         final double imxx =  myy / det;
 137         final double imxy = -mxy / det;
 138         final double imyx = -myx / det;
 139         final double imyy =  mxx / det;
 140 
 141         double xmin, xmax, ymin, ymax;
 142         double x, y;
 143         // xmin, ymin:
 144         x = clipRect[2] * imxx + clipRect[0] * imxy;
 145         y = clipRect[2] * imyx + clipRect[0] * imyy;
 146 
 147         xmin = xmax = x;
 148         ymin = ymax = y;
 149 
 150         // xmax, ymin:
 151         x = clipRect[3] * imxx + clipRect[0] * imxy;
 152         y = clipRect[3] * imyx + clipRect[0] * imyy;
 153 
 154         if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
 155         if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
 156 
 157         // xmin, ymax:
 158         x = clipRect[2] * imxx + clipRect[1] * imxy;
 159         y = clipRect[2] * imyx + clipRect[1] * imyy;
 160 
 161         if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
 162         if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
 163 
 164         // xmax, ymax:
 165         x = clipRect[3] * imxx + clipRect[1] * imxy;
 166         y = clipRect[3] * imyx + clipRect[1] * imyy;
 167 
 168         if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
 169         if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
 170 
 171         clipRect[0] = ymin;
 172         clipRect[1] = ymax;
 173         clipRect[2] = xmin;
 174         clipRect[3] = xmax;
 175     }
 176 
 177     public DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out,
 178                                                         BaseTransform at)
 179     {
 180         if (at == null) {
 181             return out;
 182         }
 183         double mxx = at.getMxx();
 184         double mxy = at.getMxy();
 185         double myx = at.getMyx();
 186         double myy = at.getMyy();
 187 
 188         if (mxy == 0.0d && myx == 0.0d) {
 189             if (mxx == 1.0d && myy == 1.0d) {
 190                 return out;
 191             } else {
 192                 return iv_DeltaScaleFilter.init(out, 1.0d/mxx, 1.0d/myy);
 193             }
 194         } else {
 195             final double det = mxx * myy - mxy * myx;
 196             return iv_DeltaTransformFilter.init(out,
 197                                                 myy / det,
 198                                                -mxy / det,
 199                                                -myx / det,
 200                                                 mxx / det);
 201         }
 202     }
 203 
 204     static final class DeltaScaleFilter implements DPathConsumer2D {
 205         private DPathConsumer2D out;
 206         private double sx, sy;
 207 
 208         DeltaScaleFilter() {}
 209 
 210         DeltaScaleFilter init(DPathConsumer2D out,
 211                               double mxx, double myy)
 212         {
 213             this.out = out;
 214             sx = mxx;
 215             sy = myy;
 216             return this; // fluent API
 217         }
 218 
 219         @Override
 220         public void moveTo(double x0, double y0) {
 221             out.moveTo(x0 * sx, y0 * sy);
 222         }
 223 
 224         @Override
 225         public void lineTo(double x1, double y1) {
 226             out.lineTo(x1 * sx, y1 * sy);
 227         }
 228 
 229         @Override
 230         public void quadTo(double x1, double y1,
 231                            double x2, double y2)
 232         {
 233             out.quadTo(x1 * sx, y1 * sy,
 234                        x2 * sx, y2 * sy);
 235         }
 236 
 237         @Override
 238         public void curveTo(double x1, double y1,
 239                             double x2, double y2,
 240                             double x3, double y3)
 241         {
 242             out.curveTo(x1 * sx, y1 * sy,
 243                         x2 * sx, y2 * sy,
 244                         x3 * sx, y3 * sy);
 245         }
 246 
 247         @Override
 248         public void closePath() {
 249             out.closePath();
 250         }
 251 
 252         @Override
 253         public void pathDone() {
 254             out.pathDone();
 255         }
 256     }
 257 
 258     static final class DeltaTransformFilter implements DPathConsumer2D {
 259         private DPathConsumer2D out;
 260         private double mxx, mxy, myx, myy;
 261 
 262         DeltaTransformFilter() {}
 263 
 264         DeltaTransformFilter init(DPathConsumer2D out,
 265                                   double mxx, double mxy,
 266                                   double myx, double myy)
 267         {
 268             this.out = out;
 269             this.mxx = mxx;
 270             this.mxy = mxy;
 271             this.myx = myx;
 272             this.myy = myy;
 273             return this; // fluent API
 274         }
 275 
 276         @Override
 277         public void moveTo(double x0, double y0) {
 278             out.moveTo(x0 * mxx + y0 * mxy,
 279                        x0 * myx + y0 * myy);
 280         }
 281 
 282         @Override
 283         public void lineTo(double x1, double y1) {
 284             out.lineTo(x1 * mxx + y1 * mxy,
 285                        x1 * myx + y1 * myy);
 286         }
 287 
 288         @Override
 289         public void quadTo(double x1, double y1,
 290                            double x2, double y2)
 291         {
 292             out.quadTo(x1 * mxx + y1 * mxy,
 293                        x1 * myx + y1 * myy,
 294                        x2 * mxx + y2 * mxy,
 295                        x2 * myx + y2 * myy);
 296         }
 297 
 298         @Override
 299         public void curveTo(double x1, double y1,
 300                             double x2, double y2,
 301                             double x3, double y3)
 302         {
 303             out.curveTo(x1 * mxx + y1 * mxy,
 304                         x1 * myx + y1 * myy,
 305                         x2 * mxx + y2 * mxy,
 306                         x2 * myx + y2 * myy,
 307                         x3 * mxx + y3 * mxy,
 308                         x3 * myx + y3 * myy);
 309         }
 310 
 311         @Override
 312         public void closePath() {
 313             out.closePath();
 314         }
 315 
 316         @Override
 317         public void pathDone() {
 318             out.pathDone();
 319         }
 320     }
 321 
 322     static final class ClosedPathDetector implements DPathConsumer2D {
 323 
 324         private final DRendererContext rdrCtx;
 325         private final PolyStack stack;
 326 
 327         private DPathConsumer2D out;
 328 
 329         ClosedPathDetector(final DRendererContext rdrCtx) {
 330             this.rdrCtx = rdrCtx;
 331             this.stack = (rdrCtx.stats != null) ?
 332                 new PolyStack(rdrCtx,
 333                         rdrCtx.stats.stat_cpd_polystack_types,
 334                         rdrCtx.stats.stat_cpd_polystack_curves,
 335                         rdrCtx.stats.hist_cpd_polystack_curves,
 336                         rdrCtx.stats.stat_array_cpd_polystack_curves,
 337                         rdrCtx.stats.stat_array_cpd_polystack_types)
 338                 : new PolyStack(rdrCtx);
 339         }
 340 
 341         ClosedPathDetector init(DPathConsumer2D out) {
 342             this.out = out;
 343             return this; // fluent API
 344         }
 345 
 346         /**
 347          * Disposes this instance:
 348          * clean up before reusing this instance
 349          */
 350         void dispose() {
 351             stack.dispose();
 352         }
 353 
 354         @Override
 355         public void pathDone() {
 356             // previous path is not closed:
 357             finish(false);
 358             out.pathDone();
 359 
 360             // TODO: fix possible leak if exception happened
 361             // Dispose this instance:
 362             dispose();
 363         }
 364 
 365         @Override
 366         public void closePath() {
 367             // path is closed
 368             finish(true);
 369             out.closePath();
 370         }
 371 
 372         @Override
 373         public void moveTo(double x0, double y0) {
 374             // previous path is not closed:
 375             finish(false);
 376             out.moveTo(x0, y0);
 377         }
 378 
 379         private void finish(final boolean closed) {
 380             rdrCtx.closedPath = closed;
 381             stack.pullAll(out);
 382         }
 383 
 384         @Override
 385         public void lineTo(double x1, double y1) {
 386             stack.pushLine(x1, y1);
 387         }
 388 
 389         @Override
 390         public void curveTo(double x3, double y3,
 391                             double x2, double y2,
 392                             double x1, double y1)
 393         {
 394             stack.pushCubic(x1, y1, x2, y2, x3, y3);
 395         }
 396 
 397         @Override
 398         public void quadTo(double x2, double y2, double x1, double y1) {
 399             stack.pushQuad(x1, y1, x2, y2);
 400         }
 401     }
 402 
 403     static final class PathTracer implements DPathConsumer2D {
 404         private final String prefix;
 405         private DPathConsumer2D out;
 406 
 407         PathTracer(String name) {
 408             this.prefix = name + ": ";
 409         }
 410 
 411         PathTracer init(DPathConsumer2D out) {
 412             this.out = out;
 413             return this; // fluent API
 414         }
 415 
 416         @Override
 417         public void moveTo(double x0, double y0) {
 418             log("moveTo (" + x0 + ", " + y0 + ')');
 419             out.moveTo(x0, y0);
 420         }
 421 
 422         @Override
 423         public void lineTo(double x1, double y1) {
 424             log("lineTo (" + x1 + ", " + y1 + ')');
 425             out.lineTo(x1, y1);
 426         }
 427 
 428         @Override
 429         public void curveTo(double x1, double y1,
 430                             double x2, double y2,
 431                             double x3, double y3)
 432         {
 433             log("curveTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2  + ") P3(" + x3 + ", " + y3 + ')');
 434             out.curveTo(x1, y1, x2, y2, x3, y3);
 435         }
 436 
 437         @Override
 438         public void quadTo(double x1, double y1, double x2, double y2) {
 439             log("quadTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2  + ')');
 440             out.quadTo(x1, y1, x2, y2);
 441         }
 442 
 443         @Override
 444         public void closePath() {
 445             log("closePath");
 446             out.closePath();
 447         }
 448 
 449         @Override
 450         public void pathDone() {
 451             log("pathDone");
 452             out.pathDone();
 453         }
 454 
 455         private void log(final String message) {
 456             System.out.println(prefix + message);
 457         }
 458     }
 459 }