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 sun.java2d.marlin;
  27 
  28 import java.awt.geom.AffineTransform;
  29 import java.awt.geom.Path2D;
  30 import sun.java2d.marlin.DHelpers.PolyStack;
  31 
  32 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 instance from wrapPath2d()
  40     private final Path2DWrapper        wp_Path2DWrapper        = new Path2DWrapper();
  41 
  42     // recycled DPathConsumer2D instances from deltaTransformConsumer()
  43     private final DeltaScaleFilter     dt_DeltaScaleFilter     = new DeltaScaleFilter();
  44     private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
  45 
  46     // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer()
  47     private final DeltaScaleFilter     iv_DeltaScaleFilter     = new DeltaScaleFilter();
  48     private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
  49 
  50     // recycled PathTracer instances from tracer...() methods
  51     private final PathTracer tracerInput      = new PathTracer("[Input]");
  52     private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector");
  53     private final PathTracer tracerStroker    = new PathTracer("Stroker");
  54 
  55     DTransformingPathConsumer2D(final DRendererContext rdrCtx) {
  56         // used by RendererContext
  57         this.rdrCtx = rdrCtx;
  58         this.cpDetector = new ClosedPathDetector(rdrCtx);
  59     }
  60 
  61     DPathConsumer2D wrapPath2d(Path2D.Double p2d)
  62     {
  63         return wp_Path2DWrapper.init(p2d);
  64     }
  65 
  66     DPathConsumer2D traceInput(DPathConsumer2D out) {
  67         return tracerInput.init(out);
  68     }
  69 
  70     DPathConsumer2D traceClosedPathDetector(DPathConsumer2D out) {
  71         return tracerCPDetector.init(out);
  72     }
  73 
  74     DPathConsumer2D traceStroker(DPathConsumer2D out) {
  75         return tracerStroker.init(out);
  76     }
  77 
  78     DPathConsumer2D detectClosedPath(DPathConsumer2D out)
  79     {
  80         return cpDetector.init(out);
  81     }
  82 
  83     DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out,
  84                                           AffineTransform at)
  85     {
  86         if (at == null) {
  87             return out;
  88         }
  89         double mxx = at.getScaleX();
  90         double mxy = at.getShearX();
  91         double myx = at.getShearY();
  92         double myy = at.getScaleY();
  93 
  94         if (mxy == 0.0d && myx == 0.0d) {
  95             if (mxx == 1.0d && myy == 1.0d) {
  96                 return out;
  97             } else {
  98                 // Scale only
  99                 if (rdrCtx.doClip) {
 100                     // adjust clip rectangle (ymin, ymax, xmin, xmax):
 101                     adjustClipScale(rdrCtx.clipRect, mxx, myy);
 102                 }
 103                 return dt_DeltaScaleFilter.init(out, mxx, myy);
 104             }
 105         } else {
 106             if (rdrCtx.doClip) {
 107                 // adjust clip rectangle (ymin, ymax, xmin, xmax):
 108                 adjustClipInverseDelta(rdrCtx.clipRect, mxx, mxy, myx, myy);
 109             }
 110             return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
 111         }
 112     }
 113 
 114     private static void adjustClipOffset(final double[] clipRect) {
 115         clipRect[0] += Renderer.RDR_OFFSET_Y;
 116         clipRect[1] += Renderer.RDR_OFFSET_Y;
 117         clipRect[2] += Renderer.RDR_OFFSET_X;
 118         clipRect[3] += Renderer.RDR_OFFSET_X;
 119     }
 120 
 121     private static void adjustClipScale(final double[] clipRect,
 122                                         final double mxx, final double myy)
 123     {
 124         adjustClipOffset(clipRect);
 125 
 126         // Adjust the clipping rectangle (iv_DeltaScaleFilter):
 127         clipRect[0] /= myy;
 128         clipRect[1] /= myy;
 129         clipRect[2] /= mxx;
 130         clipRect[3] /= mxx;
 131     }
 132 
 133     private static void adjustClipInverseDelta(final double[] clipRect,
 134                                                final double mxx, final double mxy,
 135                                                final double myx, final double myy)
 136     {
 137         adjustClipOffset(clipRect);
 138 
 139         // Adjust the clipping rectangle (iv_DeltaTransformFilter):
 140         final double det = mxx * myy - mxy * myx;
 141         final double imxx =  myy / det;
 142         final double imxy = -mxy / det;
 143         final double imyx = -myx / det;
 144         final double imyy =  mxx / det;
 145 
 146         double xmin, xmax, ymin, ymax;
 147         double x, y;
 148         // xmin, ymin:
 149         x = clipRect[2] * imxx + clipRect[0] * imxy;
 150         y = clipRect[2] * imyx + clipRect[0] * imyy;
 151 
 152         xmin = xmax = x;
 153         ymin = ymax = y;
 154 
 155         // xmax, ymin:
 156         x = clipRect[3] * imxx + clipRect[0] * imxy;
 157         y = clipRect[3] * imyx + clipRect[0] * imyy;
 158 
 159         if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
 160         if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
 161 
 162         // xmin, ymax:
 163         x = clipRect[2] * imxx + clipRect[1] * imxy;
 164         y = clipRect[2] * imyx + clipRect[1] * imyy;
 165 
 166         if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
 167         if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
 168 
 169         // xmax, ymax:
 170         x = clipRect[3] * imxx + clipRect[1] * imxy;
 171         y = clipRect[3] * imyx + clipRect[1] * imyy;
 172 
 173         if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
 174         if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
 175 
 176         clipRect[0] = ymin;
 177         clipRect[1] = ymax;
 178         clipRect[2] = xmin;
 179         clipRect[3] = xmax;
 180     }
 181 
 182     DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out,
 183                                                  AffineTransform at)
 184     {
 185         if (at == null) {
 186             return out;
 187         }
 188         double mxx = at.getScaleX();
 189         double mxy = at.getShearX();
 190         double myx = at.getShearY();
 191         double myy = at.getScaleY();
 192 
 193         if (mxy == 0.0d && myx == 0.0d) {
 194             if (mxx == 1.0d && myy == 1.0d) {
 195                 return out;
 196             } else {
 197                 return iv_DeltaScaleFilter.init(out, 1.0d/mxx, 1.0d/myy);
 198             }
 199         } else {
 200             final double det = mxx * myy - mxy * myx;
 201             return iv_DeltaTransformFilter.init(out,
 202                                                 myy / det,
 203                                                -mxy / det,
 204                                                -myx / det,
 205                                                 mxx / det);
 206         }
 207     }
 208 
 209     static final class DeltaScaleFilter implements DPathConsumer2D {
 210         private DPathConsumer2D out;
 211         private double sx, sy;
 212 
 213         DeltaScaleFilter() {}
 214 
 215         DeltaScaleFilter init(DPathConsumer2D out,
 216                               double mxx, double myy)
 217         {
 218             this.out = out;
 219             sx = mxx;
 220             sy = myy;
 221             return this; // fluent API
 222         }
 223 
 224         @Override
 225         public void moveTo(double x0, double y0) {
 226             out.moveTo(x0 * sx, y0 * sy);
 227         }
 228 
 229         @Override
 230         public void lineTo(double x1, double y1) {
 231             out.lineTo(x1 * sx, y1 * sy);
 232         }
 233 
 234         @Override
 235         public void quadTo(double x1, double y1,
 236                            double x2, double y2)
 237         {
 238             out.quadTo(x1 * sx, y1 * sy,
 239                        x2 * sx, y2 * sy);
 240         }
 241 
 242         @Override
 243         public void curveTo(double x1, double y1,
 244                             double x2, double y2,
 245                             double x3, double y3)
 246         {
 247             out.curveTo(x1 * sx, y1 * sy,
 248                         x2 * sx, y2 * sy,
 249                         x3 * sx, y3 * sy);
 250         }
 251 
 252         @Override
 253         public void closePath() {
 254             out.closePath();
 255         }
 256 
 257         @Override
 258         public void pathDone() {
 259             out.pathDone();
 260         }
 261 
 262         @Override
 263         public long getNativeConsumer() {
 264             return 0;
 265         }
 266     }
 267 
 268     static final class DeltaTransformFilter implements DPathConsumer2D {
 269         private DPathConsumer2D out;
 270         private double mxx, mxy, myx, myy;
 271 
 272         DeltaTransformFilter() {}
 273 
 274         DeltaTransformFilter init(DPathConsumer2D out,
 275                                   double mxx, double mxy,
 276                                   double myx, double myy)
 277         {
 278             this.out = out;
 279             this.mxx = mxx;
 280             this.mxy = mxy;
 281             this.myx = myx;
 282             this.myy = myy;
 283             return this; // fluent API
 284         }
 285 
 286         @Override
 287         public void moveTo(double x0, double y0) {
 288             out.moveTo(x0 * mxx + y0 * mxy,
 289                        x0 * myx + y0 * myy);
 290         }
 291 
 292         @Override
 293         public void lineTo(double x1, double y1) {
 294             out.lineTo(x1 * mxx + y1 * mxy,
 295                        x1 * myx + y1 * myy);
 296         }
 297 
 298         @Override
 299         public void quadTo(double x1, double y1,
 300                            double x2, double y2)
 301         {
 302             out.quadTo(x1 * mxx + y1 * mxy,
 303                        x1 * myx + y1 * myy,
 304                        x2 * mxx + y2 * mxy,
 305                        x2 * myx + y2 * myy);
 306         }
 307 
 308         @Override
 309         public void curveTo(double x1, double y1,
 310                             double x2, double y2,
 311                             double x3, double y3)
 312         {
 313             out.curveTo(x1 * mxx + y1 * mxy,
 314                         x1 * myx + y1 * myy,
 315                         x2 * mxx + y2 * mxy,
 316                         x2 * myx + y2 * myy,
 317                         x3 * mxx + y3 * mxy,
 318                         x3 * myx + y3 * myy);
 319         }
 320 
 321         @Override
 322         public void closePath() {
 323             out.closePath();
 324         }
 325 
 326         @Override
 327         public void pathDone() {
 328             out.pathDone();
 329         }
 330 
 331         @Override
 332         public long getNativeConsumer() {
 333             return 0;
 334         }
 335     }
 336 
 337     static final class Path2DWrapper implements DPathConsumer2D {
 338         private Path2D.Double p2d;
 339 
 340         Path2DWrapper() {}
 341 
 342         Path2DWrapper init(Path2D.Double p2d) {
 343             this.p2d = p2d;
 344             return this;
 345         }
 346 
 347         @Override
 348         public void moveTo(double x0, double y0) {
 349             p2d.moveTo(x0, y0);
 350         }
 351 
 352         @Override
 353         public void lineTo(double x1, double y1) {
 354             p2d.lineTo(x1, y1);
 355         }
 356 
 357         @Override
 358         public void closePath() {
 359             p2d.closePath();
 360         }
 361 
 362         @Override
 363         public void pathDone() {}
 364 
 365         @Override
 366         public void curveTo(double x1, double y1,
 367                             double x2, double y2,
 368                             double x3, double y3)
 369         {
 370             p2d.curveTo(x1, y1, x2, y2, x3, y3);
 371         }
 372 
 373         @Override
 374         public void quadTo(double x1, double y1, double x2, double y2) {
 375             p2d.quadTo(x1, y1, x2, y2);
 376         }
 377 
 378         @Override
 379         public long getNativeConsumer() {
 380             throw new InternalError("Not using a native peer");
 381         }
 382     }
 383 
 384     static final class ClosedPathDetector implements DPathConsumer2D {
 385 
 386         private final DRendererContext rdrCtx;
 387         private final PolyStack stack;
 388 
 389         private DPathConsumer2D out;
 390 
 391         ClosedPathDetector(final DRendererContext rdrCtx) {
 392             this.rdrCtx = rdrCtx;
 393             this.stack = (rdrCtx.stats != null) ?
 394                 new PolyStack(rdrCtx,
 395                         rdrCtx.stats.stat_cpd_polystack_types,
 396                         rdrCtx.stats.stat_cpd_polystack_curves,
 397                         rdrCtx.stats.hist_cpd_polystack_curves,
 398                         rdrCtx.stats.stat_array_cpd_polystack_curves,
 399                         rdrCtx.stats.stat_array_cpd_polystack_types)
 400                 : new PolyStack(rdrCtx);
 401         }
 402 
 403         ClosedPathDetector init(DPathConsumer2D out) {
 404             this.out = out;
 405             return this; // fluent API
 406         }
 407 
 408         /**
 409          * Disposes this instance:
 410          * clean up before reusing this instance
 411          */
 412         void dispose() {
 413             stack.dispose();
 414         }
 415 
 416         @Override
 417         public void pathDone() {
 418             // previous path is not closed:
 419             finish(false);
 420             out.pathDone();
 421 
 422             // TODO: fix possible leak if exception happened
 423             // Dispose this instance:
 424             dispose();
 425         }
 426 
 427         @Override
 428         public void closePath() {
 429             // path is closed
 430             finish(true);
 431             out.closePath();
 432         }
 433 
 434         @Override
 435         public void moveTo(double x0, double y0) {
 436             // previous path is not closed:
 437             finish(false);
 438             out.moveTo(x0, y0);
 439         }
 440 
 441         private void finish(final boolean closed) {
 442             rdrCtx.closedPath = closed;
 443             stack.pullAll(out);
 444         }
 445 
 446         @Override
 447         public void lineTo(double x1, double y1) {
 448             stack.pushLine(x1, y1);
 449         }
 450 
 451         @Override
 452         public void curveTo(double x3, double y3,
 453                             double x2, double y2,
 454                             double x1, double y1)
 455         {
 456             stack.pushCubic(x1, y1, x2, y2, x3, y3);
 457         }
 458 
 459         @Override
 460         public void quadTo(double x2, double y2, double x1, double y1) {
 461             stack.pushQuad(x1, y1, x2, y2);
 462         }
 463 
 464         @Override
 465         public long getNativeConsumer() {
 466             throw new InternalError("Not using a native peer");
 467         }
 468     }
 469 
 470     static final class PathTracer implements DPathConsumer2D {
 471         private final String prefix;
 472         private DPathConsumer2D out;
 473 
 474         PathTracer(String name) {
 475             this.prefix = name + ": ";
 476         }
 477 
 478         PathTracer init(DPathConsumer2D out) {
 479             this.out = out;
 480             return this; // fluent API
 481         }
 482 
 483         @Override
 484         public void moveTo(double x0, double y0) {
 485             log("moveTo (" + x0 + ", " + y0 + ')');
 486             out.moveTo(x0, y0);
 487         }
 488 
 489         @Override
 490         public void lineTo(double x1, double y1) {
 491             log("lineTo (" + x1 + ", " + y1 + ')');
 492             out.lineTo(x1, y1);
 493         }
 494 
 495         @Override
 496         public void curveTo(double x1, double y1,
 497                             double x2, double y2,
 498                             double x3, double y3)
 499         {
 500             log("curveTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2  + ") P3(" + x3 + ", " + y3 + ')');
 501             out.curveTo(x1, y1, x2, y2, x3, y3);
 502         }
 503 
 504         @Override
 505         public void quadTo(double x1, double y1, double x2, double y2) {
 506             log("quadTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2  + ')');
 507             out.quadTo(x1, y1, x2, y2);
 508         }
 509 
 510         @Override
 511         public void closePath() {
 512             log("closePath");
 513             out.closePath();
 514         }
 515 
 516         @Override
 517         public void pathDone() {
 518             log("pathDone");
 519             out.pathDone();
 520         }
 521 
 522         private void log(final String message) {
 523             System.out.println(prefix + message);
 524         }
 525 
 526         @Override
 527         public long getNativeConsumer() {
 528             throw new InternalError("Not using a native peer");
 529         }
 530     }
 531 }