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 }