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 import com.sun.javafx.geom.PathConsumer2D; 29 import com.sun.javafx.geom.transform.BaseTransform; 30 import com.sun.marlin.Helpers.PolyStack; 31 32 public final class TransformingPathConsumer2D { 33 34 private final RendererContext rdrCtx; 35 36 // recycled ClosedPathDetector instance from detectClosedPath() 37 private final ClosedPathDetector cpDetector; 38 39 // recycled PathConsumer2D instances from deltaTransformConsumer() 40 private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter(); 41 private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter(); 42 43 // recycled PathConsumer2D 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 TransformingPathConsumer2D(final RendererContext rdrCtx) { 53 // used by RendererContext 54 this.rdrCtx = rdrCtx; 55 this.cpDetector = new ClosedPathDetector(rdrCtx); 56 } 57 58 public PathConsumer2D traceInput(PathConsumer2D out) { 59 return tracerInput.init(out); 60 } 61 62 public PathConsumer2D traceClosedPathDetector(PathConsumer2D out) { 63 return tracerCPDetector.init(out); 64 } 65 66 public PathConsumer2D traceStroker(PathConsumer2D out) { 67 return tracerStroker.init(out); 68 } 69 70 public PathConsumer2D detectClosedPath(PathConsumer2D out) 71 { 72 return cpDetector.init(out); 73 } 74 75 public PathConsumer2D deltaTransformConsumer(PathConsumer2D out, 76 BaseTransform at, 77 final float rdrOffX, 78 final float rdrOffY) 79 { 80 if (at == null) { 81 return out; 82 } 83 final float mxx = (float) at.getMxx(); 84 final float mxy = (float) at.getMxy(); 85 final float myx = (float) at.getMyx(); 86 final float myy = (float) at.getMyy(); 87 88 if (mxy == 0.0f && myx == 0.0f) { 89 if (mxx == 1.0f && myy == 1.0f) { 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 float[] clipRect, 111 final float rdrOffX, 112 final float 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 float[] clipRect, 121 final float mxx, final float 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 float[] clipRect, 131 final float mxx, final float mxy, 132 final float myx, final float myy) 133 { 134 // Adjust the clipping rectangle (iv_DeltaTransformFilter): 135 final float det = mxx * myy - mxy * myx; 136 final float imxx = myy / det; 137 final float imxy = -mxy / det; 138 final float imyx = -myx / det; 139 final float imyy = mxx / det; 140 141 float xmin, xmax, ymin, ymax; 142 float 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 PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out, 178 BaseTransform at) 179 { 180 if (at == null) { 181 return out; 182 } 183 float mxx = (float) at.getMxx(); 184 float mxy = (float) at.getMxy(); 185 float myx = (float) at.getMyx(); 186 float myy = (float) at.getMyy(); 187 188 if (mxy == 0.0f && myx == 0.0f) { 189 if (mxx == 1.0f && myy == 1.0f) { 190 return out; 191 } else { 192 return iv_DeltaScaleFilter.init(out, 1.0f/mxx, 1.0f/myy); 193 } 194 } else { 195 final float 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 205 static final class DeltaScaleFilter implements PathConsumer2D { 206 private PathConsumer2D out; 207 private float sx, sy; 208 209 DeltaScaleFilter() {} 210 211 DeltaScaleFilter init(PathConsumer2D out, 212 float mxx, float myy) 213 { 214 this.out = out; 215 sx = mxx; 216 sy = myy; 217 return this; // fluent API 218 } 219 220 @Override 221 public void moveTo(float x0, float y0) { 222 out.moveTo(x0 * sx, y0 * sy); 223 } 224 225 @Override 226 public void lineTo(float x1, float y1) { 227 out.lineTo(x1 * sx, y1 * sy); 228 } 229 230 @Override 231 public void quadTo(float x1, float y1, 232 float x2, float y2) 233 { 234 out.quadTo(x1 * sx, y1 * sy, 235 x2 * sx, y2 * sy); 236 } 237 238 @Override 239 public void curveTo(float x1, float y1, 240 float x2, float y2, 241 float x3, float y3) 242 { 243 out.curveTo(x1 * sx, y1 * sy, 244 x2 * sx, y2 * sy, 245 x3 * sx, y3 * sy); 246 } 247 248 @Override 249 public void closePath() { 250 out.closePath(); 251 } 252 253 @Override 254 public void pathDone() { 255 out.pathDone(); 256 } 257 } 258 259 static final class DeltaTransformFilter implements PathConsumer2D { 260 private PathConsumer2D out; 261 private float mxx, mxy, myx, myy; 262 263 DeltaTransformFilter() {} 264 265 DeltaTransformFilter init(PathConsumer2D out, 266 float mxx, float mxy, 267 float myx, float myy) 268 { 269 this.out = out; 270 this.mxx = mxx; 271 this.mxy = mxy; 272 this.myx = myx; 273 this.myy = myy; 274 return this; // fluent API 275 } 276 277 @Override 278 public void moveTo(float x0, float y0) { 279 out.moveTo(x0 * mxx + y0 * mxy, 280 x0 * myx + y0 * myy); 281 } 282 283 @Override 284 public void lineTo(float x1, float y1) { 285 out.lineTo(x1 * mxx + y1 * mxy, 286 x1 * myx + y1 * myy); 287 } 288 289 @Override 290 public void quadTo(float x1, float y1, 291 float x2, float y2) 292 { 293 out.quadTo(x1 * mxx + y1 * mxy, 294 x1 * myx + y1 * myy, 295 x2 * mxx + y2 * mxy, 296 x2 * myx + y2 * myy); 297 } 298 299 @Override 300 public void curveTo(float x1, float y1, 301 float x2, float y2, 302 float x3, float y3) 303 { 304 out.curveTo(x1 * mxx + y1 * mxy, 305 x1 * myx + y1 * myy, 306 x2 * mxx + y2 * mxy, 307 x2 * myx + y2 * myy, 308 x3 * mxx + y3 * mxy, 309 x3 * myx + y3 * myy); 310 } 311 312 @Override 313 public void closePath() { 314 out.closePath(); 315 } 316 317 @Override 318 public void pathDone() { 319 out.pathDone(); 320 } 321 } 322 323 static final class ClosedPathDetector implements PathConsumer2D { 324 325 private final RendererContext rdrCtx; 326 private final PolyStack stack; 327 328 private PathConsumer2D out; 329 330 ClosedPathDetector(final RendererContext rdrCtx) { 331 this.rdrCtx = rdrCtx; 332 this.stack = (rdrCtx.stats != null) ? 333 new PolyStack(rdrCtx, 334 rdrCtx.stats.stat_cpd_polystack_types, 335 rdrCtx.stats.stat_cpd_polystack_curves, 336 rdrCtx.stats.hist_cpd_polystack_curves, 337 rdrCtx.stats.stat_array_cpd_polystack_curves, 338 rdrCtx.stats.stat_array_cpd_polystack_types) 339 : new PolyStack(rdrCtx); 340 } 341 342 ClosedPathDetector init(PathConsumer2D out) { 343 this.out = out; 344 return this; // fluent API 345 } 346 347 /** 348 * Disposes this instance: 349 * clean up before reusing this instance 350 */ 351 void dispose() { 352 stack.dispose(); 353 } 354 355 @Override 356 public void pathDone() { 357 // previous path is not closed: 358 finish(false); 359 out.pathDone(); 360 361 // TODO: fix possible leak if exception happened 362 // Dispose this instance: 363 dispose(); 364 } 365 366 @Override 367 public void closePath() { 368 // path is closed 369 finish(true); 370 out.closePath(); 371 } 372 373 @Override 374 public void moveTo(float x0, float y0) { 375 // previous path is not closed: 376 finish(false); 377 out.moveTo(x0, y0); 378 } 379 380 private void finish(final boolean closed) { 381 rdrCtx.closedPath = closed; 382 stack.pullAll(out); 383 } 384 385 @Override 386 public void lineTo(float x1, float y1) { 387 stack.pushLine(x1, y1); 388 } 389 390 @Override 391 public void curveTo(float x3, float y3, 392 float x2, float y2, 393 float x1, float y1) 394 { 395 stack.pushCubic(x1, y1, x2, y2, x3, y3); 396 } 397 398 @Override 399 public void quadTo(float x2, float y2, float x1, float y1) { 400 stack.pushQuad(x1, y1, x2, y2); 401 } 402 } 403 404 static final class PathTracer implements PathConsumer2D { 405 private final String prefix; 406 private PathConsumer2D out; 407 408 PathTracer(String name) { 409 this.prefix = name + ": "; 410 } 411 412 PathTracer init(PathConsumer2D out) { 413 this.out = out; 414 return this; // fluent API 415 } 416 417 @Override 418 public void moveTo(float x0, float y0) { 419 log("moveTo (" + x0 + ", " + y0 + ')'); 420 out.moveTo(x0, y0); 421 } 422 423 @Override 424 public void lineTo(float x1, float y1) { 425 log("lineTo (" + x1 + ", " + y1 + ')'); 426 out.lineTo(x1, y1); 427 } 428 429 @Override 430 public void curveTo(float x1, float y1, 431 float x2, float y2, 432 float x3, float y3) 433 { 434 log("curveTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ')'); 435 out.curveTo(x1, y1, x2, y2, x3, y3); 436 } 437 438 @Override 439 public void quadTo(float x1, float y1, float x2, float y2) { 440 log("quadTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ')'); 441 out.quadTo(x1, y1, x2, y2); 442 } 443 444 @Override 445 public void closePath() { 446 log("closePath"); 447 out.closePath(); 448 } 449 450 @Override 451 public void pathDone() { 452 log("pathDone"); 453 out.pathDone(); 454 } 455 456 private void log(final String message) { 457 System.out.println(prefix + message); 458 } 459 } 460 }