Showing the changes to java/pkg/sun/java2d/pisces/PiscesRenderingEngine.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,656 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2007, 2011, 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.pisces; | ||
| 27 | |||
| 28 | import java.awt.Shape; | ||
| 29 | import java.awt.BasicStroke; | ||
| 30 | import java.awt.geom.Path2D; | ||
| 31 | import java.awt.geom.AffineTransform; | ||
| 32 | import java.awt.geom.PathIterator; | ||
| 33 | |||
| 34 | import sun.awt.geom.PathConsumer2D; | ||
| 35 | import sun.java2d.pipe.Region; | ||
| 36 | import sun.java2d.pipe.RenderingEngine; | ||
| 37 | import sun.java2d.pipe.AATileGenerator; | ||
| 38 | |||
| 39 | public class PiscesRenderingEngine extends RenderingEngine { | ||
| 40 | private static enum NormMode {OFF, ON_NO_AA, ON_WITH_AA} | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Create a widened path as specified by the parameters. | ||
| 44 | * <p> | ||
| 45 | * The specified {@code src} {@link Shape} is widened according | ||
| 46 | * to the specified attribute parameters as per the | ||
| 47 | * {@link BasicStroke} specification. | ||
| 48 | * | ||
| 49 | * @param src the source path to be widened | ||
| 50 | * @param width the width of the widened path as per {@code BasicStroke} | ||
| 51 | * @param caps the end cap decorations as per {@code BasicStroke} | ||
| 52 | * @param join the segment join decorations as per {@code BasicStroke} | ||
| 53 | * @param miterlimit the miter limit as per {@code BasicStroke} | ||
| 54 | * @param dashes the dash length array as per {@code BasicStroke} | ||
| 55 | * @param dashphase the initial dash phase as per {@code BasicStroke} | ||
| 56 | * @return the widened path stored in a new {@code Shape} object | ||
| 57 | * @since 1.7 | ||
| 58 | */ | ||
| 59 | public Shape createStrokedShape(Shape src, | ||
| 60 | float width, | ||
| 61 | int caps, | ||
| 62 | int join, | ||
| 63 | float miterlimit, | ||
| 64 | float dashes[], | ||
| 65 | float dashphase) | ||
| 66 | { | ||
| 67 | final Path2D p2d = new Path2D.Float(); | ||
| 68 | |||
| 69 | strokeTo(src, | ||
| 70 | null, | ||
| 71 | width, | ||
| 72 | NormMode.OFF, | ||
| 73 | caps, | ||
| 74 | join, | ||
| 75 | miterlimit, | ||
| 76 | dashes, | ||
| 77 | dashphase, | ||
| 78 | new PathConsumer2D() { | ||
| 79 | public void moveTo(float x0, float y0) { | ||
| 80 | p2d.moveTo(x0, y0); | ||
| 81 | } | ||
| 82 | public void lineTo(float x1, float y1) { | ||
| 83 | p2d.lineTo(x1, y1); | ||
| 84 | } | ||
| 85 | public void closePath() { | ||
| 86 | p2d.closePath(); | ||
| 87 | } | ||
| 88 | public void pathDone() {} | ||
| 89 | public void curveTo(float x1, float y1, | ||
| 90 | float x2, float y2, | ||
| 91 | float x3, float y3) { | ||
| 92 | p2d.curveTo(x1, y1, x2, y2, x3, y3); | ||
| 93 | } | ||
| 94 | public void quadTo(float x1, float y1, float x2, float y2) { | ||
| 95 | p2d.quadTo(x1, y1, x2, y2); | ||
| 96 | } | ||
| 97 | public long getNativeConsumer() { | ||
| 98 | throw new InternalError("Not using a native peer"); | ||
| 99 | } | ||
| 100 | }); | ||
| 101 | return p2d; | ||
| 102 | } | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Sends the geometry for a widened path as specified by the parameters | ||
| 106 | * to the specified consumer. | ||
| 107 | * <p> | ||
| 108 | * The specified {@code src} {@link Shape} is widened according | ||
| 109 | * to the parameters specified by the {@link BasicStroke} object. | ||
| 110 | * Adjustments are made to the path as appropriate for the | ||
| 111 | * {@link VALUE_STROKE_NORMALIZE} hint if the {@code normalize} | ||
| 112 | * boolean parameter is true. | ||
| 113 | * Adjustments are made to the path as appropriate for the | ||
| 114 | * {@link VALUE_ANTIALIAS_ON} hint if the {@code antialias} | ||
| 115 | * boolean parameter is true. | ||
| 116 | * <p> | ||
| 117 | * The geometry of the widened path is forwarded to the indicated | ||
| 118 | * {@link PathConsumer2D} object as it is calculated. | ||
| 119 | * | ||
| 120 | * @param src the source path to be widened | ||
| 121 | * @param bs the {@code BasicSroke} object specifying the | ||
| 122 | * decorations to be applied to the widened path | ||
| 123 | * @param normalize indicates whether stroke normalization should | ||
| 124 | * be applied | ||
| 125 | * @param antialias indicates whether or not adjustments appropriate | ||
| 126 | * to antialiased rendering should be applied | ||
| 127 | * @param consumer the {@code PathConsumer2D} instance to forward | ||
| 128 | * the widened geometry to | ||
| 129 | * @since 1.7 | ||
| 130 | */ | ||
| 131 | public void strokeTo(Shape src, | ||
| 132 | AffineTransform at, | ||
| 133 | BasicStroke bs, | ||
| 134 | boolean thin, | ||
| 135 | boolean normalize, | ||
| 136 | boolean antialias, | ||
| 137 | final PathConsumer2D consumer) | ||
| 138 | { | ||
| 139 | NormMode norm = (normalize) ? | ||
| 140 | ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA) | ||
| 141 | : NormMode.OFF; | ||
| 142 | strokeTo(src, at, bs, thin, norm, antialias, consumer); | ||
| 143 | } | ||
| 144 | |||
| 145 | void strokeTo(Shape src, | ||
| 146 | AffineTransform at, | ||
| 147 | BasicStroke bs, | ||
| 148 | boolean thin, | ||
| 149 | NormMode normalize, | ||
| 150 | boolean antialias, | ||
| 151 | PathConsumer2D pc2d) | ||
| 152 | { | ||
| 153 | float lw; | ||
| 154 | if (thin) { | ||
| 155 | if (antialias) { | ||
| 156 | lw = userSpaceLineWidth(at, 0.5f); | ||
| 157 | } else { | ||
| 158 | lw = userSpaceLineWidth(at, 1.0f); | ||
| 159 | } | ||
| 160 | } else { | ||
| 161 | lw = bs.getLineWidth(); | ||
| 162 | } | ||
| 163 | strokeTo(src, | ||
| 164 | at, | ||
| 165 | lw, | ||
| 166 | normalize, | ||
| 167 | bs.getEndCap(), | ||
| 168 | bs.getLineJoin(), | ||
| 169 | bs.getMiterLimit(), | ||
| 170 | bs.getDashArray(), | ||
| 171 | bs.getDashPhase(), | ||
| 172 | pc2d); | ||
| 173 | } | ||
| 174 | |||
| 175 | private float userSpaceLineWidth(AffineTransform at, float lw) { | ||
| 176 | |||
| 177 | double widthScale; | ||
| 178 | |||
| 179 | if ((at.getType() & (AffineTransform.TYPE_GENERAL_TRANSFORM | | ||
| 180 | AffineTransform.TYPE_GENERAL_SCALE)) != 0) { | ||
| 181 | widthScale = Math.sqrt(at.getDeterminant()); | ||
| 182 | } else { | ||
| 183 | /* First calculate the "maximum scale" of this transform. */ | ||
| 184 | double A = at.getScaleX(); // m00 | ||
| 185 | double C = at.getShearX(); // m01 | ||
| 186 | double B = at.getShearY(); // m10 | ||
| 187 | double D = at.getScaleY(); // m11 | ||
| 188 | |||
| 189 | /* | ||
| 190 | * Given a 2 x 2 affine matrix [ A B ] such that | ||
| 191 | * [ C D ] | ||
| 192 | * v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to | ||
| 193 | * find the maximum magnitude (norm) of the vector v' | ||
| 194 | * with the constraint (x^2 + y^2 = 1). | ||
| 195 | * The equation to maximize is | ||
| 196 | * |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2) | ||
| 197 | * or |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2). | ||
| 198 | * Since sqrt is monotonic we can maximize |v'|^2 | ||
| 199 | * instead and plug in the substitution y = sqrt(1 - x^2). | ||
| 200 | * Trigonometric equalities can then be used to get | ||
| 201 | * rid of most of the sqrt terms. | ||
| 202 | */ | ||
| 203 | |||
| 204 | double EA = A*A + B*B; // x^2 coefficient | ||
| 205 | double EB = 2*(A*C + B*D); // xy coefficient | ||
| 206 | double EC = C*C + D*D; // y^2 coefficient | ||
| 207 | |||
| 208 | /* | ||
| 209 | * There is a lot of calculus omitted here. | ||
| 210 | * | ||
| 211 | * Conceptually, in the interests of understanding the | ||
| 212 | * terms that the calculus produced we can consider | ||
| 213 | * that EA and EC end up providing the lengths along | ||
| 214 | * the major axes and the hypot term ends up being an | ||
| 215 | * adjustment for the additional length along the off-axis | ||
| 216 | * angle of rotated or sheared ellipses as well as an | ||
| 217 | * adjustment for the fact that the equation below | ||
| 218 | * averages the two major axis lengths. (Notice that | ||
| 219 | * the hypot term contains a part which resolves to the | ||
| 220 | * difference of these two axis lengths in the absence | ||
| 221 | * of rotation.) | ||
| 222 | * | ||
| 223 | * In the calculus, the ratio of the EB and (EA-EC) terms | ||
| 224 | * ends up being the tangent of 2*theta where theta is | ||
| 225 | * the angle that the long axis of the ellipse makes | ||
| 226 | * with the horizontal axis. Thus, this equation is | ||
| 227 | * calculating the length of the hypotenuse of a triangle | ||
| 228 | * along that axis. | ||
| 229 | */ | ||
| 230 | |||
| 231 | double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC)); | ||
| 232 | /* sqrt omitted, compare to squared limits below. */ | ||
| 233 | double widthsquared = ((EA + EC + hypot)/2.0); | ||
| 234 | |||
| 235 | widthScale = Math.sqrt(widthsquared); | ||
| 236 | } | ||
| 237 | |||
| 238 | return (float) (lw / widthScale); | ||
| 239 | } | ||
| 240 | |||
| 241 | void strokeTo(Shape src, | ||
| 242 | AffineTransform at, | ||
| 243 | float width, | ||
| 244 | NormMode normalize, | ||
| 245 | int caps, | ||
| 246 | int join, | ||
| 247 | float miterlimit, | ||
| 248 | float dashes[], | ||
| 249 | float dashphase, | ||
| 250 | PathConsumer2D pc2d) | ||
| 251 | { | ||
| 252 | // We use strokerat and outat so that in Stroker and Dasher we can work only | ||
| 253 | // with the pre-transformation coordinates. This will repeat a lot of | ||
| 254 | // computations done in the path iterator, but the alternative is to | ||
| 255 | // work with transformed paths and compute untransformed coordinates | ||
| 256 | // as needed. This would be faster but I do not think the complexity | ||
| 257 | // of working with both untransformed and transformed coordinates in | ||
| 258 | // the same code is worth it. | ||
| 259 | // However, if a path's width is constant after a transformation, | ||
| 260 | // we can skip all this untransforming. | ||
| 261 | |||
| 262 | // If normalization is off we save some transformations by not | ||
| 263 | // transforming the input to pisces. Instead, we apply the | ||
| 264 | // transformation after the path processing has been done. | ||
| 265 | // We can't do this if normalization is on, because it isn't a good | ||
| 266 | // idea to normalize before the transformation is applied. | ||
| 267 | AffineTransform strokerat = null; | ||
| 268 | AffineTransform outat = null; | ||
| 269 | |||
| 270 | PathIterator pi = null; | ||
| 271 | |||
| 272 | if (at != null && !at.isIdentity()) { | ||
| 273 | final double a = at.getScaleX(); | ||
| 274 | final double b = at.getShearX(); | ||
| 275 | final double c = at.getShearY(); | ||
| 276 | final double d = at.getScaleY(); | ||
| 277 | final double det = a * d - c * b; | ||
| 278 | if (Math.abs(det) <= 2 * Float.MIN_VALUE) { | ||
| 279 | // this rendering engine takes one dimensional curves and turns | ||
| 280 | // them into 2D shapes by giving them width. | ||
| 281 | // However, if everything is to be passed through a singular | ||
| 282 | // transformation, these 2D shapes will be squashed down to 1D | ||
| 283 | // again so, nothing can be drawn. | ||
| 284 | |||
| 285 | // Every path needs an initial moveTo and a pathDone. If these | ||
| 286 | // are not there this causes a SIGSEGV in libawt.so (at the time | ||
| 287 | // of writing of this comment (September 16, 2010)). Actually, | ||
| 288 | // I am not sure if the moveTo is necessary to avoid the SIGSEGV | ||
| 289 | // but the pathDone is definitely needed. | ||
| 290 | pc2d.moveTo(0, 0); | ||
| 291 | pc2d.pathDone(); | ||
| 292 | return; | ||
| 293 | } | ||
| 294 | |||
| 295 | // If the transform is a constant multiple of an orthogonal transformation | ||
| 296 | // then every length is just multiplied by a constant, so we just | ||
| 297 | // need to transform input paths to stroker and tell stroker | ||
| 298 | // the scaled width. This condition is satisfied if | ||
| 299 | // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we | ||
| 300 | // leave a bit of room for error. | ||
| 301 | if (nearZero(a*b + c*d, 2) && nearZero(a*a+c*c - (b*b+d*d), 2)) { | ||
| 302 | double scale = Math.sqrt(a*a + c*c); | ||
| 303 | if (dashes != null) { | ||
| 304 | dashes = java.util.Arrays.copyOf(dashes, dashes.length); | ||
| 305 | for (int i = 0; i < dashes.length; i++) { | ||
| 306 | dashes[i] = (float)(scale * dashes[i]); | ||
| 307 | } | ||
| 308 | dashphase = (float)(scale * dashphase); | ||
| 309 | } | ||
| 310 | width = (float)(scale * width); | ||
| 311 | pi = src.getPathIterator(at); | ||
| 312 | if (normalize != NormMode.OFF) { | ||
| 313 | pi = new NormalizingPathIterator(pi, normalize); | ||
| 314 | } | ||
| 315 | // by now strokerat == null && outat == null. Input paths to | ||
| 316 | // stroker (and maybe dasher) will have the full transform at | ||
| 317 | // applied to them and nothing will happen to the output paths. | ||
| 318 | } else { | ||
| 319 | if (normalize != NormMode.OFF) { | ||
| 320 | strokerat = at; | ||
| 321 | pi = src.getPathIterator(at); | ||
| 322 | pi = new NormalizingPathIterator(pi, normalize); | ||
| 323 | // by now strokerat == at && outat == null. Input paths to | ||
| 324 | // stroker (and maybe dasher) will have the full transform at | ||
| 325 | // applied to them, then they will be normalized, and then | ||
| 326 | // the inverse of *only the non translation part of at* will | ||
| 327 | // be applied to the normalized paths. This won't cause problems | ||
| 328 | // in stroker, because, suppose at = T*A, where T is just the | ||
| 329 | // translation part of at, and A is the rest. T*A has already | ||
| 330 | // been applied to Stroker/Dasher's input. Then Ainv will be | ||
| 331 | // applied. Ainv*T*A is not equal to T, but it is a translation, | ||
| 332 | // which means that none of stroker's assumptions about its | ||
| 333 | // input will be violated. After all this, A will be applied | ||
| 334 | // to stroker's output. | ||
| 335 | } else { | ||
| 336 | outat = at; | ||
| 337 | pi = src.getPathIterator(null); | ||
| 338 | // outat == at && strokerat == null. This is because if no | ||
| 339 | // normalization is done, we can just apply all our | ||
| 340 | // transformations to stroker's output. | ||
| 341 | } | ||
| 342 | } | ||
| 343 | } else { | ||
| 344 | // either at is null or it's the identity. In either case | ||
| 345 | // we don't transform the path. | ||
| 346 | pi = src.getPathIterator(null); | ||
| 347 | if (normalize != NormMode.OFF) { | ||
| 348 | pi = new NormalizingPathIterator(pi, normalize); | ||
| 349 | } | ||
| 350 | } | ||
| 351 | |||
| 352 | // by now, at least one of outat and strokerat will be null. Unless at is not | ||
| 353 | // a constant multiple of an orthogonal transformation, they will both be | ||
| 354 | // null. In other cases, outat == at if normalization is off, and if | ||
| 355 | // normalization is on, strokerat == at. | ||
| 356 | pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, outat); | ||
| 357 | pc2d = TransformingPathConsumer2D.deltaTransformConsumer(pc2d, strokerat); | ||
| 358 | pc2d = new Stroker(pc2d, width, caps, join, miterlimit); | ||
| 359 | if (dashes != null) { | ||
| 360 | pc2d = new Dasher(pc2d, dashes, dashphase); | ||
| 361 | } | ||
| 362 | pc2d = TransformingPathConsumer2D.inverseDeltaTransformConsumer(pc2d, strokerat); | ||
| 363 | pathTo(pi, pc2d); | ||
| 364 | } | ||
| 365 | |||
| 366 | private static boolean nearZero(double num, int nulps) { | ||
| 367 | return Math.abs(num) < nulps * Math.ulp(num); | ||
| 368 | } | ||
| 369 | |||
| 370 | private static class NormalizingPathIterator implements PathIterator { | ||
| 371 | |||
| 372 | private final PathIterator src; | ||
| 373 | |||
| 374 | // the adjustment applied to the current position. | ||
| 375 | private float curx_adjust, cury_adjust; | ||
| 376 | // the adjustment applied to the last moveTo position. | ||
| 377 | private float movx_adjust, movy_adjust; | ||
| 378 | |||
| 379 | // constants used in normalization computations | ||
| 380 | private final float lval, rval; | ||
| 381 | |||
| 382 | NormalizingPathIterator(PathIterator src, NormMode mode) { | ||
| 383 | this.src = src; | ||
| 384 | switch (mode) { | ||
| 385 | case ON_NO_AA: | ||
| 386 | // round to nearest (0.25, 0.25) pixel | ||
| 387 | lval = rval = 0.25f; | ||
| 388 | break; | ||
| 389 | case ON_WITH_AA: | ||
| 390 | // round to nearest pixel center | ||
| 391 | lval = 0f; | ||
| 392 | rval = 0.5f; | ||
| 393 | break; | ||
| 394 | case OFF: | ||
| 395 | throw new InternalError("A NormalizingPathIterator should " + | ||
| 396 | "not be created if no normalization is being done"); | ||
| 397 | default: | ||
| 398 | throw new InternalError("Unrecognized normalization mode"); | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | public int currentSegment(float[] coords) { | ||
| 403 | int type = src.currentSegment(coords); | ||
| 404 | |||
| 405 | int lastCoord; | ||
| 406 | switch(type) { | ||
| 407 | case PathIterator.SEG_CUBICTO: | ||
| 408 | lastCoord = 4; | ||
| 409 | break; | ||
| 410 | case PathIterator.SEG_QUADTO: | ||
| 411 | lastCoord = 2; | ||
| 412 | break; | ||
| 413 | case PathIterator.SEG_LINETO: | ||
| 414 | case PathIterator.SEG_MOVETO: | ||
| 415 | lastCoord = 0; | ||
| 416 | break; | ||
| 417 | case PathIterator.SEG_CLOSE: | ||
| 418 | // we don't want to deal with this case later. We just exit now | ||
| 419 | curx_adjust = movx_adjust; | ||
| 420 | cury_adjust = movy_adjust; | ||
| 421 | return type; | ||
| 422 | default: | ||
| 423 | throw new InternalError("Unrecognized curve type"); | ||
| 424 | } | ||
| 425 | |||
| 426 | // normalize endpoint | ||
| 427 | float x_adjust = (float)Math.floor(coords[lastCoord] + lval) + | ||
| 428 | rval - coords[lastCoord]; | ||
| 429 | float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) + | ||
| 430 | rval - coords[lastCoord + 1]; | ||
| 431 | |||
| 432 | coords[lastCoord ] += x_adjust; | ||
| 433 | coords[lastCoord + 1] += y_adjust; | ||
| 434 | |||
| 435 | // now that the end points are done, normalize the control points | ||
| 436 | switch(type) { | ||
| 437 | case PathIterator.SEG_CUBICTO: | ||
| 438 | coords[0] += curx_adjust; | ||
| 439 | coords[1] += cury_adjust; | ||
| 440 | coords[2] += x_adjust; | ||
| 441 | coords[3] += y_adjust; | ||
| 442 | break; | ||
| 443 | case PathIterator.SEG_QUADTO: | ||
| 444 | coords[0] += (curx_adjust + x_adjust) / 2; | ||
| 445 | coords[1] += (cury_adjust + y_adjust) / 2; | ||
| 446 | break; | ||
| 447 | case PathIterator.SEG_LINETO: | ||
| 448 | break; | ||
| 449 | case PathIterator.SEG_MOVETO: | ||
| 450 | movx_adjust = x_adjust; | ||
| 451 | movy_adjust = y_adjust; | ||
| 452 | break; | ||
| 453 | case PathIterator.SEG_CLOSE: | ||
| 454 | throw new InternalError("This should be handled earlier."); | ||
| 455 | } | ||
| 456 | curx_adjust = x_adjust; | ||
| 457 | cury_adjust = y_adjust; | ||
| 458 | return type; | ||
| 459 | } | ||
| 460 | |||
| 461 | public int currentSegment(double[] coords) { | ||
| 462 | float[] tmp = new float[6]; | ||
| 463 | int type = this.currentSegment(tmp); | ||
| 464 | for (int i = 0; i < 6; i++) { | ||
| 465 | coords[i] = (float) tmp[i]; | ||
| 466 | } | ||
| 467 | return type; | ||
| 468 | } | ||
| 469 | |||
| 470 | public int getWindingRule() { | ||
| 471 | return src.getWindingRule(); | ||
| 472 | } | ||
| 473 | |||
| 474 | public boolean isDone() { | ||
| 475 | return src.isDone(); | ||
| 476 | } | ||
| 477 | |||
| 478 | public void next() { | ||
| 479 | src.next(); | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | static void pathTo(PathIterator pi, PathConsumer2D pc2d) { | ||
| 484 | RenderingEngine.feedConsumer(pi, pc2d); | ||
| 485 | pc2d.pathDone(); | ||
| 486 | } | ||
| 487 | |||
| 488 | /** | ||
| 489 | * Construct an antialiased tile generator for the given shape with | ||
| 490 | * the given rendering attributes and store the bounds of the tile | ||
| 491 | * iteration in the bbox parameter. | ||
| 492 | * The {@code at} parameter specifies a transform that should affect | ||
| 493 | * both the shape and the {@code BasicStroke} attributes. | ||
| 494 | * The {@code clip} parameter specifies the current clip in effect | ||
| 495 | * in device coordinates and can be used to prune the data for the | ||
| 496 | * operation, but the renderer is not required to perform any | ||
| 497 | * clipping. | ||
| 498 | * If the {@code BasicStroke} parameter is null then the shape | ||
| 499 | * should be filled as is, otherwise the attributes of the | ||
| 500 | * {@code BasicStroke} should be used to specify a draw operation. | ||
| 501 | * The {@code thin} parameter indicates whether or not the | ||
| 502 | * transformed {@code BasicStroke} represents coordinates smaller | ||
| 503 | * than the minimum resolution of the antialiasing rasterizer as | ||
| 504 | * specified by the {@code getMinimumAAPenWidth()} method. | ||
| 505 | * <p> | ||
| 506 | * Upon returning, this method will fill the {@code bbox} parameter | ||
| 507 | * with 4 values indicating the bounds of the iteration of the | ||
| 508 | * tile generator. | ||
| 509 | * The iteration order of the tiles will be as specified by the | ||
| 510 | * pseudo-code: | ||
| 511 | * <pre> | ||
| 512 | * for (y = bbox[1]; y < bbox[3]; y += tileheight) { | ||
| 513 | * for (x = bbox[0]; x < bbox[2]; x += tilewidth) { | ||
| 514 | * } | ||
| 515 | * } | ||
| 516 | * </pre> | ||
| 517 | * If there is no output to be rendered, this method may return | ||
| 518 | * null. | ||
| 519 | * | ||
| 520 | * @param s the shape to be rendered (fill or draw) | ||
| 521 | * @param at the transform to be applied to the shape and the | ||
| 522 | * stroke attributes | ||
| 523 | * @param clip the current clip in effect in device coordinates | ||
| 524 | * @param bs if non-null, a {@code BasicStroke} whose attributes | ||
| 525 | * should be applied to this operation | ||
| 526 | * @param thin true if the transformed stroke attributes are smaller | ||
| 527 | * than the minimum dropout pen width | ||
| 528 | * @param normalize true if the {@code VALUE_STROKE_NORMALIZE} | ||
| 529 | * {@code RenderingHint} is in effect | ||
| 530 | * @param bbox returns the bounds of the iteration | ||
| 531 | * @return the {@code AATileGenerator} instance to be consulted | ||
| 532 | * for tile coverages, or null if there is no output to render | ||
| 533 | * @since 1.7 | ||
| 534 | */ | ||
| 535 | public AATileGenerator getAATileGenerator(Shape s, | ||
| 536 | AffineTransform at, | ||
| 537 | Region clip, | ||
| 538 | BasicStroke bs, | ||
| 539 | boolean thin, | ||
| 540 | boolean normalize, | ||
| 541 | int bbox[]) | ||
| 542 | { | ||
| 543 | Renderer r; | ||
| 544 | NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF; | ||
| 545 | if (bs == null) { | ||
| 546 | PathIterator pi; | ||
| 547 | if (normalize) { | ||
| 548 | pi = new NormalizingPathIterator(s.getPathIterator(at), norm); | ||
| 549 | } else { | ||
| 550 | pi = s.getPathIterator(at); | ||
| 551 | } | ||
| 552 | r = new Renderer(3, 3, | ||
| 553 | clip.getLoX(), clip.getLoY(), | ||
| 554 | clip.getWidth(), clip.getHeight(), | ||
| 555 | pi.getWindingRule()); | ||
| 556 | pathTo(pi, r); | ||
| 557 | } else { | ||
| 558 | r = new Renderer(3, 3, | ||
| 559 | clip.getLoX(), clip.getLoY(), | ||
| 560 | clip.getWidth(), clip.getHeight(), | ||
| 561 | PathIterator.WIND_NON_ZERO); | ||
| 562 | strokeTo(s, at, bs, thin, norm, true, r); | ||
| 563 | } | ||
| 564 | r.endRendering(); | ||
| 565 | PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA); | ||
| 566 | ptg.getBbox(bbox); | ||
| 567 | return ptg; | ||
| 568 | } | ||
| 569 | |||
| 570 | public AATileGenerator getAATileGenerator(double x, double y, | ||
| 571 | double dx1, double dy1, | ||
| 572 | double dx2, double dy2, | ||
| 573 | double lw1, double lw2, | ||
| 574 | Region clip, | ||
| 575 | int bbox[]) | ||
| 576 | { | ||
| 577 | // REMIND: Deal with large coordinates! | ||
| 578 | double ldx1, ldy1, ldx2, ldy2; | ||
| 579 | boolean innerpgram = (lw1 > 0 && lw2 > 0); | ||
| 580 | |||
| 581 | if (innerpgram) { | ||
| 582 | ldx1 = dx1 * lw1; | ||
| 583 | ldy1 = dy1 * lw1; | ||
| 584 | ldx2 = dx2 * lw2; | ||
| 585 | ldy2 = dy2 * lw2; | ||
| 586 | x -= (ldx1 + ldx2) / 2.0; | ||
| 587 | y -= (ldy1 + ldy2) / 2.0; | ||
| 588 | dx1 += ldx1; | ||
| 589 | dy1 += ldy1; | ||
| 590 | dx2 += ldx2; | ||
| 591 | dy2 += ldy2; | ||
| 592 | if (lw1 > 1 && lw2 > 1) { | ||
| 593 | // Inner parallelogram was entirely consumed by stroke... | ||
| 594 | innerpgram = false; | ||
| 595 | } | ||
| 596 | } else { | ||
| 597 | ldx1 = ldy1 = ldx2 = ldy2 = 0; | ||
| 598 | } | ||
| 599 | |||
| 600 | Renderer r = new Renderer(3, 3, | ||
| 601 | clip.getLoX(), clip.getLoY(), | ||
| 602 | clip.getWidth(), clip.getHeight(), | ||
| 603 | PathIterator.WIND_EVEN_ODD); | ||
| 604 | |||
| 605 | r.moveTo((float) x, (float) y); | ||
| 606 | r.lineTo((float) (x+dx1), (float) (y+dy1)); | ||
| 607 | r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2)); | ||
| 608 | r.lineTo((float) (x+dx2), (float) (y+dy2)); | ||
| 609 | r.closePath(); | ||
| 610 | |||
| 611 | if (innerpgram) { | ||
| 612 | x += ldx1 + ldx2; | ||
| 613 | y += ldy1 + ldy2; | ||
| 614 | dx1 -= 2.0 * ldx1; | ||
| 615 | dy1 -= 2.0 * ldy1; | ||
| 616 | dx2 -= 2.0 * ldx2; | ||
| 617 | dy2 -= 2.0 * ldy2; | ||
| 618 | r.moveTo((float) x, (float) y); | ||
| 619 | r.lineTo((float) (x+dx1), (float) (y+dy1)); | ||
| 620 | r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2)); | ||
| 621 | r.lineTo((float) (x+dx2), (float) (y+dy2)); | ||
| 622 | r.closePath(); | ||
| 623 | } | ||
| 624 | |||
| 625 | r.pathDone(); | ||
| 626 | |||
| 627 | r.endRendering(); | ||
| 628 | PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA); | ||
| 629 | ptg.getBbox(bbox); | ||
| 630 | return ptg; | ||
| 631 | } | ||
| 632 | |||
| 633 | /** | ||
| 634 | * Returns the minimum pen width that the antialiasing rasterizer | ||
| 635 | * can represent without dropouts occuring. | ||
| 636 | * @since 1.7 | ||
| 637 | */ | ||
| 638 | public float getMinimumAAPenSize() { | ||
| 639 | return 0.5f; | ||
| 640 | } | ||
| 641 | |||
| 642 | static { | ||
| 643 | if (PathIterator.WIND_NON_ZERO != Renderer.WIND_NON_ZERO || | ||
| 644 | PathIterator.WIND_EVEN_ODD != Renderer.WIND_EVEN_ODD || | ||
| 645 | BasicStroke.JOIN_MITER != Stroker.JOIN_MITER || | ||
| 646 | BasicStroke.JOIN_ROUND != Stroker.JOIN_ROUND || | ||
| 647 | BasicStroke.JOIN_BEVEL != Stroker.JOIN_BEVEL || | ||
| 648 | BasicStroke.CAP_BUTT != Stroker.CAP_BUTT || | ||
| 649 | BasicStroke.CAP_ROUND != Stroker.CAP_ROUND || | ||
| 650 | BasicStroke.CAP_SQUARE != Stroker.CAP_SQUARE) | ||
| 651 | { | ||
| 652 | throw new InternalError("mismatched renderer constants"); | ||
| 653 | } | ||
| 654 | } | ||
| 655 | } | ||
| 656 | |||