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