History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/java2d/pisces/PiscesRenderingEngine.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/java2d/pisces/PiscesRenderingEngine.java+656−0
@@ -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+