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 } --- EOF ---