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 32 final class TransformingPathConsumer2D { 33 34 TransformingPathConsumer2D() { 35 // used by RendererContext 36 } 37 38 // recycled PathConsumer2D instance from wrapPath2d() 39 private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper(); 40 41 PathConsumer2D wrapPath2d(Path2D.Float p2d) 42 { 43 return wp_Path2DWrapper.init(p2d); 44 } 45 46 // recycled PathConsumer2D instances from deltaTransformConsumer() 47 private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter(); 48 private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter(); 49 50 PathConsumer2D deltaTransformConsumer(PathConsumer2D out, 51 AffineTransform at) 52 { 53 if (at == null) { 54 return out; 55 } 56 float mxx = (float) at.getScaleX(); 57 float mxy = (float) at.getShearX(); 58 float myx = (float) at.getShearY(); 59 float myy = (float) at.getScaleY(); 60 61 if (mxy == 0.0f && myx == 0.0f) { 62 if (mxx == 1.0f && myy == 1.0f) { 63 return out; 64 } else { 65 return dt_DeltaScaleFilter.init(out, mxx, myy); 66 } 67 } else { 68 return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy); 69 } 70 } 71 72 // recycled PathConsumer2D instances from inverseDeltaTransformConsumer() 73 private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter(); 74 private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter(); 75 76 PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out, 77 AffineTransform at) 78 { 79 if (at == null) { 80 return out; 81 } 82 float mxx = (float) at.getScaleX(); 83 float mxy = (float) at.getShearX(); 84 float myx = (float) at.getShearY(); 85 float myy = (float) at.getScaleY(); 86 87 if (mxy == 0.0f && myx == 0.0f) { 88 if (mxx == 1.0f && myy == 1.0f) { 89 return out; 90 } else { 91 return iv_DeltaScaleFilter.init(out, 1.0f/mxx, 1.0f/myy); 92 } 93 } else { 94 float det = mxx * myy - mxy * myx; 95 return iv_DeltaTransformFilter.init(out, 96 myy / det, 97 -mxy / det, 98 -myx / det, 99 mxx / det); 100 } 101 } 102 103 104 static final class DeltaScaleFilter implements PathConsumer2D { 105 private PathConsumer2D out; 106 private float sx, sy; 107 108 DeltaScaleFilter() {} 109 110 DeltaScaleFilter init(PathConsumer2D out, 111 float mxx, float myy) 112 { 113 this.out = out; 114 sx = mxx; 115 sy = myy; 116 return this; // fluent API 117 } 118 119 @Override 120 public void moveTo(float x0, float y0) { 121 out.moveTo(x0 * sx, y0 * sy); 122 } 123 251 252 @Override 253 public void closePath() { 254 p2d.closePath(); 255 } 256 257 @Override 258 public void pathDone() {} 259 260 @Override 261 public void curveTo(float x1, float y1, 262 float x2, float y2, 263 float x3, float y3) 264 { 265 p2d.curveTo(x1, y1, x2, y2, x3, y3); 266 } 267 268 @Override 269 public void quadTo(float x1, float y1, float x2, float y2) { 270 p2d.quadTo(x1, y1, x2, y2); 271 } 272 273 @Override 274 public long getNativeConsumer() { 275 throw new InternalError("Not using a native peer"); 276 } 277 } 278 } | 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 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 } |