Mercurial > hg > release > icedtea6-1.10
view patches/openjdk/6766342-AA-simple-shape-performance.patch @ 2578:96394d394527
Add security patches for 2012/06/12.
2012-06-07 Andrew John Hughes <ahughes@redhat.com>
* Makefile.am:
(ICEDTEA_PATCHES): Add security patches. Make more
patches HotSpot-build specific.
* patches/ecj/override.patch:
Add additional cases from 7143872.
* patches/arm.patch: Moved to HotSpot-specific versions.
* patches/arch.patch,
* patches/freetypeversion.patch,
* patches/gcc-suffix.patch:
Fix to work with no fuzz.
* patches/hotspot/hs20/arm.patch,
* patches/hotspot/hs20/gcc-stack-markings.patch,
* patches/hotspot/hs20/numa_on_early_glibc.patch,
* patches/hotspot/hs20/sparc-trapsfix.patch,
* patches/hotspot/hs20/version-hotspot.patch:
Split to work with hs20 with no fuzz.
* patches/hotspot/original/arm.patch,
* patches/hotspot/original/gcc-stack-markings.patch,
* patches/hotspot/original/numa_on_early_glibc.patch,
* patches/hotspot/original/sparc-trapsfix.patch,
* patches/hotspot/original/version-hotspot.patch:
Likewise for hs19 (original).
* patches/jaxp-serial-version-uid.patch,
* patches/libraries.patch,
* patches/nio2.patch,
* patches/no-static-linking.patch,
* patches/openjdk/6693253-security_warning.patch,
* patches/openjdk/6766342-AA-simple-shape-performance.patch,
* patches/openjdk/6797139-jbutton_truncation.patch,
* patches/openjdk/6851973-kerberos.patch,
* patches/openjdk/7102369-7094468-rmiregistry.patch:
Fixed to work with no fuzz.
* patches/openjdk/hs20/7034464-hugepage.patch,
* patches/openjdk/hs20/7103224-glibc_name_collision.patch,
Fixed to work with hs20 and no fuzz.
* patches/openjdk/mutter.patch:
Fixed to work with no fuzz.
* patches/openjdk/original/7034464-hugepage.patch,
* patches/openjdk/original/7103224-glibc_name_collision.patch,
Fixed to work with hs19 (original) and no fuzz.
* patches/openjdk/remove-mimpure-option-to-gcc.patch:
Fixed to work with no fuzz.
* patches/security/20120612/7079902.patch,
* patches/security/20120612/7143606.patch,
* patches/security/20120612/7143614.patch,
* patches/security/20120612/7143617.patch,
* patches/security/20120612/7143851.patch,
* patches/security/20120612/7143872.patch,
* patches/security/20120612/7145239.patch,
* patches/security/20120612/7157609.patch,
* patches/security/20120612/7160677.patch,
* patches/security/20120612/7160757.patch,
* patches/security/20120612/hs20/7110720.patch,
* patches/security/20120612/hs20/7152811.patch,
* patches/security/20120612/original/7110720.patch,
* patches/security/20120612/original/7152811.patch,
Security patches for 2012/06/12.
* NEWS: Updated.
author | Andrew John Hughes <ahughes@redhat.com> |
---|---|
date | Fri, 08 Jun 2012 14:23:28 +0100 |
parents | 514ec565d646 |
children |
line wrap: on
line source
diff -Nru openjdk.orig/jdk/make/sun/awt/mapfile-vers openjdk/jdk/make/sun/awt/mapfile-vers --- openjdk.orig/jdk/make/sun/awt/mapfile-vers 2012-06-08 12:04:25.289301993 +0100 +++ openjdk/jdk/make/sun/awt/mapfile-vers 2012-06-08 12:08:06.220870613 +0100 @@ -119,6 +119,8 @@ Java_sun_java2d_loops_GraphicsPrimitiveMgr_registerNativeLoops; Java_sun_java2d_loops_MaskBlit_MaskBlit; Java_sun_java2d_loops_MaskFill_MaskFill; + Java_sun_java2d_loops_MaskFill_FillAAPgram; + Java_sun_java2d_loops_MaskFill_DrawAAPgram; Java_sun_java2d_loops_TransformHelper_Transform; Java_sun_java2d_pipe_Region_initIDs; Java_sun_java2d_pipe_SpanClipRenderer_initIDs; diff -Nru openjdk.orig/jdk/make/sun/awt/mapfile-vers-linux openjdk/jdk/make/sun/awt/mapfile-vers-linux --- openjdk.orig/jdk/make/sun/awt/mapfile-vers-linux 2012-06-08 12:04:24.353286879 +0100 +++ openjdk/jdk/make/sun/awt/mapfile-vers-linux 2012-06-08 12:08:06.220870613 +0100 @@ -115,6 +115,8 @@ Java_sun_java2d_loops_GraphicsPrimitiveMgr_registerNativeLoops; Java_sun_java2d_loops_MaskBlit_MaskBlit; Java_sun_java2d_loops_MaskFill_MaskFill; + Java_sun_java2d_loops_MaskFill_FillAAPgram; + Java_sun_java2d_loops_MaskFill_DrawAAPgram; Java_sun_java2d_pipe_BufferedRenderPipe_fillSpans; Java_sun_java2d_pipe_RenderBuffer_copyFromArray; Java_sun_java2d_pipe_SpanClipRenderer_initIDs; diff -Nru openjdk.orig/jdk/src/share/classes/sun/dc/DuctusRenderingEngine.java openjdk/jdk/src/share/classes/sun/dc/DuctusRenderingEngine.java --- openjdk.orig/jdk/src/share/classes/sun/dc/DuctusRenderingEngine.java 2011-02-28 16:06:31.000000000 +0000 +++ openjdk/jdk/src/share/classes/sun/dc/DuctusRenderingEngine.java 2012-06-08 12:08:06.220870613 +0100 @@ -635,6 +635,88 @@ return r; } + /** + * {@inheritDoc} + */ + @Override + public AATileGenerator getAATileGenerator(double x, double y, + double dx1, double dy1, + double dx2, double dy2, + double lw1, double lw2, + Region clip, + int bbox[]) + { + // REMIND: Deal with large coordinates! + double ldx1, ldy1, ldx2, ldy2; + boolean innerpgram = (lw1 > 0 && lw2 > 0); + + if (innerpgram) { + ldx1 = dx1 * lw1; + ldy1 = dy1 * lw1; + ldx2 = dx2 * lw2; + ldy2 = dy2 * lw2; + x -= (ldx1 + ldx2) / 2.0; + y -= (ldy1 + ldy2) / 2.0; + dx1 += ldx1; + dy1 += ldy1; + dx2 += ldx2; + dy2 += ldy2; + if (lw1 > 1 && lw2 > 1) { + // Inner parallelogram was entirely consumed by stroke... + innerpgram = false; + } + } else { + ldx1 = ldy1 = ldx2 = ldy2 = 0; + } + + Rasterizer r = getRasterizer(); + + r.setUsage(Rasterizer.EOFILL); + + r.beginPath(); + r.beginSubpath((float) x, (float) y); + r.appendLine((float) (x+dx1), (float) (y+dy1)); + r.appendLine((float) (x+dx1+dx2), (float) (y+dy1+dy2)); + r.appendLine((float) (x+dx2), (float) (y+dy2)); + r.closedSubpath(); + if (innerpgram) { + x += ldx1 + ldx2; + y += ldy1 + ldy2; + dx1 -= 2.0 * ldx1; + dy1 -= 2.0 * ldy1; + dx2 -= 2.0 * ldx2; + dy2 -= 2.0 * ldy2; + r.beginSubpath((float) x, (float) y); + r.appendLine((float) (x+dx1), (float) (y+dy1)); + r.appendLine((float) (x+dx1+dx2), (float) (y+dy1+dy2)); + r.appendLine((float) (x+dx2), (float) (y+dy2)); + r.closedSubpath(); + } + + try { + r.endPath(); + r.getAlphaBox(bbox); + clip.clipBoxToBounds(bbox); + if (bbox[0] >= bbox[2] || bbox[1] >= bbox[3]) { + dropRasterizer(r); + return null; + } + r.setOutputArea(bbox[0], bbox[1], + bbox[2] - bbox[0], + bbox[3] - bbox[1]); + } catch (PRException e) { + /* + * This exeption is thrown from the native part of the Ductus + * (only in case of a debug build) to indicate that some + * segments of the path have very large coordinates. + * See 4485298 for more info. + */ + System.err.println("DuctusRenderingEngine.getAATileGenerator: "+e); + } + + return r; + } + private void feedConsumer(PathConsumer consumer, PathIterator pi) { try { consumer.beginPath(); diff -Nru openjdk.orig/jdk/src/share/classes/sun/java2d/loops/CompositeType.java openjdk/jdk/src/share/classes/sun/java2d/loops/CompositeType.java --- openjdk.orig/jdk/src/share/classes/sun/java2d/loops/CompositeType.java 2011-02-28 16:06:33.000000000 +0000 +++ openjdk/jdk/src/share/classes/sun/java2d/loops/CompositeType.java 2012-06-08 12:08:06.228870743 +0100 @@ -27,6 +27,7 @@ import java.awt.image.BufferedImage; import java.awt.AlphaComposite; +import java.util.HashMap; /** * A CompositeType object provides a chained description of a type of @@ -51,6 +52,11 @@ * the indicated algorithm if all of the more specific searches fail. */ public final class CompositeType { + + private static int unusedUID = 1; + private static final HashMap<String,Integer> compositeUIDMap = + new HashMap<String,Integer>(100); + /* * CONSTANTS USED BY ALL PRIMITIVES TO DESCRIBE THE COMPOSITING * ALGORITHMS THEY CAN PERFORM @@ -153,6 +159,22 @@ SrcOverNoEa = SrcOver.deriveSubType(DESC_SRC_OVER_NO_EA); /* + * A special CompositeType for the case where we are filling in + * SrcOverNoEa mode with an opaque color. In that case then the + * best loop for us to use would be a SrcNoEa loop, but what if + * there is no such loop? In that case then we would end up + * backing off to a Src loop (which should still be fine) or an + * AnyAlpha loop which would be slower than a SrcOver loop in + * most cases. + * The fix is to use the following chain which looks for loops + * in the following order: + * SrcNoEa, Src, SrcOverNoEa, SrcOver, AnyAlpha + */ + public static final CompositeType + OpaqueSrcOverNoEa = SrcOverNoEa.deriveSubType(DESC_SRC) + .deriveSubType(DESC_SRC_NO_EA); + + /* * END OF CompositeType OBJECTS FOR THE VARIOUS CONSTANTS */ @@ -210,7 +232,6 @@ } } - private static int unusedUID = 1; private int uniqueID; private String desc; private CompositeType next; @@ -218,14 +239,20 @@ private CompositeType(CompositeType parent, String desc) { next = parent; this.desc = desc; - this.uniqueID = makeUniqueID(); + this.uniqueID = makeUniqueID(desc); } - private synchronized static final int makeUniqueID() { - if (unusedUID > 255) { - throw new InternalError("composite type id overflow"); + public synchronized static final int makeUniqueID(String desc) { + Integer i = compositeUIDMap.get(desc); + + if (i == null) { + if (unusedUID > 255) { + throw new InternalError("composite type id overflow"); + } + i = unusedUID++; + compositeUIDMap.put(desc, i); } - return unusedUID++; + return i; } public int getUniqueID() { diff -Nru openjdk.orig/jdk/src/share/classes/sun/java2d/loops/MaskFill.java openjdk/jdk/src/share/classes/sun/java2d/loops/MaskFill.java --- openjdk.orig/jdk/src/share/classes/sun/java2d/loops/MaskFill.java 2011-02-28 16:06:33.000000000 +0000 +++ openjdk/jdk/src/share/classes/sun/java2d/loops/MaskFill.java 2012-06-08 12:08:06.228870743 +0100 @@ -50,6 +50,10 @@ public class MaskFill extends GraphicsPrimitive { public static final String methodSignature = "MaskFill(...)".toString(); + public static final String fillPgramSignature = + "FillAAPgram(...)".toString(); + public static final String drawPgramSignature = + "DrawAAPgram(...)".toString(); public static final int primTypeID = makePrimTypeID(); @@ -92,6 +96,14 @@ return fill; } + protected MaskFill(String alternateSignature, + SurfaceType srctype, + CompositeType comptype, + SurfaceType dsttype) + { + super(alternateSignature, primTypeID, srctype, comptype, dsttype); + } + protected MaskFill(SurfaceType srctype, CompositeType comptype, SurfaceType dsttype) @@ -115,6 +127,23 @@ int x, int y, int w, int h, byte[] mask, int maskoff, int maskscan); + public native void FillAAPgram(SunGraphics2D sg2d, SurfaceData sData, + Composite comp, + double x, double y, + double dx1, double dy1, + double dx2, double dy2); + + public native void DrawAAPgram(SunGraphics2D sg2d, SurfaceData sData, + Composite comp, + double x, double y, + double dx1, double dy1, + double dx2, double dy2, + double lw1, double lw2); + + public boolean canDoParallelograms() { + return (getNativePrim() != 0); + } + static { GraphicsPrimitiveMgr.registerGeneral(new MaskFill(null, null, null)); } @@ -182,12 +211,22 @@ private static class TraceMaskFill extends MaskFill { MaskFill target; + MaskFill fillPgramTarget; + MaskFill drawPgramTarget; public TraceMaskFill(MaskFill target) { super(target.getSourceType(), target.getCompositeType(), target.getDestType()); this.target = target; + this.fillPgramTarget = new MaskFill(fillPgramSignature, + target.getSourceType(), + target.getCompositeType(), + target.getDestType()); + this.drawPgramTarget = new MaskFill(drawPgramSignature, + target.getSourceType(), + target.getCompositeType(), + target.getDestType()); } public GraphicsPrimitive traceWrap() { @@ -203,5 +242,32 @@ target.MaskFill(sg2d, sData, comp, x, y, w, h, mask, maskoff, maskscan); } + + public void FillAAPgram(SunGraphics2D sg2d, SurfaceData sData, + Composite comp, + double x, double y, + double dx1, double dy1, + double dx2, double dy2) + { + tracePrimitive(fillPgramTarget); + target.FillAAPgram(sg2d, sData, comp, + x, y, dx1, dy1, dx2, dy2); + } + + public void DrawAAPgram(SunGraphics2D sg2d, SurfaceData sData, + Composite comp, + double x, double y, + double dx1, double dy1, + double dx2, double dy2, + double lw1, double lw2) + { + tracePrimitive(drawPgramTarget); + target.DrawAAPgram(sg2d, sData, comp, + x, y, dx1, dy1, dx2, dy2, lw1, lw2); + } + + public boolean canDoParallelograms() { + return target.canDoParallelograms(); + } } } diff -Nru openjdk.orig/jdk/src/share/classes/sun/java2d/pipe/AAShapePipe.java openjdk/jdk/src/share/classes/sun/java2d/pipe/AAShapePipe.java --- openjdk.orig/jdk/src/share/classes/sun/java2d/pipe/AAShapePipe.java 2011-02-28 16:06:33.000000000 +0000 +++ openjdk/jdk/src/share/classes/sun/java2d/pipe/AAShapePipe.java 2012-06-08 12:08:06.228870743 +0100 @@ -28,6 +28,7 @@ import java.awt.BasicStroke; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.geom.Rectangle2D; import java.awt.geom.PathIterator; import sun.awt.SunHints; import sun.java2d.SunGraphics2D; @@ -39,7 +40,9 @@ * This class sets up the Generator and computes the alpha tiles * and then passes them on to a CompositePipe object for painting. */ -public class AAShapePipe implements ShapeDrawPipe { +public class AAShapePipe + implements ShapeDrawPipe, ParallelogramPipe +{ static RenderingEngine renderengine = RenderingEngine.getInstance(); CompositePipe outpipe; @@ -65,6 +68,59 @@ renderPath(sg, s, null); } + private static Rectangle2D computeBBox(double x, double y, + double dx1, double dy1, + double dx2, double dy2) + { + double lox, loy, hix, hiy; + lox = hix = x; + loy = hiy = y; + if (dx1 < 0) { lox += dx1; } else { hix += dx1; } + if (dy1 < 0) { loy += dy1; } else { hiy += dy1; } + if (dx2 < 0) { lox += dx2; } else { hix += dx2; } + if (dy2 < 0) { loy += dy2; } else { hiy += dy2; } + return new Rectangle2D.Double(lox, loy, hix-lox, hiy-loy); + } + + public void fillParallelogram(SunGraphics2D sg, + double x, double y, + double dx1, double dy1, + double dx2, double dy2) + { + Region clip = sg.getCompClip(); + int abox[] = new int[4]; + AATileGenerator aatg = + renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, 0, 0, + clip, abox); + if (aatg == null) { + // Nothing to render + return; + } + + renderTiles(sg, computeBBox(x, y, dx1, dy1, dx2, dy2), aatg, abox); + } + + public void drawParallelogram(SunGraphics2D sg, + double x, double y, + double dx1, double dy1, + double dx2, double dy2, + double lw1, double lw2) + { + Region clip = sg.getCompClip(); + int abox[] = new int[4]; + AATileGenerator aatg = + renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, 0, 0, + clip, abox); + if (aatg == null) { + // Nothing to render + return; + } + + // Note that bbox is of the original shape, not the wide path. + // This is appropriate for handing to Paint methods... + renderTiles(sg, computeBBox(x, y, dx1, dy1, dx2, dy2), aatg, abox); + } + private static byte[] theTile; public synchronized static byte[] getAlphaTile(int len) { @@ -85,8 +141,6 @@ boolean adjust = (bs != null && sg.strokeHint != SunHints.INTVAL_STROKE_PURE); boolean thin = (sg.strokeState <= sg.STROKE_THINDASHED); - Object context = null; - byte alpha[] = null; Region clip = sg.getCompClip(); int abox[] = new int[4]; @@ -98,6 +152,14 @@ return; } + renderTiles(sg, s, aatg, abox); + } + + public void renderTiles(SunGraphics2D sg, Shape s, + AATileGenerator aatg, int abox[]) + { + Object context = null; + byte alpha[] = null; try { context = outpipe.startSequence(sg, s, new Rectangle(abox[0], abox[1], diff -Nru openjdk.orig/jdk/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java openjdk/jdk/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java --- openjdk.orig/jdk/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java 2011-02-28 16:06:33.000000000 +0000 +++ openjdk/jdk/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java 2012-06-08 12:08:06.228870743 +0100 @@ -34,7 +34,7 @@ * into a destination that supports direct alpha compositing of a solid * color, according to one of the rules in the AlphaComposite class. */ -public class AlphaColorPipe implements CompositePipe { +public class AlphaColorPipe implements CompositePipe, ParallelogramPipe { public AlphaColorPipe() { } @@ -64,4 +64,23 @@ public void endSequence(Object context) { return; } + + public void fillParallelogram(SunGraphics2D sg, + double x, double y, + double dx1, double dy1, + double dx2, double dy2) + { + sg.alphafill.FillAAPgram(sg, sg.getSurfaceData(), sg.composite, + x, y, dx1, dy1, dx2, dy2); + } + + public void drawParallelogram(SunGraphics2D sg, + double x, double y, + double dx1, double dy1, + double dx2, double dy2, + double lw1, double lw2) + { + sg.alphafill.DrawAAPgram(sg, sg.getSurfaceData(), sg.composite, + x, y, dx1, dy1, dx2, dy2, lw1, lw2); + } } diff -Nru openjdk.orig/jdk/src/share/classes/sun/java2d/pipe/RenderingEngine.java openjdk/jdk/src/share/classes/sun/java2d/pipe/RenderingEngine.java --- openjdk.orig/jdk/src/share/classes/sun/java2d/pipe/RenderingEngine.java 2011-02-28 16:06:33.000000000 +0000 +++ openjdk/jdk/src/share/classes/sun/java2d/pipe/RenderingEngine.java 2012-06-08 12:08:06.228870743 +0100 @@ -264,6 +264,72 @@ int bbox[]); /** + * Construct an antialiased tile generator for the given parallelogram + * store the bounds of the tile iteration in the bbox parameter. + * The parallelogram is specified as a starting point and 2 delta + * vectors that indicate the slopes of the 2 pairs of sides of the + * parallelogram. + * The 4 corners of the parallelogram are defined by the 4 points: + * <ul> + * <li> {@code x}, {@code y} + * <li> {@code x+dx1}, {@code y+dy1} + * <li> {@code x+dx1+dx2}, {@code y+dy1+dy2} + * <li> {@code x+dx2}, {@code y+dy2} + * </ul> + * The {@code lw1} and {@code lw2} parameters provide a specification + * for an optionally stroked parallelogram if they are positive numbers. + * The {@code lw1} parameter is the ratio of the length of the {@code dx1}, + * {@code dx2} delta vector to half of the line width in that same + * direction. + * The {@code lw2} parameter provides the same ratio for the other delta + * vector. + * If {@code lw1} and {@code lw2} are both greater than zero, then + * the parallelogram figure is doubled by both expanding and contracting + * each delta vector by its corresponding {@code lw} value. + * If either (@code lw1) or {@code lw2} are also greater than 1, then + * the inner (contracted) parallelogram disappears and the figure is + * simply a single expanded parallelogram. + * The {@code clip} parameter specifies the current clip in effect + * in device coordinates and can be used to prune the data for the + * operation, but the renderer is not required to perform any + * clipping. + * <p> + * Upon returning, this method will fill the {@code bbox} parameter + * with 4 values indicating the bounds of the iteration of the + * tile generator. + * The iteration order of the tiles will be as specified by the + * pseudo-code: + * <pre> + * for (y = bbox[1]; y < bbox[3]; y += tileheight) { + * for (x = bbox[0]; x < bbox[2]; x += tilewidth) { + * } + * } + * </pre> + * If there is no output to be rendered, this method may return + * null. + * + * @param x the X coordinate of the first corner of the parallelogram + * @param y the Y coordinate of the first corner of the parallelogram + * @param dx1 the X coordinate delta of the first leg of the parallelogram + * @param dy1 the Y coordinate delta of the first leg of the parallelogram + * @param dx2 the X coordinate delta of the second leg of the parallelogram + * @param dy2 the Y coordinate delta of the second leg of the parallelogram + * @param lw1 the line width ratio for the first leg of the parallelogram + * @param lw2 the line width ratio for the second leg of the parallelogram + * @param clip the current clip in effect in device coordinates + * @param bbox returns the bounds of the iteration + * @return the {@code AATileGenerator} instance to be consulted + * for tile coverages, or null if there is no output to render + * @since 1.7 + */ + public abstract AATileGenerator getAATileGenerator(double x, double y, + double dx1, double dy1, + double dx2, double dy2, + double lw1, double lw2, + Region clip, + int bbox[]); + + /** * Returns the minimum pen width that the antialiasing rasterizer * can represent without dropouts occuring. * @since 1.7 @@ -376,5 +442,24 @@ bs, thin, normalize, bbox); } + public AATileGenerator getAATileGenerator(double x, double y, + double dx1, double dy1, + double dx2, double dy2, + double lw1, double lw2, + Region clip, + int bbox[]) + { + System.out.println(name+".getAATileGenerator("+ + x+", "+y+", "+ + dx1+", "+dy1+", "+ + dx2+", "+dy2+", "+ + lw1+", "+lw2+", "+ + clip+")"); + return target.getAATileGenerator(x, y, + dx1, dy1, + dx2, dy2, + lw1, lw2, + clip, bbox); + } } } diff -Nru openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java openjdk/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java --- openjdk.orig/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java 2012-06-08 12:04:24.965296763 +0100 +++ openjdk/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java 2012-06-08 12:08:06.228870743 +0100 @@ -557,6 +557,69 @@ return ptg; } + public AATileGenerator getAATileGenerator(double x, double y, + double dx1, double dy1, + double dx2, double dy2, + double lw1, double lw2, + Region clip, + int bbox[]) + { + // REMIND: Deal with large coordinates! + double ldx1, ldy1, ldx2, ldy2; + boolean innerpgram = (lw1 > 0 && lw2 > 0); + + if (innerpgram) { + ldx1 = dx1 * lw1; + ldy1 = dy1 * lw1; + ldx2 = dx2 * lw2; + ldy2 = dy2 * lw2; + x -= (ldx1 + ldx2) / 2.0; + y -= (ldy1 + ldy2) / 2.0; + dx1 += ldx1; + dy1 += ldy1; + dx2 += ldx2; + dy2 += ldy2; + if (lw1 > 1 && lw2 > 1) { + // Inner parallelogram was entirely consumed by stroke... + innerpgram = false; + } + } else { + ldx1 = ldy1 = ldx2 = ldy2 = 0; + } + + Renderer r = new Renderer(3, 3, + clip.getLoX(), clip.getLoY(), + clip.getWidth(), clip.getHeight(), + PathIterator.WIND_EVEN_ODD); + + r.moveTo((float) x, (float) y); + r.lineTo((float) (x+dx1), (float) (y+dy1)); + r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2)); + r.lineTo((float) (x+dx2), (float) (y+dy2)); + r.closePath(); + + if (innerpgram) { + x += ldx1 + ldx2; + y += ldy1 + ldy2; + dx1 -= 2.0 * ldx1; + dy1 -= 2.0 * ldy1; + dx2 -= 2.0 * ldx2; + dy2 -= 2.0 * ldy2; + r.moveTo((float) x, (float) y); + r.lineTo((float) (x+dx1), (float) (y+dy1)); + r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2)); + r.lineTo((float) (x+dx2), (float) (y+dy2)); + r.closePath(); + } + + r.pathDone(); + + r.endRendering(); + PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA); + ptg.getBbox(bbox); + return ptg; + } + /** * Returns the minimum pen width that the antialiasing rasterizer * can represent without dropouts occuring. diff -Nru openjdk.orig/jdk/src/share/classes/sun/java2d/SurfaceData.java openjdk/jdk/src/share/classes/sun/java2d/SurfaceData.java --- openjdk.orig/jdk/src/share/classes/sun/java2d/SurfaceData.java 2012-06-08 12:04:25.293302058 +0100 +++ openjdk/jdk/src/share/classes/sun/java2d/SurfaceData.java 2012-06-08 12:08:06.228870743 +0100 @@ -367,16 +367,17 @@ public static final TextPipe aaTextRenderer; public static final TextPipe lcdTextRenderer; - protected static final CompositePipe colorPipe; + protected static final AlphaColorPipe colorPipe; protected static final PixelToShapeConverter colorViaShape; protected static final PixelToParallelogramConverter colorViaPgram; protected static final TextPipe colorText; protected static final CompositePipe clipColorPipe; protected static final TextPipe clipColorText; protected static final AAShapePipe AAColorShape; - protected static final PixelToShapeConverter AAColorViaShape; + protected static final PixelToParallelogramConverter AAColorViaShape; + protected static final PixelToParallelogramConverter AAColorViaPgram; protected static final AAShapePipe AAClipColorShape; - protected static final PixelToShapeConverter AAClipColorViaShape; + protected static final PixelToParallelogramConverter AAClipColorViaShape; protected static final CompositePipe paintPipe; protected static final SpanShapeRenderer paintShape; @@ -385,9 +386,9 @@ protected static final CompositePipe clipPaintPipe; protected static final TextPipe clipPaintText; protected static final AAShapePipe AAPaintShape; - protected static final PixelToShapeConverter AAPaintViaShape; + protected static final PixelToParallelogramConverter AAPaintViaShape; protected static final AAShapePipe AAClipPaintShape; - protected static final PixelToShapeConverter AAClipPaintViaShape; + protected static final PixelToParallelogramConverter AAClipPaintViaShape; protected static final CompositePipe compPipe; protected static final SpanShapeRenderer compShape; @@ -396,9 +397,9 @@ protected static final CompositePipe clipCompPipe; protected static final TextPipe clipCompText; protected static final AAShapePipe AACompShape; - protected static final PixelToShapeConverter AACompViaShape; + protected static final PixelToParallelogramConverter AACompViaShape; protected static final AAShapePipe AAClipCompShape; - protected static final PixelToShapeConverter AAClipCompViaShape; + protected static final PixelToParallelogramConverter AAClipCompViaShape; protected static final DrawImagePipe imagepipe; @@ -427,6 +428,22 @@ } } + private static PixelToParallelogramConverter + makeConverter(AAShapePipe renderer, + ParallelogramPipe pgrampipe) + { + return new PixelToParallelogramConverter(renderer, + pgrampipe, + 1.0/8.0, 0.499, + false); + } + + private static PixelToParallelogramConverter + makeConverter(AAShapePipe renderer) + { + return makeConverter(renderer, renderer); + } + static { colorPrimitives = new LoopPipe(); @@ -445,9 +462,10 @@ clipColorPipe = new SpanClipRenderer(colorPipe); clipColorText = new TextRenderer(clipColorPipe); AAColorShape = new AAShapePipe(colorPipe); - AAColorViaShape = new PixelToShapeConverter(AAColorShape); + AAColorViaShape = makeConverter(AAColorShape); + AAColorViaPgram = makeConverter(AAColorShape, colorPipe); AAClipColorShape = new AAShapePipe(clipColorPipe); - AAClipColorViaShape = new PixelToShapeConverter(AAClipColorShape); + AAClipColorViaShape = makeConverter(AAClipColorShape); paintPipe = new AlphaPaintPipe(); paintShape = new SpanShapeRenderer.Composite(paintPipe); @@ -456,9 +474,9 @@ clipPaintPipe = new SpanClipRenderer(paintPipe); clipPaintText = new TextRenderer(clipPaintPipe); AAPaintShape = new AAShapePipe(paintPipe); - AAPaintViaShape = new PixelToShapeConverter(AAPaintShape); + AAPaintViaShape = makeConverter(AAPaintShape); AAClipPaintShape = new AAShapePipe(clipPaintPipe); - AAClipPaintViaShape = new PixelToShapeConverter(AAClipPaintShape); + AAClipPaintViaShape = makeConverter(AAClipPaintShape); compPipe = new GeneralCompositePipe(); compShape = new SpanShapeRenderer.Composite(compPipe); @@ -467,9 +485,9 @@ clipCompPipe = new SpanClipRenderer(compPipe); clipCompText = new TextRenderer(clipCompPipe); AACompShape = new AAShapePipe(compPipe); - AACompViaShape = new PixelToShapeConverter(AACompShape); + AACompViaShape = makeConverter(AACompShape); AAClipCompShape = new AAShapePipe(clipCompPipe); - AAClipCompViaShape = new PixelToShapeConverter(AAClipCompShape); + AAClipCompViaShape = makeConverter(AAClipCompShape); imagepipe = new DrawImage(); } @@ -591,12 +609,12 @@ if (sg2d.clipState == sg2d.CLIP_SHAPE) { sg2d.drawpipe = AAClipCompViaShape; sg2d.fillpipe = AAClipCompViaShape; - sg2d.shapepipe = AAClipCompShape; + sg2d.shapepipe = AAClipCompViaShape; sg2d.textpipe = clipCompText; } else { sg2d.drawpipe = AACompViaShape; sg2d.fillpipe = AACompViaShape; - sg2d.shapepipe = AACompShape; + sg2d.shapepipe = AACompViaShape; sg2d.textpipe = compText; } } else { @@ -616,12 +634,16 @@ if (sg2d.clipState == sg2d.CLIP_SHAPE) { sg2d.drawpipe = AAClipColorViaShape; sg2d.fillpipe = AAClipColorViaShape; - sg2d.shapepipe = AAClipColorShape; + sg2d.shapepipe = AAClipColorViaShape; sg2d.textpipe = clipColorText; } else { - sg2d.drawpipe = AAColorViaShape; - sg2d.fillpipe = AAColorViaShape; - sg2d.shapepipe = AAColorShape; + PixelToParallelogramConverter converter = + (sg2d.alphafill.canDoParallelograms() + ? AAColorViaPgram + : AAColorViaShape); + sg2d.drawpipe = converter; + sg2d.fillpipe = converter; + sg2d.shapepipe = converter; if (sg2d.paintState > sg2d.PAINT_OPAQUECOLOR || sg2d.compositeState > sg2d.COMP_ISCOPY) { @@ -634,12 +656,12 @@ if (sg2d.clipState == sg2d.CLIP_SHAPE) { sg2d.drawpipe = AAClipPaintViaShape; sg2d.fillpipe = AAClipPaintViaShape; - sg2d.shapepipe = AAClipPaintShape; + sg2d.shapepipe = AAClipPaintViaShape; sg2d.textpipe = clipPaintText; } else { sg2d.drawpipe = AAPaintViaShape; sg2d.fillpipe = AAPaintViaShape; - sg2d.shapepipe = AAPaintShape; + sg2d.shapepipe = AAPaintViaShape; sg2d.textpipe = paintText; } } @@ -793,6 +815,18 @@ } } + private static CompositeType getFillCompositeType(SunGraphics2D sg2d) { + CompositeType compType = sg2d.imageComp; + if (sg2d.compositeState == sg2d.COMP_ISCOPY) { + if (compType == CompositeType.SrcOverNoEa) { + compType = CompositeType.OpaqueSrcOverNoEa; + } else { + compType = CompositeType.SrcNoEa; + } + } + return compType; + } + /** * Returns a MaskFill object that can be used on this destination * with the source (paint) and composite types determined by the given @@ -802,9 +836,10 @@ * surface) before returning a specific MaskFill object. */ protected MaskFill getMaskFill(SunGraphics2D sg2d) { - return MaskFill.getFromCache(getPaintSurfaceType(sg2d), - sg2d.imageComp, - getSurfaceType()); + SurfaceType src = getPaintSurfaceType(sg2d); + CompositeType comp = getFillCompositeType(sg2d); + SurfaceType dst = getSurfaceType(); + return MaskFill.getFromCache(src, comp, dst); } private static RenderCache loopcache = new RenderCache(30); @@ -816,9 +851,7 @@ */ public RenderLoops getRenderLoops(SunGraphics2D sg2d) { SurfaceType src = getPaintSurfaceType(sg2d); - CompositeType comp = (sg2d.compositeState == sg2d.COMP_ISCOPY - ? CompositeType.SrcNoEa - : sg2d.imageComp); + CompositeType comp = getFillCompositeType(sg2d); SurfaceType dst = sg2d.getSurfaceData().getSurfaceType(); Object o = loopcache.get(src, comp, dst); diff -Nru openjdk.orig/jdk/src/share/native/sun/java2d/loops/DrawParallelogram.c openjdk/jdk/src/share/native/sun/java2d/loops/DrawParallelogram.c --- openjdk.orig/jdk/src/share/native/sun/java2d/loops/DrawParallelogram.c 2012-06-08 12:04:25.297302124 +0100 +++ openjdk/jdk/src/share/native/sun/java2d/loops/DrawParallelogram.c 2012-06-08 12:08:06.232870807 +0100 @@ -26,14 +26,11 @@ #include "math.h" #include "GraphicsPrimitiveMgr.h" #include "LineUtils.h" -#include "LoopMacros.h" #include "Trace.h" +#include "ParallelogramUtils.h" -#include "sun_java2d_loops_FillParallelogram.h" #include "sun_java2d_loops_DrawParallelogram.h" -DECLARE_SOLID_DRAWLINE(AnyInt); - #define HANDLE_PGRAM_EDGE(X1, Y1, X2, Y2, \ pRasInfo, pixel, pPrim, pFunc, pCompInfo) \ do { \ @@ -46,28 +43,6 @@ ix1, iy1, ix2, iy2, JNI_TRUE); \ } while (0) -#define PGRAM_MIN_MAX(bmin, bmax, v0, dv1, dv2) \ - do { \ - double vmin, vmax; \ - if (dv1 < 0) { \ - vmin = v0+dv1; \ - vmax = v0; \ - } else { \ - vmin = v0; \ - vmax = v0+dv1; \ - } \ - if (dv2 < 0) { \ - vmin -= dv2; \ - } else { \ - vmax += dv2; \ - } \ - bmin = (jint) floor(vmin + 0.5); \ - bmax = (jint) floor(vmax + 0.5); \ - } while(0) - -#define PGRAM_INIT_X(starty, x, y, slope) \ - (DblToLong((x) + (slope) * ((starty)+0.5 - (y))) + LongOneHalf - 1) - typedef struct { jdouble x0; jdouble y0; @@ -136,20 +111,8 @@ * Sort parallelogram by y values, ensure that each delta vector * has a non-negative y delta. */ - if (dy1 < 0) { - x0 += dx1; y0 += dy1; - dx1 = -dx1; dy1 = -dy1; - } - if (dy2 < 0) { - x0 += dx2; y0 += dy2; - dx2 = -dx2; dy2 = -dy2; - } - /* Sort delta vectors so dxy1 is left of dxy2. */ - if (dx1 * dy2 > dx2 * dy1) { - double v = dx1; dx1 = dx2; dx2 = v; - v = dy1; dy1 = dy2; dy2 = v; - v = lw1; lw1 = lw2; lw2 = v; - } + SORT_PGRAM(x0, y0, dx1, dy1, dx2, dy2, + v = lw1; lw1 = lw2; lw2 = v;); // dx,dy for line width in the "1" and "2" directions. ldx1 = dx1 * lw1; @@ -161,7 +124,7 @@ ox0 = x0 - (ldx1 + ldx2) / 2.0; oy0 = y0 - (ldy1 + ldy2) / 2.0; - PGRAM_MIN_MAX(ix1, ix2, ox0, dx1+ldx1, dx2+ldx2); + PGRAM_MIN_MAX(ix1, ix2, ox0, dx1+ldx1, dx2+ldx2, JNI_FALSE); iy1 = (jint) floor(oy0 + 0.5); iy2 = (jint) floor(oy0 + dy1 + ldy1 + dy2 + ldy2 + 0.5); @@ -212,7 +175,7 @@ // Only need to generate 4 quads if the interior still // has a hole in it (i.e. if the line width ratios were // both less than 1.0) - if (lw1 < 1.0f && lw2 < 1.0f) { + if (lw1 < 1.0 && lw2 < 1.0) { // If the line widths are both less than a pixel wide // then we can use a drawline function instead for even // more performance. diff -Nru openjdk.orig/jdk/src/share/native/sun/java2d/loops/FillParallelogram.c openjdk/jdk/src/share/native/sun/java2d/loops/FillParallelogram.c --- openjdk.orig/jdk/src/share/native/sun/java2d/loops/FillParallelogram.c 2012-06-08 12:04:25.297302124 +0100 +++ openjdk/jdk/src/share/native/sun/java2d/loops/FillParallelogram.c 2012-06-08 12:08:06.232870807 +0100 @@ -25,31 +25,10 @@ #include "math.h" #include "GraphicsPrimitiveMgr.h" +#include "ParallelogramUtils.h" #include "sun_java2d_loops_FillParallelogram.h" -#define PGRAM_MIN_MAX(bmin, bmax, v0, dv1, dv2) \ - do { \ - double vmin, vmax; \ - if (dv1 < 0) { \ - vmin = v0+dv1; \ - vmax = v0; \ - } else { \ - vmin = v0; \ - vmax = v0+dv1; \ - } \ - if (dv2 < 0) { \ - vmin -= dv2; \ - } else { \ - vmax += dv2; \ - } \ - bmin = (jint) floor(vmin + 0.5); \ - bmax = (jint) floor(vmax + 0.5); \ - } while(0) - -#define PGRAM_INIT_X(starty, x, y, slope) \ - (DblToLong((x) + (slope) * ((starty)+0.5 - (y))) + LongOneHalf - 1) - /* * Class: sun_java2d_loops_FillParallelogram * Method: FillParallelogram @@ -76,22 +55,11 @@ /* * Sort parallelogram by y values, ensure that each delta vector - * has a non-negative y delta, and eliminate degenerate parallelograms. + * has a non-negative y delta. */ - if (dy1 < 0) { - x0 += dx1; y0 += dy1; - dx1 = -dx1; dy1 = -dy1; - } - if (dy2 < 0) { - x0 += dx2; y0 += dy2; - dx2 = -dx2; dy2 = -dy2; - } - /* Sort delta vectors so dxy1 is left of dxy2. */ - if (dx1 * dy2 > dx2 * dy1) { - double v = dx1; dx1 = dx2; dx2 = v; - v = dy1; dy1 = dy2; dy2 = v; - } - PGRAM_MIN_MAX(ix1, ix2, x0, dx1, dx2); + SORT_PGRAM(x0, y0, dx1, dy1, dx2, dy2, ); + + PGRAM_MIN_MAX(ix1, ix2, x0, dx1, dx2, JNI_FALSE); iy1 = (jint) floor(y0 + 0.5); iy2 = (jint) floor(y0 + dy1 + dy2 + 0.5); diff -Nru openjdk.orig/jdk/src/share/native/sun/java2d/loops/MaskFill.c openjdk/jdk/src/share/native/sun/java2d/loops/MaskFill.c --- openjdk.orig/jdk/src/share/native/sun/java2d/loops/MaskFill.c 2011-02-28 16:06:47.000000000 +0000 +++ openjdk/jdk/src/share/native/sun/java2d/loops/MaskFill.c 2012-06-08 12:08:06.232870807 +0100 @@ -23,7 +23,11 @@ * questions. */ +#include <math.h> +#include <stdlib.h> +#include <string.h> #include "GraphicsPrimitiveMgr.h" +#include "ParallelogramUtils.h" #include "sun_java2d_loops_MaskFill.h" @@ -93,6 +97,967 @@ } } SurfaceData_InvokeRelease(env, sdOps, &rasInfo); + } + SurfaceData_InvokeUnlock(env, sdOps, &rasInfo); +} + +#define MASK_BUF_LEN 1024 + +#define DblToMask(v) ((unsigned char) ((v)*255.9999)) + +/* Fills an aligned rectangle with potentially translucent edges. */ +static void +fillAARect(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, + CompositeInfo *pCompInfo, jint color, unsigned char *pMask, + void *pDst, + jdouble x1, jdouble y1, jdouble x2, jdouble y2) +{ + jint cx1 = pRasInfo->bounds.x1; + jint cy1 = pRasInfo->bounds.y1; + jint cx2 = pRasInfo->bounds.x2; + jint cy2 = pRasInfo->bounds.y2; + jint rx1 = (jint) ceil(x1); + jint ry1 = (jint) ceil(y1); + jint rx2 = (jint) floor(x2); + jint ry2 = (jint) floor(y2); + jint width = cx2 - cx1; + jint scan = pRasInfo->scanStride; + /* Convert xy12 into the edge coverage fractions for those edges. */ + x1 = rx1-x1; + y1 = ry1-y1; + x2 = x2-rx2; + y2 = y2-ry2; + if (ry2 < ry1) { + /* Accumulate bottom coverage into top coverage. */ + y1 = y1 + y2 - 1.0; + /* prevent processing of "bottom fractional row" */ + ry2 = cy2; + } + if (rx2 < rx1) { + /* Accumulate right coverage into left coverage. */ + x1 = x1 + x2 - 1.0; + /* prevent processing of "right fractional column" */ + rx2 = cx2; + } + /* Check for a visible "top fractional row" and process it */ + if (cy1 < ry1) { + unsigned char midcov = DblToMask(y1); + jint x; + for (x = 0; x < width; x++) { + pMask[x] = midcov; + } + if (cx1 < rx1) { + pMask[0] = DblToMask(y1 * x1); + } + if (cx2 > rx2) { + pMask[width-1] = DblToMask(y1 * x2); + } + (*pPrim->funcs.maskfill)(pDst, + pMask, 0, 0, + width, 1, + color, pRasInfo, + pPrim, pCompInfo); + pDst = PtrAddBytes(pDst, scan); + cy1++; + } + /* Check for a visible "left fract, solid middle, right fract" section. */ + if (cy1 < ry2 && cy1 < cy2) { + jint midh = ((ry2 < cy2) ? ry2 : cy2) - cy1; + jint midx = cx1; + void *pMid = pDst; + /* First process the left "fractional column" if it is visible. */ + if (midx < rx1) { + pMask[0] = DblToMask(x1); + /* Note: maskscan == 0 means we reuse this value for every row. */ + (*pPrim->funcs.maskfill)(pMid, + pMask, 0, 0, + 1, midh, + color, pRasInfo, + pPrim, pCompInfo); + pMid = PtrAddBytes(pMid, pRasInfo->pixelStride); + midx++; + } + /* Process the central solid section if it is visible. */ + if (midx < rx2 && midx < cx2) { + jint midw = ((rx2 < cx2) ? rx2 : cx2) - midx; + /* A NULL mask buffer means "all coverages are 0xff" */ + (*pPrim->funcs.maskfill)(pMid, + NULL, 0, 0, + midw, midh, + color, pRasInfo, + pPrim, pCompInfo); + pMid = PtrCoord(pMid, midw, pRasInfo->pixelStride, 0, 0); + midx += midw; + } + /* Finally process the right "fractional column" if it is visible. */ + if (midx < cx2) { + pMask[0] = DblToMask(x2); + /* Note: maskscan == 0 means we reuse this value for every row. */ + (*pPrim->funcs.maskfill)(pMid, + pMask, 0, 0, + 1, midh, + color, pRasInfo, + pPrim, pCompInfo); + } + cy1 += midh; + pDst = PtrCoord(pDst, 0, 0, midh, scan); + } + /* Check for a visible "bottom fractional row" and process it */ + if (cy1 < cy2) { + unsigned char midcov = DblToMask(y2); + jint x; + for (x = 0; x < width; x++) { + pMask[x] = midcov; + } + if (cx1 < rx1) { + pMask[0] = DblToMask(y2 * x1); + } + if (cx2 > rx2) { + pMask[width-1] = DblToMask(y2 * x2); + } + (*pPrim->funcs.maskfill)(pDst, + pMask, 0, 0, + width, 1, + color, pRasInfo, + pPrim, pCompInfo); + } +} + +/* + * Support code for arbitrary tracing and MaskFill filling of + * non-rectilinear (diagonal) parallelograms. + * + * This code is based upon the following model of AA coverage. + * + * Each edge of a parallelogram (for fillPgram) or a double + * parallelogram (inner and outer parallelograms for drawPgram) + * can be rasterized independently because the geometry is well + * defined in such a way that none of the sides will ever cross + * each other and they have a fixed ordering that is fairly + * well predetermined. + * + * So, for each edge we will look at the diagonal line that + * the edge makes as it passes through a row of pixels. Some + * such diagonal lines may pass entirely through the row of + * pixels in a single pixel column. Some may cut across the + * row and pass through several pixel columns before they pass + * on to the next row. + * + * As the edge passes through the row of pixels it will affect + * the coverage of the pixels it passes through as well as all + * of the pixels to the right of the edge. The coverage will + * either be increased (by a left edge of a parallelogram) or + * decreased (by a right edge) for all pixels to the right, until + * another edge passing the opposite direction is encountered. + * + * The coverage added or subtracted by an edge as it crosses a + * given pixel is calculated using a trapezoid formula in the + * following manner: + * + * / + * +-----+---/-+-----+ + * | | / | | + * | | / | | + * +-----+/----+-----+ + * / + * + * The area to the right of that edge for the pixel where it + * crosses is given as: + * + * trapheight * (topedge + bottomedge)/2 + * + * Another thing to note is that the above formula gives the + * contribution of that edge to the given pixel where it crossed, + * but in so crossing the pixel row, it also created 100% coverage + * for all of the pixels to the right. + * + * This example was simplified in that the edge depicted crossed + * the complete pixel row and it did so entirely within the bounds + * of a single pixel column. In practice, many edges may start or + * end in a given row and thus provide only partial row coverage + * (i.e. the total "trapheight" in the formula never reaches 1.0). + * And in other cases, edges may travel sideways through several + * pixel columns on a given pixel row from where they enter it to + * where the leave it (which also mans that the trapheight for a + * given pixel will be less than 1.0, but by the time the edge + * completes its journey through the pixel row the "coverage shadow" + * that it casts on all pixels to the right eventually reaches 100%). + * + * In order to simplify the calculations so that we don't have to + * keep propagating coverages we calculate for one edge "until we + * reach another edge" we will process one edge at a time and + * simply record in a buffer the amount that an edge added to + * or subtracted from the coverage for a given pixel and its + * following right-side neighbors. Thus, the true total coverage + * of a given pixel is only determined by summing the deltas for + * that pixel and all of the pixels to its left. Since we already + * have to scan the buffer to change floating point coverages into + * mask values for a MaskFill loop, it is simple enough to sum the + * values as we perform that scan from left to right. + * + * In the above example, note that 2 deltas need to be recorded even + * though the edge only intersected a single pixel. The delta recorded + * for the pixel where the edge crossed will be approximately 55% + * (guesstimating by examining the poor ascii art) which is fine for + * determining how to render that pixel, but the rest of the pixels + * to its right should have their coverage modified by a full 100% + * and the 55% delta value we recorded for the pixel that the edge + * crossed will not get them there. We adjust for this by adding + * the "remainder" of the coverage implied by the shadow to the + * pixel immediately to the right of where we record a trapezoidal + * contribution. In this case a delta of 45% will be recorded in + * the pixel immediately to the right to raise the total to 100%. + * + * As we sum these delta values as we process the line from left + * to right, these delta values will typically drive the sum from + * 0% up to 100% and back down to 0% over the course of a single + * pixel row. In the case of a drawn (double) parallelogram the + * sum will go to 100% and back to 0% twice on most scanlines. + * + * The fillAAPgram and drawAAPgram functions drive the main flow + * of the algorithm with help from the following structures, + * macros, and functions. It is probably best to start with + * those 2 functions to gain an understanding of the algorithm. + */ +typedef struct { + jdouble x; + jdouble y; + jdouble xbot; + jdouble ybot; + jdouble xnexty; + jdouble ynextx; + jdouble xnextx; + jdouble linedx; + jdouble celldx; + jdouble celldy; + jboolean isTrailing; +} EdgeInfo; + +#define MIN_DELTA (1.0/256.0) + +/* + * Calculates slopes and deltas for an edge and stores results in an EdgeInfo. + * Returns true if the edge was valid (i.e. not ignored for some reason). + */ +static jboolean +storeEdge(EdgeInfo *pEdge, + jdouble x, jdouble y, jdouble dx, jdouble dy, + jint cx1, jint cy1, jint cx2, jint cy2, + jboolean isTrailing) +{ + jdouble xbot = x + dx; + jdouble ybot = y + dy; + jboolean ret; + + pEdge->x = x; + pEdge->y = y; + pEdge->xbot = xbot; + pEdge->ybot = ybot; + + /* Note that parallelograms are sorted so dy is always non-negative */ + if (dy > MIN_DELTA && /* NaN and horizontal protection */ + ybot > cy1 && /* NaN and "OUT_ABOVE" protection */ + y < cy2 && /* NaN and "OUT_BELOW" protection */ + xbot == xbot && /* NaN protection */ + (x < cx2 || xbot < cx2)) /* "OUT_RIGHT" protection */ + /* Note: "OUT_LEFT" segments may still contribute coverage... */ + { + /* no NaNs, dy is not horizontal, and segment contributes to clip */ + if (dx < -MIN_DELTA || dx > MIN_DELTA) { + /* dx is not vertical */ + jdouble linedx; + jdouble celldy; + jdouble nextx; + + linedx = dx / dy; + celldy = dy / dx; + if (y < cy1) { + pEdge->x = x = x + (cy1 - y) * linedx; + pEdge->y = y = cy1; + } + pEdge->linedx = linedx; + if (dx < 0) { + pEdge->celldx = -1.0; + pEdge->celldy = -celldy; + pEdge->xnextx = nextx = ceil(x) - 1.0; + } else { + pEdge->celldx = +1.0; + pEdge->celldy = celldy; + pEdge->xnextx = nextx = floor(x) + 1.0; + } + pEdge->ynextx = y + (nextx - x) * celldy; + pEdge->xnexty = x + ((floor(y) + 1) - y) * linedx; + } else { + /* dx is essentially vertical */ + if (y < cy1) { + pEdge->y = y = cy1; + } + pEdge->xbot = x; + pEdge->linedx = 0.0; + pEdge->celldx = 0.0; + pEdge->celldy = 1.0; + pEdge->xnextx = x; + pEdge->xnexty = x; + pEdge->ynextx = ybot; + } + ret = JNI_TRUE; + } else { + /* There is some reason to ignore this segment, "celldy=0" omits it */ + pEdge->ybot = y; + pEdge->linedx = dx; + pEdge->celldx = dx; + pEdge->celldy = 0.0; + pEdge->xnextx = xbot; + pEdge->xnexty = xbot; + pEdge->ynextx = y; + ret = JNI_FALSE; + } + pEdge->isTrailing = isTrailing; + return ret; +} + +/* + * Calculates and stores slopes and deltas for all edges of a parallelogram. + * Returns true if at least 1 edge was valid (i.e. not ignored for some reason). + * + * The inverted flag is true for an outer parallelogram (left and right + * edges are leading and trailing) and false for an inner parallelogram + * (where the left edge is trailing and the right edge is leading). + */ +static jboolean +storePgram(EdgeInfo *pLeftEdge, EdgeInfo *pRightEdge, + jdouble x, jdouble y, + jdouble dx1, jdouble dy1, + jdouble dx2, jdouble dy2, + jint cx1, jint cy1, jint cx2, jint cy2, + jboolean inverted) +{ + jboolean ret = JNI_FALSE; + ret = (storeEdge(pLeftEdge + 0, + x , y , dx1, dy1, + cx1, cy1, cx2, cy2, inverted) || ret); + ret = (storeEdge(pLeftEdge + 1, + x+dx1, y+dy1, dx2, dy2, + cx1, cy1, cx2, cy2, inverted) || ret); + ret = (storeEdge(pRightEdge + 0, + x , y , dx2, dy2, + cx1, cy1, cx2, cy2, !inverted) || ret); + ret = (storeEdge(pRightEdge + 1, + x+dx2, y+dy2, dx1, dy1, + cx1, cy1, cx2, cy2, !inverted) || ret); + return ret; +} + +/* + * The X0,Y0,X1,Y1 values represent a trapezoidal fragment whose + * coverage must be accounted for in the accum buffer. + * + * All four values are assumed to fall within (or on the edge of) + * a single pixel. + * + * The trapezoid area is accumulated into the proper element of + * the accum buffer and the remainder of the "slice height" is + * accumulated into the element to its right. + */ +#define INSERT_ACCUM(pACCUM, IMIN, IMAX, X0, Y0, X1, Y1, CX1, CX2, MULT) \ + do { \ + jdouble xmid = ((X0) + (X1)) * 0.5; \ + if (xmid <= (CX2)) { \ + jdouble sliceh = ((Y1) - (Y0)); \ + jdouble slicearea; \ + jint i; \ + if (xmid < (CX1)) { \ + /* Accumulate the entire slice height into accum[0]. */ \ + i = 0; \ + slicearea = sliceh; \ + } else { \ + jdouble xpos = floor(xmid); \ + i = ((jint) xpos) - (CX1); \ + slicearea = (xpos+1-xmid) * sliceh; \ + } \ + if (IMIN > i) { \ + IMIN = i; \ + } \ + (pACCUM)[i++] += (jfloat) ((MULT) * slicearea); \ + (pACCUM)[i++] += (jfloat) ((MULT) * (sliceh - slicearea)); \ + if (IMAX < i) { \ + IMAX = i; \ + } \ + } \ + } while (0) + +/* + * Accumulate the contributions for a given edge crossing a given + * scan line into the corresponding entries of the accum buffer. + * CY1 is the Y coordinate of the top edge of the scanline and CY2 + * is equal to (CY1 + 1) and is the Y coordinate of the bottom edge + * of the scanline. CX1 and CX2 are the left and right edges of the + * clip (or area of interest) being rendered. + * + * The edge is processed from the top edge to the bottom edge and + * a single pixel column at a time. + */ +#define ACCUM_EDGE(pEDGE, pACCUM, IMIN, IMAX, CX1, CY1, CX2, CY2) \ + do { \ + jdouble x, y, xnext, ynext, xlast, ylast, dx, dy, mult; \ + y = (pEDGE)->y; \ + dy = (pEDGE)->celldy; \ + ylast = (pEDGE)->ybot; \ + if (ylast <= (CY1) || y >= (CY2) || dy == 0.0) { \ + break; \ + } \ + x = (pEDGE)->x; \ + dx = (pEDGE)->celldx; \ + if (ylast > (CY2)) { \ + ylast = (CY2); \ + xlast = (pEDGE)->xnexty; \ + } else { \ + xlast = (pEDGE)->xbot; \ + } \ + xnext = (pEDGE)->xnextx; \ + ynext = (pEDGE)->ynextx; \ + mult = ((pEDGE)->isTrailing) ? -1.0 : 1.0; \ + while (ynext <= ylast) { \ + INSERT_ACCUM(pACCUM, IMIN, IMAX, \ + x, y, xnext, ynext, \ + CX1, CX2, mult); \ + x = xnext; \ + y = ynext; \ + xnext += dx; \ + ynext += dy; \ + } \ + (pEDGE)->ynextx = ynext; \ + (pEDGE)->xnextx = xnext; \ + INSERT_ACCUM(pACCUM, IMIN, IMAX, \ + x, y, xlast, ylast, \ + CX1, CX2, mult); \ + (pEDGE)->x = xlast; \ + (pEDGE)->y = ylast; \ + (pEDGE)->xnexty = xlast + (pEDGE)->linedx; \ + } while(0) + +/* Main function to fill a single Parallelogram */ +static void +fillAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, + CompositeInfo *pCompInfo, jint color, unsigned char *pMask, + void *pDst, + jdouble x1, jdouble y1, + jdouble dx1, jdouble dy1, + jdouble dx2, jdouble dy2) +{ + jint cx1 = pRasInfo->bounds.x1; + jint cy1 = pRasInfo->bounds.y1; + jint cx2 = pRasInfo->bounds.x2; + jint cy2 = pRasInfo->bounds.y2; + jint width = cx2 - cx1; + EdgeInfo edges[4]; + jfloat localaccum[MASK_BUF_LEN + 1]; + jfloat *pAccum; + + if (!storePgram(edges + 0, edges + 2, + x1, y1, dx1, dy1, dx2, dy2, + cx1, cy1, cx2, cy2, + JNI_FALSE)) + { + return; + } + + pAccum = ((width > MASK_BUF_LEN) + ? malloc((width + 1) * sizeof(jfloat)) + : localaccum); + if (pAccum == NULL) { + return; + } + memset(pAccum, 0, (width+1) * sizeof(jfloat)); + + while (cy1 < cy2) { + jint lmin, lmax, rmin, rmax; + jint moff, x; + jdouble accum; + unsigned char lastcov; + + lmin = rmin = width + 2; + lmax = rmax = 0; + ACCUM_EDGE(&edges[0], pAccum, lmin, lmax, + cx1, cy1, cx2, cy1+1); + ACCUM_EDGE(&edges[1], pAccum, lmin, lmax, + cx1, cy1, cx2, cy1+1); + ACCUM_EDGE(&edges[2], pAccum, rmin, rmax, + cx1, cy1, cx2, cy1+1); + ACCUM_EDGE(&edges[3], pAccum, rmin, rmax, + cx1, cy1, cx2, cy1+1); + if (lmax > width) { + lmax = width; /* Extra col has data we do not need. */ + } + if (rmax > width) { + rmax = width; /* Extra col has data we do not need. */ + } + /* If ranges overlap, handle both in the first pass. */ + if (rmin <= lmax) { + lmax = rmax; + } + + x = lmin; + accum = 0.0; + moff = 0; + lastcov = 0; + while (x < lmax) { + accum += pAccum[x]; + pAccum[x] = 0.0f; + pMask[moff++] = lastcov = DblToMask(accum); + x++; + } + /* Check for a solid center section. */ + if (lastcov == 0xFF) { + jint endx; + void *pRow; + + /* First process the existing partial coverage data. */ + if (moff > 0) { + pRow = PtrCoord(pDst, x-moff, pRasInfo->pixelStride, 0, 0); + (*pPrim->funcs.maskfill)(pRow, + pMask, 0, 0, + moff, 1, + color, pRasInfo, + pPrim, pCompInfo); + moff = 0; + } + + /* Where does the center section end? */ + /* If there is no right AA edge in the accum buffer, then */ + /* the right edge was beyond the clip, so fill out to width */ + endx = (rmin < rmax) ? rmin : width; + if (x < endx) { + pRow = PtrCoord(pDst, x, pRasInfo->pixelStride, 0, 0); + (*pPrim->funcs.maskfill)(pRow, + NULL, 0, 0, + endx - x, 1, + color, pRasInfo, + pPrim, pCompInfo); + x = endx; + } + } else if (lastcov > 0 && rmin >= rmax) { + /* We are not at 0 coverage, but there is no right edge, */ + /* force a right edge so we process pixels out to width. */ + rmax = width; + } + /* The following loop will process the right AA edge and/or any */ + /* partial coverage center section not processed above. */ + while (x < rmax) { + accum += pAccum[x]; + pAccum[x] = 0.0f; + pMask[moff++] = DblToMask(accum); + x++; + } + if (moff > 0) { + void *pRow = PtrCoord(pDst, x-moff, pRasInfo->pixelStride, 0, 0); + (*pPrim->funcs.maskfill)(pRow, + pMask, 0, 0, + moff, 1, + color, pRasInfo, + pPrim, pCompInfo); + } + pDst = PtrAddBytes(pDst, pRasInfo->scanStride); + cy1++; + } + if (pAccum != localaccum) { + free(pAccum); + } +} + +/* + * Class: sun_java2d_loops_MaskFill + * Method: FillAAPgram + * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Ljava/awt/Composite;DDDDDD)V + */ +JNIEXPORT void JNICALL +Java_sun_java2d_loops_MaskFill_FillAAPgram + (JNIEnv *env, jobject self, + jobject sg2d, jobject sData, jobject comp, + jdouble x0, jdouble y0, + jdouble dx1, jdouble dy1, + jdouble dx2, jdouble dy2) +{ + SurfaceDataOps *sdOps; + SurfaceDataRasInfo rasInfo; + NativePrimitive *pPrim; + CompositeInfo compInfo; + jint ix1, iy1, ix2, iy2; + + if ((dy1 == 0 && dx1 == 0) || (dy2 == 0 && dx2 == 0)) { + return; + } + + /* + * Sort parallelogram by y values, ensure that each delta vector + * has a non-negative y delta. + */ + SORT_PGRAM(x0, y0, dx1, dy1, dx2, dy2, ); + + PGRAM_MIN_MAX(ix1, ix2, x0, dx1, dx2, JNI_TRUE); + iy1 = (jint) floor(y0); + iy2 = (jint) ceil(y0 + dy1 + dy2); + + pPrim = GetNativePrim(env, self); + if (pPrim == NULL) { + return; + } + if (pPrim->pCompType->getCompInfo != NULL) { + (*pPrim->pCompType->getCompInfo)(env, &compInfo, comp); + } + + sdOps = SurfaceData_GetOps(env, sData); + if (sdOps == 0) { + return; + } + + GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds); + SurfaceData_IntersectBoundsXYXY(&rasInfo.bounds, ix1, iy1, ix2, iy2); + if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 || + rasInfo.bounds.x2 <= rasInfo.bounds.x1) + { + return; + } + + if (sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags) != SD_SUCCESS) { + return; + } + + ix1 = rasInfo.bounds.x1; + iy1 = rasInfo.bounds.y1; + ix2 = rasInfo.bounds.x2; + iy2 = rasInfo.bounds.y2; + if (ix2 > ix1 && iy2 > iy1) { + jint width = ix2 - ix1; + jint color = GrPrim_Sg2dGetEaRGB(env, sg2d); + unsigned char localmask[MASK_BUF_LEN]; + unsigned char *pMask = ((width > MASK_BUF_LEN) + ? malloc(width) + : localmask); + + sdOps->GetRasInfo(env, sdOps, &rasInfo); + if (rasInfo.rasBase != NULL && pMask != NULL) { + void *pDst = PtrCoord(rasInfo.rasBase, + ix1, rasInfo.pixelStride, + iy1, rasInfo.scanStride); + if (dy1 == 0 && dx2 == 0) { + if (dx1 < 0) { + // We sorted by Y above, but not by X + x0 += dx1; + dx1 = -dx1; + } + fillAARect(pPrim, &rasInfo, &compInfo, + color, pMask, pDst, + x0, y0, x0+dx1, y0+dy2); + } else if (dx1 == 0 && dy2 == 0) { + if (dx2 < 0) { + // We sorted by Y above, but not by X + x0 += dx2; + dx2 = -dx2; + } + fillAARect(pPrim, &rasInfo, &compInfo, + color, pMask, pDst, + x0, y0, x0+dx2, y0+dy1); + } else { + fillAAPgram(pPrim, &rasInfo, &compInfo, + color, pMask, pDst, + x0, y0, dx1, dy1, dx2, dy2); + } + } + SurfaceData_InvokeRelease(env, sdOps, &rasInfo); + if (pMask != NULL && pMask != localmask) { + free(pMask); + } + } + SurfaceData_InvokeUnlock(env, sdOps, &rasInfo); +} + +/* Main function to fill a double pair of (inner and outer) parallelograms */ +static void +drawAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, + CompositeInfo *pCompInfo, jint color, unsigned char *pMask, + void *pDst, + jdouble ox0, jdouble oy0, + jdouble dx1, jdouble dy1, + jdouble dx2, jdouble dy2, + jdouble ldx1, jdouble ldy1, + jdouble ldx2, jdouble ldy2) +{ + jint cx1 = pRasInfo->bounds.x1; + jint cy1 = pRasInfo->bounds.y1; + jint cx2 = pRasInfo->bounds.x2; + jint cy2 = pRasInfo->bounds.y2; + jint width = cx2 - cx1; + EdgeInfo edges[8]; + jfloat localaccum[MASK_BUF_LEN + 1]; + jfloat *pAccum; + + if (!storePgram(edges + 0, edges + 6, + ox0, oy0, + dx1 + ldx1, dy1 + ldy1, + dx2 + ldx2, dy2 + ldy2, + cx1, cy1, cx2, cy2, + JNI_FALSE)) + { + /* If outer pgram does not contribute, then inner cannot either. */ + return; + } + storePgram(edges + 2, edges + 4, + ox0 + ldx1 + ldx2, oy0 + ldy1 + ldy2, + dx1 - ldx1, dy1 - ldy1, + dx2 - ldx2, dy2 - ldy2, + cx1, cy1, cx2, cy2, + JNI_TRUE); + + pAccum = ((width > MASK_BUF_LEN) + ? malloc((width + 1) * sizeof(jfloat)) + : localaccum); + if (pAccum == NULL) { + return; + } + memset(pAccum, 0, (width+1) * sizeof(jfloat)); + + while (cy1 < cy2) { + jint lmin, lmax, rmin, rmax; + jint moff, x; + jdouble accum; + unsigned char lastcov; + + lmin = rmin = width + 2; + lmax = rmax = 0; + ACCUM_EDGE(&edges[0], pAccum, lmin, lmax, + cx1, cy1, cx2, cy1+1); + ACCUM_EDGE(&edges[1], pAccum, lmin, lmax, + cx1, cy1, cx2, cy1+1); + ACCUM_EDGE(&edges[2], pAccum, lmin, lmax, + cx1, cy1, cx2, cy1+1); + ACCUM_EDGE(&edges[3], pAccum, lmin, lmax, + cx1, cy1, cx2, cy1+1); + ACCUM_EDGE(&edges[4], pAccum, rmin, rmax, + cx1, cy1, cx2, cy1+1); + ACCUM_EDGE(&edges[5], pAccum, rmin, rmax, + cx1, cy1, cx2, cy1+1); + ACCUM_EDGE(&edges[6], pAccum, rmin, rmax, + cx1, cy1, cx2, cy1+1); + ACCUM_EDGE(&edges[7], pAccum, rmin, rmax, + cx1, cy1, cx2, cy1+1); + if (lmax > width) { + lmax = width; /* Extra col has data we do not need. */ + } + if (rmax > width) { + rmax = width; /* Extra col has data we do not need. */ + } + /* If ranges overlap, handle both in the first pass. */ + if (rmin <= lmax) { + lmax = rmax; + } + + x = lmin; + accum = 0.0; + moff = 0; + lastcov = 0; + while (x < lmax) { + accum += pAccum[x]; + pAccum[x] = 0.0f; + pMask[moff++] = lastcov = DblToMask(accum); + x++; + } + /* Check for an empty or solidcenter section. */ + if (lastcov == 0 || lastcov == 0xFF) { + jint endx; + void *pRow; + + /* First process the existing partial coverage data. */ + if (moff > 0) { + pRow = PtrCoord(pDst, x-moff, pRasInfo->pixelStride, 0, 0); + (*pPrim->funcs.maskfill)(pRow, + pMask, 0, 0, + moff, 1, + color, pRasInfo, + pPrim, pCompInfo); + moff = 0; + } + + /* Where does the center section end? */ + /* If there is no right AA edge in the accum buffer, then */ + /* the right edge was beyond the clip, so fill out to width */ + endx = (rmin < rmax) ? rmin : width; + if (x < endx) { + if (lastcov == 0xFF) { + pRow = PtrCoord(pDst, x, pRasInfo->pixelStride, 0, 0); + (*pPrim->funcs.maskfill)(pRow, + NULL, 0, 0, + endx - x, 1, + color, pRasInfo, + pPrim, pCompInfo); + } + x = endx; + } + } else if (rmin >= rmax) { + /* We are not at 0 coverage, but there is no right edge, */ + /* force a right edge so we process pixels out to width. */ + rmax = width; + } + /* The following loop will process the right AA edge and/or any */ + /* partial coverage center section not processed above. */ + while (x < rmax) { + accum += pAccum[x]; + pAccum[x] = 0.0f; + pMask[moff++] = lastcov = DblToMask(accum); + x++; + } + if (moff > 0) { + void *pRow = PtrCoord(pDst, x-moff, pRasInfo->pixelStride, 0, 0); + (*pPrim->funcs.maskfill)(pRow, + pMask, 0, 0, + moff, 1, + color, pRasInfo, + pPrim, pCompInfo); + } + if (lastcov == 0xFF && x < width) { + void *pRow = PtrCoord(pDst, x, pRasInfo->pixelStride, 0, 0); + (*pPrim->funcs.maskfill)(pRow, + NULL, 0, 0, + width - x, 1, + color, pRasInfo, + pPrim, pCompInfo); + } + pDst = PtrAddBytes(pDst, pRasInfo->scanStride); + cy1++; + } + if (pAccum != localaccum) { + free(pAccum); + } +} + +/* + * Class: sun_java2d_loops_MaskFill + * Method: DrawAAPgram + * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Ljava/awt/Composite;DDDDDDDD)V + */ +JNIEXPORT void JNICALL +Java_sun_java2d_loops_MaskFill_DrawAAPgram + (JNIEnv *env, jobject self, + jobject sg2d, jobject sData, jobject comp, + jdouble x0, jdouble y0, + jdouble dx1, jdouble dy1, + jdouble dx2, jdouble dy2, + jdouble lw1, jdouble lw2) +{ + SurfaceDataOps *sdOps; + SurfaceDataRasInfo rasInfo; + NativePrimitive *pPrim; + CompositeInfo compInfo; + jint ix1, iy1, ix2, iy2; + jdouble ldx1, ldy1, ldx2, ldy2; + jdouble ox0, oy0; + + if ((dy1 == 0 && dx1 == 0) || (dy2 == 0 && dx2 == 0)) { + return; + } + + /* + * Sort parallelogram by y values, ensure that each delta vector + * has a non-negative y delta. + */ + SORT_PGRAM(x0, y0, dx1, dy1, dx2, dy2, + v = lw1; lw1 = lw2; lw2 = v;); + + // dx,dy for line width in the "1" and "2" directions. + ldx1 = dx1 * lw1; + ldy1 = dy1 * lw1; + ldx2 = dx2 * lw2; + ldy2 = dy2 * lw2; + + // calculate origin of the outer parallelogram + ox0 = x0 - (ldx1 + ldx2) / 2.0; + oy0 = y0 - (ldy1 + ldy2) / 2.0; + + if (lw1 >= 1.0 || lw2 >= 1.0) { + /* Only need to fill an outer pgram if the interior no longer + * has a hole in it (i.e. if either of the line width ratios + * were greater than or equal to 1.0). + */ + Java_sun_java2d_loops_MaskFill_FillAAPgram(env, self, + sg2d, sData, comp, + ox0, oy0, + dx1 + ldx1, dy1 + ldy1, + dx2 + ldx2, dy2 + ldy2); + return; + } + + PGRAM_MIN_MAX(ix1, ix2, ox0, dx1+ldx1, dx2+ldx2, JNI_TRUE); + iy1 = (jint) floor(oy0); + iy2 = (jint) ceil(oy0 + dy1 + ldy1 + dy2 + ldy2); + + pPrim = GetNativePrim(env, self); + if (pPrim == NULL) { + return; + } + if (pPrim->pCompType->getCompInfo != NULL) { + (*pPrim->pCompType->getCompInfo)(env, &compInfo, comp); + } + + sdOps = SurfaceData_GetOps(env, sData); + if (sdOps == 0) { + return; + } + + GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds); + SurfaceData_IntersectBoundsXYXY(&rasInfo.bounds, ix1, iy1, ix2, iy2); + if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 || + rasInfo.bounds.x2 <= rasInfo.bounds.x1) + { + return; + } + + if (sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags) != SD_SUCCESS) { + return; + } + + ix1 = rasInfo.bounds.x1; + iy1 = rasInfo.bounds.y1; + ix2 = rasInfo.bounds.x2; + iy2 = rasInfo.bounds.y2; + if (ix2 > ix1 && iy2 > iy1) { + jint width = ix2 - ix1; + jint color = GrPrim_Sg2dGetEaRGB(env, sg2d); + unsigned char localmask[MASK_BUF_LEN]; + unsigned char *pMask = ((width > MASK_BUF_LEN) + ? malloc(width) + : localmask); + + sdOps->GetRasInfo(env, sdOps, &rasInfo); + if (rasInfo.rasBase != NULL && pMask != NULL) { + void *pDst = PtrCoord(rasInfo.rasBase, + ix1, rasInfo.pixelStride, + iy1, rasInfo.scanStride); + /* + * NOTE: aligned rects could probably be drawn + * even faster with a little work here. + * if (dy1 == 0 && dx2 == 0) { + * drawAARect(pPrim, &rasInfo, &compInfo, + * color, pMask, pDst, + * ox0, oy0, ox0+dx1+ldx1, oy0+dy2+ldy2, ldx1, ldy2); + * } else if (dx1 == 0 && dy2 == 0) { + * drawAARect(pPrim, &rasInfo, &compInfo, + * color, pMask, pDst, + * ox0, oy0, ox0+dx2+ldx2, oy0+dy1+ldy1, ldx2, ldy1); + * } else { + */ + drawAAPgram(pPrim, &rasInfo, &compInfo, + color, pMask, pDst, + ox0, oy0, + dx1, dy1, dx2, dy2, + ldx1, ldy1, ldx2, ldy2); + /* + * } + */ + } + SurfaceData_InvokeRelease(env, sdOps, &rasInfo); + if (pMask != NULL && pMask != localmask) { + free(pMask); + } } SurfaceData_InvokeUnlock(env, sdOps, &rasInfo); } diff -Nru openjdk.orig/jdk/src/share/native/sun/java2d/loops/ParallelogramUtils.h openjdk/jdk/src/share/native/sun/java2d/loops/ParallelogramUtils.h --- openjdk.orig/jdk/src/share/native/sun/java2d/loops/ParallelogramUtils.h 1970-01-01 01:00:00.000000000 +0100 +++ openjdk/jdk/src/share/native/sun/java2d/loops/ParallelogramUtils.h 2012-06-08 12:08:06.232870807 +0100 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ParallelogramUtils_h_Included +#define ParallelogramUtils_h_Included + +#ifdef __cplusplus +extern "C" { +#endif + +#define PGRAM_MIN_MAX(bmin, bmax, v0, dv1, dv2, AA) \ + do { \ + double vmin, vmax; \ + if (dv1 < 0) { \ + vmin = v0+dv1; \ + vmax = v0; \ + } else { \ + vmin = v0; \ + vmax = v0+dv1; \ + } \ + if (dv2 < 0) { \ + vmin += dv2; \ + } else { \ + vmax += dv2; \ + } \ + if (AA) { \ + bmin = (jint) floor(vmin); \ + bmax = (jint) ceil(vmax); \ + } else { \ + bmin = (jint) floor(vmin + 0.5); \ + bmax = (jint) floor(vmax + 0.5); \ + } \ + } while(0) + +#define PGRAM_INIT_X(starty, x, y, slope) \ + (DblToLong((x) + (slope) * ((starty)+0.5 - (y))) + LongOneHalf - 1) + +/* + * Sort parallelogram by y values, ensure that each delta vector + * has a non-negative y delta. + */ +#define SORT_PGRAM(x0, y0, dx1, dy1, dx2, dy2, OTHER_SWAP_CODE) \ + do { \ + if (dy1 < 0) { \ + x0 += dx1; y0 += dy1; \ + dx1 = -dx1; dy1 = -dy1; \ + } \ + if (dy2 < 0) { \ + x0 += dx2; y0 += dy2; \ + dx2 = -dx2; dy2 = -dy2; \ + } \ + /* Sort delta vectors so dxy1 is left of dxy2. */ \ + if (dx1 * dy2 > dx2 * dy1) { \ + double v; \ + v = dx1; dx1 = dx2; dx2 = v; \ + v = dy1; dy1 = dy2; dy2 = v; \ + OTHER_SWAP_CODE \ + } \ + } while(0) + +#endif /* ParallelogramUtils_h_Included */ diff -Nru openjdk.orig/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgbPre_Mask.c openjdk/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgbPre_Mask.c --- openjdk.orig/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgbPre_Mask.c 2011-02-28 16:06:50.000000000 +0000 +++ openjdk/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgbPre_Mask.c 2012-06-08 12:08:06.232870807 +0100 @@ -517,13 +517,15 @@ ADD_SUFF(AnyIntSetRect)(pRasInfo, 0, 0, width, height, fgColor, pPrim, pCompInfo); #else + void *pBase = pRasInfo->rasBase; + pRasInfo->rasBase = rasBase; if (cnstA != 0xff) { fgColor = (cnstA << 24) | (cnstR << 16) | (cnstG << 8) | cnstB; } ADD_SUFF(AnyIntSetRect)(pRasInfo, - pRasInfo->bounds.x1, pRasInfo->bounds.y1, - pRasInfo->bounds.x2, pRasInfo->bounds.y2, + 0, 0, width, height, fgColor, pPrim, pCompInfo); + pRasInfo->rasBase = pBase; #endif return; } @@ -582,11 +584,13 @@ } if (pMask == NULL) { + void *pBase = pRasInfo->rasBase; + pRasInfo->rasBase = rasBase; fgColor = (cnstR << 24) | (cnstG << 16) | (cnstB << 8) | cnstA; ADD_SUFF(Any4ByteSetRect)(pRasInfo, - pRasInfo->bounds.x1, pRasInfo->bounds.y1, - pRasInfo->bounds.x2, pRasInfo->bounds.y2, + 0, 0, width, height, fgColor, pPrim, pCompInfo); + pRasInfo->rasBase = pBase; return; } diff -Nru openjdk.orig/jdk/src/solaris/native/sun/java2d/loops/vis_SrcMaskFill.c openjdk/jdk/src/solaris/native/sun/java2d/loops/vis_SrcMaskFill.c --- openjdk.orig/jdk/src/solaris/native/sun/java2d/loops/vis_SrcMaskFill.c 2011-02-28 16:06:50.000000000 +0000 +++ openjdk/jdk/src/solaris/native/sun/java2d/loops/vis_SrcMaskFill.c 2012-06-08 12:08:06.232870807 +0100 @@ -150,10 +150,12 @@ } if (pMask == NULL) { + void *pBase = pRasInfo->rasBase; + pRasInfo->rasBase = rasBase; ADD_SUFF(AnyIntSetRect)(pRasInfo, - pRasInfo->bounds.x1, pRasInfo->bounds.y1, - pRasInfo->bounds.x2, pRasInfo->bounds.y2, + 0, 0, width, height, fgColor, pPrim, pCompInfo); + pRasInfo->rasBase = pBase; return; } @@ -214,15 +216,17 @@ cnstB = (fgColor ) & 0xff; if (pMask == NULL) { + void *pBase = pRasInfo->rasBase; + pRasInfo->rasBase = rasBase; if (cnstA == 0) { fgColor = 0; } else { fgColor = (fgColor << 8) | cnstA; } ADD_SUFF(Any4ByteSetRect)(pRasInfo, - pRasInfo->bounds.x1, pRasInfo->bounds.y1, - pRasInfo->bounds.x2, pRasInfo->bounds.y2, + 0, 0, width, height, fgColor, pPrim, pCompInfo); + pRasInfo->rasBase = pBase; return; } @@ -390,10 +394,12 @@ if (cnstA == 0) fgColor = 0; if (pMask == NULL) { + void *pBase = pRasInfo->rasBase; + pRasInfo->rasBase = rasBase; ADD_SUFF(AnyIntSetRect)(pRasInfo, - pRasInfo->bounds.x1, pRasInfo->bounds.y1, - pRasInfo->bounds.x2, pRasInfo->bounds.y2, + 0, 0, width, height, fgColor, pPrim, pCompInfo); + pRasInfo->rasBase = pBase; return; } @@ -458,10 +464,12 @@ } if (pMask == NULL) { + void *pBase = pRasInfo->rasBase; + pRasInfo->rasBase = rasBase; ADD_SUFF(AnyIntSetRect)(pRasInfo, - pRasInfo->bounds.x1, pRasInfo->bounds.y1, - pRasInfo->bounds.x2, pRasInfo->bounds.y2, + 0, 0, width, height, fgColor, pPrim, pCompInfo); + pRasInfo->rasBase = pBase; return; } @@ -526,10 +534,12 @@ } if (pMask == NULL) { + void *pBase = pRasInfo->rasBase; + pRasInfo->rasBase = rasBase; ADD_SUFF(Any3ByteSetRect)(pRasInfo, - pRasInfo->bounds.x1, pRasInfo->bounds.y1, - pRasInfo->bounds.x2, pRasInfo->bounds.y2, + 0, 0, width, height, fgColor, pPrim, pCompInfo); + pRasInfo->rasBase = pBase; return; } diff -Nru openjdk.orig/jdk/test/java/awt/Graphics2D/RenderClipTest/6766342.tests openjdk/jdk/test/java/awt/Graphics2D/RenderClipTest/6766342.tests --- openjdk.orig/jdk/test/java/awt/Graphics2D/RenderClipTest/6766342.tests 1970-01-01 01:00:00.000000000 +0100 +++ openjdk/jdk/test/java/awt/Graphics2D/RenderClipTest/6766342.tests 2012-06-08 12:08:06.232870807 +0100 @@ -0,0 +1,3 @@ +Filled AA Pure Rect(5, 29.4, 10, 10) +Stroked AA Pure Rect(5, 4.4, 10, 10) +Stroked AA Line(20, 20, -10, 20) diff -Nru openjdk.orig/jdk/test/java/awt/Graphics2D/RenderClipTest/RenderClipTest.java openjdk/jdk/test/java/awt/Graphics2D/RenderClipTest/RenderClipTest.java --- openjdk.orig/jdk/test/java/awt/Graphics2D/RenderClipTest/RenderClipTest.java 1970-01-01 01:00:00.000000000 +0100 +++ openjdk/jdk/test/java/awt/Graphics2D/RenderClipTest/RenderClipTest.java 2012-06-08 12:08:06.232870807 +0100 @@ -0,0 +1,1634 @@ +/* + * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6766342 + * @summary Tests clipping invariance for AA rectangle and line primitives + * @run main RenderClipTest -strict -readfile 6766342.tests + * @run main RenderClipTest -rectsuite -count 10 + */ + +import java.awt.*; +import java.awt.geom.*; +import java.awt.image.*; +import java.awt.event.*; +import java.util.Vector; +import java.io.*; + +public class RenderClipTest { + public static double randDblCoord() { + return Math.random()*60 - 10; + } + + public static float randFltCoord() { + return (float) randDblCoord(); + } + + public static int randIntCoord() { + return (int) Math.round(randDblCoord()); + } + + public static int randInt(int n) { + return ((int) (Math.random() * (n*4))) >> 2; + } + + static int numtests; + static int numerrors; + static int numfillfailures; + static int numstrokefailures; + static int maxerr; + + static boolean useAA; + static boolean strokePure; + static boolean testFill; + static boolean testDraw; + static boolean silent; + static boolean verbose; + static boolean strict; + static boolean showErrors; + static float lw; + static double rot; + + static BufferedImage imgref; + static BufferedImage imgtst; + + static Graphics2D grefclear; + static Graphics2D gtstclear; + static Graphics2D grefrender; + static Graphics2D gtstrender; + + public static abstract class AnnotatedRenderOp { + public static AnnotatedRenderOp parse(String str) { + AnnotatedRenderOp ar; + if (((ar = Cubic.tryparse(str)) != null) || + ((ar = Quad.tryparse(str)) != null) || + ((ar = Poly.tryparse(str)) != null) || + ((ar = Path.tryparse(str)) != null) || + ((ar = Rect.tryparse(str)) != null) || + ((ar = Line.tryparse(str)) != null) || + ((ar = RectMethod.tryparse(str)) != null) || + ((ar = LineMethod.tryparse(str)) != null)) + { + return ar; + } + System.err.println("Unable to parse shape: "+str); + return null; + } + + public abstract void randomize(); + + public abstract void fill(Graphics2D g2d); + + public abstract void draw(Graphics2D g2d); + } + + public static abstract class AnnotatedShapeOp extends AnnotatedRenderOp { + public abstract Shape getShape(); + + public void fill(Graphics2D g2d) { + g2d.fill(getShape()); + } + + public void draw(Graphics2D g2d) { + g2d.draw(getShape()); + } + } + + public static void usage(String err) { + if (err != null) { + System.err.println(err); + } + System.err.println("usage: java RenderClipTest "+ + "[-read[file F]] [-rectsuite] [-fill] [-draw]"); + System.err.println(" "+ + "[-aa] [-pure] [-lw N] [-rot N]"); + System.err.println(" "+ + "[-rectmethod] [-linemethod] [-rect] [-line]"); + System.err.println(" "+ + "[-cubic] [-quad] [-poly] [-path]"); + System.err.println(" "+ + "[-silent] [-verbose] [-showerr] [-count N]"); + System.err.println(" "+ + "[-strict] [-usage]"); + System.err.println(" -read Read test data from stdin"); + System.err.println(" -readfile F Read test data from file F"); + System.err.println(" -rectsuite Run a suite of rect/line tests"); + System.err.println(" -fill Test g.fill*(...)"); + System.err.println(" -draw Test g.draw*(...)"); + System.err.println(" -aa Use antialiased rendering"); + System.err.println(" -pure Use STROKE_PURE hint"); + System.err.println(" -lw N Test line widths of N "+ + "(default 1.0)"); + System.err.println(" -rot N Test rotation by N degrees "+ + "(default 0.0)"); + System.err.println(" -rectmethod Test fillRect/drawRect methods"); + System.err.println(" -linemethod Test drawLine method"); + System.err.println(" -rect Test Rectangle2D shapes"); + System.err.println(" -line Test Line2D shapes"); + System.err.println(" -cubic Test CubicCurve2D shapes"); + System.err.println(" -quad Test QuadCurve2D shapes"); + System.err.println(" -poly Test Polygon shapes"); + System.err.println(" -path Test GeneralPath shapes"); + System.err.println(" -silent Do not print out error curves"); + System.err.println(" -verbose Print out progress info"); + System.err.println(" -showerr Display errors on screen"); + System.err.println(" -count N N tests per shape, then exit "+ + "(default 1000)"); + System.err.println(" -strict All failures are important"); + System.err.println(" -usage Print this help, then exit"); + System.exit((err != null) ? -1 : 0); + } + + public static void main(String argv[]) { + boolean readTests = false; + String readFile = null; + boolean rectsuite = false; + int count = 1000; + lw = 1.0f; + rot = 0.0; + Vector<AnnotatedRenderOp> testOps = new Vector<AnnotatedRenderOp>(); + for (int i = 0; i < argv.length; i++) { + String arg = argv[i].toLowerCase(); + if (arg.equals("-aa")) { + useAA = true; + } else if (arg.equals("-pure")) { + strokePure = true; + } else if (arg.equals("-fill")) { + testFill = true; + } else if (arg.equals("-draw")) { + testDraw = true; + } else if (arg.equals("-lw")) { + if (i+1 >= argv.length) { + usage("Missing argument: "+argv[i]); + } + lw = Float.parseFloat(argv[++i]); + } else if (arg.equals("-rot")) { + if (i+1 >= argv.length) { + usage("Missing argument: "+argv[i]); + } + rot = Double.parseDouble(argv[++i]); + } else if (arg.equals("-cubic")) { + testOps.add(new Cubic()); + } else if (arg.equals("-quad")) { + testOps.add(new Quad()); + } else if (arg.equals("-poly")) { + testOps.add(new Poly()); + } else if (arg.equals("-path")) { + testOps.add(new Path()); + } else if (arg.equals("-rect")) { + testOps.add(new Rect()); + } else if (arg.equals("-line")) { + testOps.add(new Line()); + } else if (arg.equals("-rectmethod")) { + testOps.add(new RectMethod()); + } else if (arg.equals("-linemethod")) { + testOps.add(new LineMethod()); + } else if (arg.equals("-verbose")) { + verbose = true; + } else if (arg.equals("-strict")) { + strict = true; + } else if (arg.equals("-silent")) { + silent = true; + } else if (arg.equals("-showerr")) { + showErrors = true; + } else if (arg.equals("-readfile")) { + if (i+1 >= argv.length) { + usage("Missing argument: "+argv[i]); + } + readTests = true; + readFile = argv[++i]; + } else if (arg.equals("-read")) { + readTests = true; + readFile = null; + } else if (arg.equals("-rectsuite")) { + rectsuite = true; + } else if (arg.equals("-count")) { + if (i+1 >= argv.length) { + usage("Missing argument: "+argv[i]); + } + count = Integer.parseInt(argv[++i]); + } else if (arg.equals("-usage")) { + usage(null); + } else { + usage("Unknown argument: "+argv[i]); + } + } + if (readTests) { + if (rectsuite || testDraw || testFill || + useAA || strokePure || + lw != 1.0f || rot != 0.0 || + testOps.size() > 0) + { + usage("Should not specify test types with -read options"); + } + } else if (rectsuite) { + if (testDraw || testFill || + useAA || strokePure || + lw != 1.0f || rot != 0.0 || + testOps.size() > 0) + { + usage("Should not specify test types with -rectsuite option"); + } + } else { + if (!testDraw && !testFill) { + usage("No work: Must specify one or both of "+ + "-fill or -draw"); + } + if (testOps.size() == 0) { + usage("No work: Must specify one or more of "+ + "-rect[method], -line[method], "+ + "-cubic, -quad, -poly, or -path"); + } + } + initImages(); + if (readTests) { + try { + InputStream is; + if (readFile == null) { + is = System.in; + } else { + File f = + new File(System.getProperty("test.src", "."), + readFile); + is = new FileInputStream(f); + } + parseAndRun(is); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else if (rectsuite) { + runRectSuite(count); + } else { + initGCs(); + for (int k = 0; k < testOps.size(); k++) { + AnnotatedRenderOp ar = testOps.get(k); + runRandomTests(ar, count); + } + disposeGCs(); + } + grefclear.dispose(); + gtstclear.dispose(); + grefclear = gtstclear = null; + reportStatistics(); + } + + public static int reportStatistics() { + String connector = ""; + if (numfillfailures > 0) { + System.out.print(numfillfailures+" fills "); + connector = "and "; + } + if (numstrokefailures > 0) { + System.out.print(connector+numstrokefailures+" strokes "); + } + int totalfailures = numfillfailures + numstrokefailures; + if (totalfailures == 0) { + System.out.print("0 "); + } + System.out.println("out of "+numtests+" tests failed..."); + int critical = numerrors; + if (strict) { + critical += totalfailures; + } + if (critical > 0) { + throw new RuntimeException(critical+" tests had critical errors"); + } + System.out.println("No tests had critical errors"); + return (numerrors+totalfailures); + } + + public static void runRectSuite(int count) { + AnnotatedRenderOp ops[] = { + new Rect(), + new RectMethod(), + new Line(), + new LineMethod(), + }; + // Sometimes different fill algorithms are chosen for + // thin and wide line modes, make sure we test both... + float filllinewidths[] = { 0.0f, 2.0f }; + float drawlinewidths[] = { 0.0f, 0.5f, 1.0f, + 2.0f, 2.5f, + 5.0f, 5.3f }; + double rotations[] = { 0.0, 15.0, 90.0, + 135.0, 180.0, + 200.0, 270.0, + 300.0}; + for (AnnotatedRenderOp ar: ops) { + for (double r: rotations) { + rot = r; + for (int i = 0; i < 8; i++) { + float linewidths[]; + if ((i & 1) == 0) { + if ((ar instanceof Line) || + (ar instanceof LineMethod)) + { + continue; + } + testFill = true; + testDraw = false; + linewidths = filllinewidths; + } else { + testFill = false; + testDraw = true; + linewidths = drawlinewidths; + } + useAA = ((i & 2) != 0); + strokePure = ((i & 4) != 0); + for (float w : linewidths) { + lw = w; + runSuiteTests(ar, count); + } + } + } + } + } + + public static void runSuiteTests(AnnotatedRenderOp ar, int count) { + if (verbose) { + System.out.print("Running "); + System.out.print(testFill ? "Fill " : "Draw "); + System.out.print(BaseName(ar)); + if (useAA) { + System.out.print(" AA"); + } + if (strokePure) { + System.out.print(" Pure"); + } + if (lw != 1.0f) { + System.out.print(" lw="+lw); + } + if (rot != 0.0f) { + System.out.print(" rot="+rot); + } + System.out.println(); + } + initGCs(); + runRandomTests(ar, count); + disposeGCs(); + } + + public static String BaseName(AnnotatedRenderOp ar) { + String s = ar.toString(); + int leftparen = s.indexOf('('); + if (leftparen >= 0) { + s = s.substring(0, leftparen); + } + return s; + } + + public static void runRandomTests(AnnotatedRenderOp ar, int count) { + for (int i = 0; i < count; i++) { + ar.randomize(); + if (testDraw) { + test(ar, false); + } + if (testFill) { + test(ar, true); + } + } + } + + public static void initImages() { + imgref = new BufferedImage(40, 40, BufferedImage.TYPE_INT_RGB); + imgtst = new BufferedImage(40, 40, BufferedImage.TYPE_INT_RGB); + grefclear = imgref.createGraphics(); + gtstclear = imgtst.createGraphics(); + grefclear.setColor(Color.white); + gtstclear.setColor(Color.white); + } + + public static void initGCs() { + grefrender = imgref.createGraphics(); + gtstrender = imgtst.createGraphics(); + gtstrender.clipRect(10, 10, 20, 20); + grefrender.setColor(Color.blue); + gtstrender.setColor(Color.blue); + if (lw != 1.0f) { + BasicStroke bs = new BasicStroke(lw); + grefrender.setStroke(bs); + gtstrender.setStroke(bs); + } + if (rot != 0.0) { + double rotrad = Math.toRadians(rot); + grefrender.rotate(rotrad, 20, 20); + gtstrender.rotate(rotrad, 20, 20); + } + if (strokePure) { + grefrender.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, + RenderingHints.VALUE_STROKE_PURE); + gtstrender.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, + RenderingHints.VALUE_STROKE_PURE); + } + if (useAA) { + grefrender.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + gtstrender.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + maxerr = 1; + } + } + + public static void disposeGCs() { + grefrender.dispose(); + gtstrender.dispose(); + grefrender = gtstrender = null; + } + + public static void parseAndRun(InputStream in) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String str; + while ((str = br.readLine()) != null) { + if (str.startsWith("Stroked ") || str.startsWith("Filled ")) { + parseTest(str); + continue; + } + if (str.startsWith("Running ")) { + continue; + } + if (str.startsWith("Failed: ")) { + continue; + } + if (str.indexOf(" out of ") > 0 && + str.indexOf(" tests failed...") > 0) + { + continue; + } + if (str.indexOf(" tests had critical errors") > 0) { + continue; + } + System.err.println("Unparseable line: "+str); + } + } + + public static void parseTest(String origstr) { + String str = origstr; + boolean isfill = false; + useAA = strokePure = false; + lw = 1.0f; + rot = 0.0; + if (str.startsWith("Stroked ")) { + str = str.substring(8); + isfill = false; + } else if (str.startsWith("Filled ")) { + str = str.substring(7); + isfill = true; + } else { + System.err.println("Unparseable test line: "+origstr); + } + if (str.startsWith("AA ")) { + str = str.substring(3); + useAA = true; + } + if (str.startsWith("Pure ")) { + str = str.substring(5); + strokePure = true; + } + if (str.startsWith("Lw=")) { + int index = str.indexOf(' ', 3); + if (index > 0) { + lw = Float.parseFloat(str.substring(3, index)); + str = str.substring(index+1); + } + } + if (str.startsWith("Rot=")) { + int index = str.indexOf(' ', 4); + if (index > 0) { + rot = Double.parseDouble(str.substring(4, index)); + str = str.substring(index+1); + } + } + AnnotatedRenderOp ar = AnnotatedRenderOp.parse(str); + if (ar != null) { + initGCs(); + test(ar, isfill); + disposeGCs(); + } else { + System.err.println("Unparseable test line: "+origstr); + } + } + + public static void test(AnnotatedRenderOp ar, boolean isfill) { + grefclear.fillRect(0, 0, 40, 40); + gtstclear.fillRect(0, 0, 40, 40); + if (isfill) { + ar.fill(grefrender); + ar.fill(gtstrender); + } else { + ar.draw(grefrender); + ar.draw(gtstrender); + } + check(imgref, imgtst, ar, isfill); + } + + public static int[] getData(BufferedImage img) { + Raster r = img.getRaster(); + DataBufferInt dbi = (DataBufferInt) r.getDataBuffer(); + return dbi.getData(); + } + + public static int getScan(BufferedImage img) { + Raster r = img.getRaster(); + SinglePixelPackedSampleModel sppsm = + (SinglePixelPackedSampleModel) r.getSampleModel(); + return sppsm.getScanlineStride(); + } + + public static int getOffset(BufferedImage img) { + Raster r = img.getRaster(); + SinglePixelPackedSampleModel sppsm = + (SinglePixelPackedSampleModel) r.getSampleModel(); + return sppsm.getOffset(-r.getSampleModelTranslateX(), + -r.getSampleModelTranslateY()); + } + + final static int opaque = 0xff000000; + final static int whitergb = Color.white.getRGB(); + + public static final int maxdiff(int rgb1, int rgb2) { + int maxd = 0; + for (int i = 0; i < 32; i += 8) { + int c1 = (rgb1 >> i) & 0xff; + int c2 = (rgb2 >> i) & 0xff; + int d = Math.abs(c1-c2); + if (maxd < d) { + maxd = d; + } + } + return maxd; + } + + public static void check(BufferedImage imgref, BufferedImage imgtst, + AnnotatedRenderOp ar, boolean wasfill) + { + numtests++; + int dataref[] = getData(imgref); + int datatst[] = getData(imgtst); + int scanref = getScan(imgref); + int scantst = getScan(imgtst); + int offref = getOffset(imgref); + int offtst = getOffset(imgtst); + + // We want to check for errors outside the clip at a higher + // priority than errors involving different pixels touched + // inside the clip. + + // Check above clip + if (check(ar, wasfill, + null, 0, 0, + datatst, scantst, offtst, + 0, 0, 40, 10)) + { + return; + } + // Check below clip + if (check(ar, wasfill, + null, 0, 0, + datatst, scantst, offtst, + 0, 30, 40, 40)) + { + return; + } + // Check left of clip + if (check(ar, wasfill, + null, 0, 0, + datatst, scantst, offtst, + 0, 10, 10, 30)) + { + return; + } + // Check right of clip + if (check(ar, wasfill, + null, 0, 0, + datatst, scantst, offtst, + 30, 10, 40, 30)) + { + return; + } + // Check inside clip + check(ar, wasfill, + dataref, scanref, offref, + datatst, scantst, offtst, + 10, 10, 30, 30); + } + + public static boolean check(AnnotatedRenderOp ar, boolean wasfill, + int dataref[], int scanref, int offref, + int datatst[], int scantst, int offtst, + int x0, int y0, int x1, int y1) + { + offref += scanref * y0; + offtst += scantst * y0; + for (int y = y0; y < y1; y++) { + for (int x = x0; x < x1; x++) { + boolean failed; + String reason; + int rgbref; + int rgbtst; + + rgbtst = datatst[offtst+x] | opaque; + if (dataref == null) { + /* Outside of clip, must be white, no error tolerance */ + rgbref = whitergb; + failed = (rgbtst != rgbref); + reason = "stray pixel rendered outside of clip"; + } else { + /* Inside of clip, check for maxerr delta in components */ + rgbref = dataref[offref+x] | opaque; + failed = (rgbref != rgbtst && + maxdiff(rgbref, rgbtst) > maxerr); + reason = "different pixel rendered inside clip"; + } + if (failed) { + if (dataref == null) { + numerrors++; + } + if (wasfill) { + numfillfailures++; + } else { + numstrokefailures++; + } + if (!silent) { + System.out.println("Failed: "+reason+" at "+x+", "+y+ + " ["+Integer.toHexString(rgbref)+ + " != "+Integer.toHexString(rgbtst)+ + "]"); + System.out.print(wasfill ? "Filled " : "Stroked "); + if (useAA) System.out.print("AA "); + if (strokePure) System.out.print("Pure "); + if (lw != 1) System.out.print("Lw="+lw+" "); + if (rot != 0) System.out.print("Rot="+rot+" "); + System.out.println(ar); + } + if (showErrors) { + show(imgref, imgtst); + } + return true; + } + } + offref += scanref; + offtst += scantst; + } + return false; + } + + static ErrorWindow errw; + + public static void show(BufferedImage imgref, BufferedImage imgtst) { + ErrorWindow errw = new ErrorWindow(); + errw.setImages(imgref, imgtst); + errw.setVisible(true); + errw.waitForHide(); + errw.dispose(); + } + + public static class Cubic extends AnnotatedShapeOp { + public static Cubic tryparse(String str) { + str = str.trim(); + if (!str.startsWith("Cubic(")) { + return null; + } + str = str.substring(6); + double coords[] = new double[8]; + boolean foundparen = false; + for (int i = 0; i < coords.length; i++) { + int index = str.indexOf(","); + if (index < 0) { + if (i < coords.length-1) { + return null; + } + index = str.indexOf(")"); + if (index < 0) { + return null; + } + foundparen = true; + } + String num = str.substring(0, index); + try { + coords[i] = Double.parseDouble(num); + } catch (NumberFormatException nfe) { + return null; + } + str = str.substring(index+1); + } + if (!foundparen || str.length() > 0) { + return null; + } + Cubic c = new Cubic(); + c.cubic.setCurve(coords[0], coords[1], + coords[2], coords[3], + coords[4], coords[5], + coords[6], coords[7]); + return c; + } + + private CubicCurve2D cubic = new CubicCurve2D.Double(); + + public void randomize() { + cubic.setCurve(randDblCoord(), randDblCoord(), + randDblCoord(), randDblCoord(), + randDblCoord(), randDblCoord(), + randDblCoord(), randDblCoord()); + } + + public Shape getShape() { + return cubic; + } + + public String toString() { + return ("Cubic("+ + cubic.getX1()+", "+ + cubic.getY1()+", "+ + cubic.getCtrlX1()+", "+ + cubic.getCtrlY1()+", "+ + cubic.getCtrlX2()+", "+ + cubic.getCtrlY2()+", "+ + cubic.getX2()+", "+ + cubic.getY2() + +")"); + } + } + + public static class Quad extends AnnotatedShapeOp { + public static Quad tryparse(String str) { + str = str.trim(); + if (!str.startsWith("Quad(")) { + return null; + } + str = str.substring(5); + double coords[] = new double[6]; + boolean foundparen = false; + for (int i = 0; i < coords.length; i++) { + int index = str.indexOf(","); + if (index < 0) { + if (i < coords.length-1) { + return null; + } + index = str.indexOf(")"); + if (index < 0) { + return null; + } + foundparen = true; + } + String num = str.substring(0, index); + try { + coords[i] = Double.parseDouble(num); + } catch (NumberFormatException nfe) { + return null; + } + str = str.substring(index+1); + } + if (!foundparen || str.length() > 0) { + return null; + } + Quad c = new Quad(); + c.quad.setCurve(coords[0], coords[1], + coords[2], coords[3], + coords[4], coords[5]); + return c; + } + + private QuadCurve2D quad = new QuadCurve2D.Double(); + + public void randomize() { + quad.setCurve(randDblCoord(), randDblCoord(), + randDblCoord(), randDblCoord(), + randDblCoord(), randDblCoord()); + } + + public Shape getShape() { + return quad; + } + + public String toString() { + return ("Quad("+ + quad.getX1()+", "+ + quad.getY1()+", "+ + quad.getCtrlX()+", "+ + quad.getCtrlY()+", "+ + quad.getX2()+", "+ + quad.getY2() + +")"); + } + } + + public static class Poly extends AnnotatedShapeOp { + public static Poly tryparse(String str) { + str = str.trim(); + if (!str.startsWith("Poly(")) { + return null; + } + str = str.substring(5); + Polygon p = new Polygon(); + while (true) { + int x, y; + str = str.trim(); + if (str.startsWith(")")) { + str = str.substring(1); + break; + } + if (p.npoints > 0) { + if (str.startsWith(",")) { + str = str.substring(2).trim(); + } else { + return null; + } + } + if (str.startsWith("[")) { + str = str.substring(1); + } else { + return null; + } + int index = str.indexOf(","); + if (index < 0) { + return null; + } + String num = str.substring(0, index); + try { + x = Integer.parseInt(num); + } catch (NumberFormatException nfe) { + return null; + } + str = str.substring(index+1); + index = str.indexOf("]"); + if (index < 0) { + return null; + } + num = str.substring(0, index).trim(); + try { + y = Integer.parseInt(num); + } catch (NumberFormatException nfe) { + return null; + } + str = str.substring(index+1); + p.addPoint(x, y); + } + if (str.length() > 0) { + return null; + } + if (p.npoints < 3) { + return null; + } + return new Poly(p); + } + + private Polygon poly; + + public Poly() { + this.poly = new Polygon(); + } + + private Poly(Polygon p) { + this.poly = p; + } + + public void randomize() { + poly.reset(); + poly.addPoint(randIntCoord(), randIntCoord()); + poly.addPoint(randIntCoord(), randIntCoord()); + poly.addPoint(randIntCoord(), randIntCoord()); + poly.addPoint(randIntCoord(), randIntCoord()); + poly.addPoint(randIntCoord(), randIntCoord()); + } + + public Shape getShape() { + return poly; + } + + public String toString() { + StringBuffer sb = new StringBuffer(100); + sb.append("Poly("); + for (int i = 0; i < poly.npoints; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append("["); + sb.append(poly.xpoints[i]); + sb.append(", "); + sb.append(poly.ypoints[i]); + sb.append("]"); + } + sb.append(")"); + return sb.toString(); + } + } + + public static class Path extends AnnotatedShapeOp { + public static Path tryparse(String str) { + str = str.trim(); + if (!str.startsWith("Path(")) { + return null; + } + str = str.substring(5); + GeneralPath gp = new GeneralPath(); + float coords[] = new float[6]; + int numsegs = 0; + while (true) { + int type; + int n; + str = str.trim(); + if (str.startsWith(")")) { + str = str.substring(1); + break; + } + if (str.startsWith("M[")) { + type = PathIterator.SEG_MOVETO; + n = 2; + } else if (str.startsWith("L[")) { + type = PathIterator.SEG_LINETO; + n = 2; + } else if (str.startsWith("Q[")) { + type = PathIterator.SEG_QUADTO; + n = 4; + } else if (str.startsWith("C[")) { + type = PathIterator.SEG_CUBICTO; + n = 6; + } else if (str.startsWith("E[")) { + type = PathIterator.SEG_CLOSE; + n = 0; + } else { + return null; + } + str = str.substring(2); + if (n == 0) { + if (str.startsWith("]")) { + str = str.substring(1); + } else { + return null; + } + } + for (int i = 0; i < n; i++) { + int index; + if (i < n-1) { + index = str.indexOf(","); + } else { + index = str.indexOf("]"); + } + if (index < 0) { + return null; + } + String num = str.substring(0, index); + try { + coords[i] = Float.parseFloat(num); + } catch (NumberFormatException nfe) { + return null; + } + str = str.substring(index+1).trim(); + } + switch (type) { + case PathIterator.SEG_MOVETO: + gp.moveTo(coords[0], coords[1]); + break; + case PathIterator.SEG_LINETO: + gp.lineTo(coords[0], coords[1]); + break; + case PathIterator.SEG_QUADTO: + gp.quadTo(coords[0], coords[1], + coords[2], coords[3]); + break; + case PathIterator.SEG_CUBICTO: + gp.curveTo(coords[0], coords[1], + coords[2], coords[3], + coords[4], coords[5]); + break; + case PathIterator.SEG_CLOSE: + gp.closePath(); + break; + } + numsegs++; + } + if (str.length() > 0) { + return null; + } + if (numsegs < 2) { + return null; + } + return new Path(gp); + } + + private GeneralPath path; + + public Path() { + this.path = new GeneralPath(); + } + + private Path(GeneralPath gp) { + this.path = gp; + } + + public void randomize() { + path.reset(); + path.moveTo(randFltCoord(), randFltCoord()); + for (int i = randInt(5)+3; i > 0; --i) { + switch(randInt(5)) { + case 0: + path.moveTo(randFltCoord(), randFltCoord()); + break; + case 1: + path.lineTo(randFltCoord(), randFltCoord()); + break; + case 2: + path.quadTo(randFltCoord(), randFltCoord(), + randFltCoord(), randFltCoord()); + break; + case 3: + path.curveTo(randFltCoord(), randFltCoord(), + randFltCoord(), randFltCoord(), + randFltCoord(), randFltCoord()); + break; + case 4: + path.closePath(); + break; + } + } + } + + public Shape getShape() { + return path; + } + + public String toString() { + StringBuffer sb = new StringBuffer(100); + sb.append("Path("); + PathIterator pi = path.getPathIterator(null); + float coords[] = new float[6]; + boolean first = true; + while (!pi.isDone()) { + int n; + char c; + switch(pi.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + c = 'M'; + n = 2; + break; + case PathIterator.SEG_LINETO: + c = 'L'; + n = 2; + break; + case PathIterator.SEG_QUADTO: + c = 'Q'; + n = 4; + break; + case PathIterator.SEG_CUBICTO: + c = 'C'; + n = 6; + break; + case PathIterator.SEG_CLOSE: + c = 'E'; + n = 0; + break; + default: + throw new InternalError("Unknown segment!"); + } + sb.append(c); + sb.append("["); + for (int i = 0; i < n; i++) { + if (i != 0) { + sb.append(","); + } + sb.append(coords[i]); + } + sb.append("]"); + pi.next(); + } + sb.append(")"); + return sb.toString(); + } + } + + public static class Rect extends AnnotatedShapeOp { + public static Rect tryparse(String str) { + str = str.trim(); + if (!str.startsWith("Rect(")) { + return null; + } + str = str.substring(5); + double coords[] = new double[4]; + boolean foundparen = false; + for (int i = 0; i < coords.length; i++) { + int index = str.indexOf(","); + if (index < 0) { + if (i < coords.length-1) { + return null; + } + index = str.indexOf(")"); + if (index < 0) { + return null; + } + foundparen = true; + } + String num = str.substring(0, index); + try { + coords[i] = Double.parseDouble(num); + } catch (NumberFormatException nfe) { + return null; + } + str = str.substring(index+1); + } + if (!foundparen || str.length() > 0) { + return null; + } + Rect r = new Rect(); + r.rect.setRect(coords[0], coords[1], + coords[2], coords[3]); + return r; + } + + private Rectangle2D rect = new Rectangle2D.Double(); + + public void randomize() { + rect.setRect(randDblCoord(), randDblCoord(), + randDblCoord(), randDblCoord()); + } + + public Shape getShape() { + return rect; + } + + public String toString() { + return ("Rect("+ + rect.getX()+", "+ + rect.getY()+", "+ + rect.getWidth()+", "+ + rect.getHeight() + +")"); + } + } + + public static class Line extends AnnotatedShapeOp { + public static Line tryparse(String str) { + str = str.trim(); + if (!str.startsWith("Line(")) { + return null; + } + str = str.substring(5); + double coords[] = new double[4]; + boolean foundparen = false; + for (int i = 0; i < coords.length; i++) { + int index = str.indexOf(","); + if (index < 0) { + if (i < coords.length-1) { + return null; + } + index = str.indexOf(")"); + if (index < 0) { + return null; + } + foundparen = true; + } + String num = str.substring(0, index); + try { + coords[i] = Double.parseDouble(num); + } catch (NumberFormatException nfe) { + return null; + } + str = str.substring(index+1); + } + if (!foundparen || str.length() > 0) { + return null; + } + Line l = new Line(); + l.line.setLine(coords[0], coords[1], + coords[2], coords[3]); + return l; + } + + private Line2D line = new Line2D.Double(); + + public void randomize() { + line.setLine(randDblCoord(), randDblCoord(), + randDblCoord(), randDblCoord()); + } + + public Shape getShape() { + return line; + } + + public String toString() { + return ("Line("+ + line.getX1()+", "+ + line.getY1()+", "+ + line.getX2()+", "+ + line.getY2() + +")"); + } + } + + public static class RectMethod extends AnnotatedRenderOp { + public static RectMethod tryparse(String str) { + str = str.trim(); + if (!str.startsWith("RectMethod(")) { + return null; + } + str = str.substring(11); + int coords[] = new int[4]; + boolean foundparen = false; + for (int i = 0; i < coords.length; i++) { + int index = str.indexOf(","); + if (index < 0) { + if (i < coords.length-1) { + return null; + } + index = str.indexOf(")"); + if (index < 0) { + return null; + } + foundparen = true; + } + String num = str.substring(0, index).trim(); + try { + coords[i] = Integer.parseInt(num); + } catch (NumberFormatException nfe) { + return null; + } + str = str.substring(index+1); + } + if (!foundparen || str.length() > 0) { + return null; + } + RectMethod rm = new RectMethod(); + rm.rect.setBounds(coords[0], coords[1], + coords[2], coords[3]); + return rm; + } + + private Rectangle rect = new Rectangle(); + + public void randomize() { + rect.setBounds(randIntCoord(), randIntCoord(), + randIntCoord(), randIntCoord()); + } + + public void fill(Graphics2D g2d) { + g2d.fillRect(rect.x, rect.y, rect.width, rect.height); + } + + public void draw(Graphics2D g2d) { + g2d.drawRect(rect.x, rect.y, rect.width, rect.height); + } + + public String toString() { + return ("RectMethod("+ + rect.x+", "+ + rect.y+", "+ + rect.width+", "+ + rect.height + +")"); + } + } + + public static class LineMethod extends AnnotatedRenderOp { + public static LineMethod tryparse(String str) { + str = str.trim(); + if (!str.startsWith("LineMethod(")) { + return null; + } + str = str.substring(11); + int coords[] = new int[4]; + boolean foundparen = false; + for (int i = 0; i < coords.length; i++) { + int index = str.indexOf(","); + if (index < 0) { + if (i < coords.length-1) { + return null; + } + index = str.indexOf(")"); + if (index < 0) { + return null; + } + foundparen = true; + } + String num = str.substring(0, index).trim(); + try { + coords[i] = Integer.parseInt(num); + } catch (NumberFormatException nfe) { + return null; + } + str = str.substring(index+1); + } + if (!foundparen || str.length() > 0) { + return null; + } + LineMethod lm = new LineMethod(); + lm.line = coords; + return lm; + } + + private int line[] = new int[4]; + + public void randomize() { + line[0] = randIntCoord(); + line[1] = randIntCoord(); + line[2] = randIntCoord(); + line[3] = randIntCoord(); + } + + public void fill(Graphics2D g2d) { + } + + public void draw(Graphics2D g2d) { + g2d.drawLine(line[0], line[1], line[2], line[3]); + } + + public String toString() { + return ("LineMethod("+ + line[0]+", "+ + line[1]+", "+ + line[2]+", "+ + line[3] + +")"); + } + } + + public static class ErrorWindow extends Frame { + ImageCanvas unclipped; + ImageCanvas reference; + ImageCanvas actual; + ImageCanvas diff; + + public ErrorWindow() { + super("Error Comparison Window"); + + unclipped = new ImageCanvas(); + reference = new ImageCanvas(); + actual = new ImageCanvas(); + diff = new ImageCanvas(); + + setLayout(new SmartGridLayout(0, 2, 5, 5)); + addImagePanel(unclipped, "Unclipped rendering"); + addImagePanel(reference, "Clipped reference"); + addImagePanel(actual, "Actual clipped"); + addImagePanel(diff, "Difference"); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + setVisible(false); + } + }); + } + + public void addImagePanel(ImageCanvas ic, String label) { + add(ic); + add(new Label(label)); + } + + public void setImages(BufferedImage imgref, BufferedImage imgtst) { + unclipped.setImage(imgref); + reference.setReference(imgref); + actual.setImage(imgtst); + diff.setDiff(reference.getImage(), imgtst); + invalidate(); + pack(); + repaint(); + } + + public void setVisible(boolean vis) { + super.setVisible(vis); + synchronized (this) { + notifyAll(); + } + } + + public synchronized void waitForHide() { + while (isShowing()) { + try { + wait(); + } catch (InterruptedException e) { + System.exit(2); + } + } + } + } + + public static class SmartGridLayout implements LayoutManager { + int rows; + int cols; + int hgap; + int vgap; + + public SmartGridLayout(int r, int c, int h, int v) { + this.rows = r; + this.cols = c; + this.hgap = h; + this.vgap = v; + } + + public void addLayoutComponent(String name, Component comp) { + } + + public void removeLayoutComponent(Component comp) { + } + + public int[][] getGridSizes(Container parent, boolean min) { + int ncomponents = parent.getComponentCount(); + int nrows = rows; + int ncols = cols; + + if (nrows > 0) { + ncols = (ncomponents + nrows - 1) / nrows; + } else { + nrows = (ncomponents + ncols - 1) / ncols; + } + int widths[] = new int[ncols+1]; + int heights[] = new int[nrows+1]; + int x = 0; + int y = 0; + for (int i = 0 ; i < ncomponents ; i++) { + Component comp = parent.getComponent(i); + Dimension d = (min + ? comp.getMinimumSize() + : comp.getPreferredSize()); + if (widths[x] < d.width) { + widths[x] = d.width; + } + if (heights[y] < d.height) { + heights[y] = d.height; + } + x++; + if (x >= ncols) { + x = 0; + y++; + } + } + for (int i = 0; i < ncols; i++) { + widths[ncols] += widths[i]; + } + for (int i = 0; i < nrows; i++) { + heights[nrows] += heights[i]; + } + return new int[][] { widths, heights }; + } + + public Dimension getSize(Container parent, boolean min) { + int sizes[][] = getGridSizes(parent, min); + int widths[] = sizes[0]; + int heights[] = sizes[1]; + int nrows = heights.length-1; + int ncols = widths.length-1; + int w = widths[ncols]; + int h = heights[nrows]; + Insets insets = parent.getInsets(); + return new Dimension(insets.left+insets.right + w+(ncols+1)*hgap, + insets.top+insets.bottom + h+(nrows+1)*vgap); + } + + public Dimension preferredLayoutSize(Container parent) { + return getSize(parent, false); + } + + public Dimension minimumLayoutSize(Container parent) { + return getSize(parent, true); + } + + public void layoutContainer(Container parent) { + int pref[][] = getGridSizes(parent, false); + int min[][] = getGridSizes(parent, true); + int minwidths[] = min[0]; + int minheights[] = min[1]; + int prefwidths[] = pref[0]; + int prefheights[] = pref[1]; + int nrows = minheights.length - 1; + int ncols = minwidths.length - 1; + Insets insets = parent.getInsets(); + int w = parent.getWidth() - insets.left - insets.right; + int h = parent.getHeight() - insets.top - insets.bottom; + w = w - (ncols+1)*hgap; + h = h - (nrows+1)*vgap; + int widths[] = calculateSizes(w, ncols, minwidths, prefwidths); + int heights[] = calculateSizes(h, nrows, minheights, prefheights); + int ncomponents = parent.getComponentCount(); + int x = insets.left + hgap; + int y = insets.top + vgap; + int r = 0; + int c = 0; + for (int i = 0; i < ncomponents; i++) { + parent.getComponent(i).setBounds(x, y, widths[c], heights[r]); + x += widths[c++] + hgap; + if (c >= ncols) { + c = 0; + x = insets.left + hgap; + y += heights[r++] + vgap; + if (r >= nrows) { + // just in case + break; + } + } + } + } + + public static int[] calculateSizes(int total, int num, + int minsizes[], int prefsizes[]) + { + if (total <= minsizes[num]) { + return minsizes; + } + if (total >= prefsizes[num]) { + return prefsizes; + } + int sizes[] = new int[total]; + int prevhappy = 0; + int nhappy = 0; + int happysize = 0; + do { + int addsize = (total - happysize) / (num - nhappy); + happysize = 0; + for (int i = 0; i < num; i++) { + if (sizes[i] >= prefsizes[i] || + minsizes[i] + addsize > prefsizes[i]) + { + happysize += (sizes[i] = prefsizes[i]); + nhappy++; + } else { + sizes[i] = minsizes[i] + addsize; + } + } + } while (nhappy < num && nhappy > prevhappy); + return sizes; + } + } + + public static class ImageCanvas extends Canvas { + BufferedImage image; + + public void setImage(BufferedImage img) { + this.image = img; + } + + public BufferedImage getImage() { + return image; + } + + public void checkImage(int w, int h) { + if (image == null || + image.getWidth() < w || + image.getHeight() < h) + { + image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); + } + } + + public void setReference(BufferedImage img) { + checkImage(img.getWidth(), img.getHeight()); + Graphics g = image.createGraphics(); + g.drawImage(img, 0, 0, null); + g.setColor(Color.white); + g.fillRect(0, 0, 30, 10); + g.fillRect(30, 0, 10, 30); + g.fillRect(10, 30, 30, 10); + g.fillRect(0, 10, 10, 30); + g.dispose(); + } + + public void setDiff(BufferedImage imgref, BufferedImage imgtst) { + int w = Math.max(imgref.getWidth(), imgtst.getWidth()); + int h = Math.max(imgref.getHeight(), imgtst.getHeight()); + checkImage(w, h); + Graphics g = image.createGraphics(); + g.drawImage(imgref, 0, 0, null); + g.setXORMode(Color.white); + g.drawImage(imgtst, 0, 0, null); + g.setPaintMode(); + g.setColor(new Color(1f, 1f, 0f, 0.25f)); + g.fillRect(10, 10, 20, 20); + g.setColor(new Color(1f, 0f, 0f, 0.25f)); + g.fillRect(0, 0, 30, 10); + g.fillRect(30, 0, 10, 30); + g.fillRect(10, 30, 30, 10); + g.fillRect(0, 10, 10, 30); + g.dispose(); + } + + public Dimension getPreferredSize() { + if (image == null) { + return new Dimension(); + } else { + return new Dimension(image.getWidth(), image.getHeight()); + } + } + + public void paint(Graphics g) { + g.drawImage(image, 0, 0, null); + } + } +}