--- a/jdk/make/sun/awt/make.depend Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/make/sun/awt/make.depend Tue Dec 14 13:25:29 2010 -0800
@@ -224,7 +224,7 @@
$(OBJDIR)/DrawLine.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h $(CLASSHDRDIR)/sun_java2d_loops_DrawLine.h ../../../src/share/javavm/export/jni.h ../../../src/share/native/common/gdefs.h ../../../src/share/native/sun/java2d/loops/AlphaMath.h ../../../src/share/native/sun/java2d/loops/GlyphImageRef.h ../../../src/share/native/sun/java2d/loops/GraphicsPrimitiveMgr.h ../../../src/share/native/sun/java2d/loops/LineUtils.h ../../../src/share/native/sun/java2d/pipe/SpanIterator.h ../../../src/share/native/sun/java2d/SurfaceData.h ../../../src/windows/javavm/export/jni_md.h ../../../src/windows/native/common/gdefs_md.h ../../../src/windows/native/sun/java2d/j2d_md.h
-$(OBJDIR)/DrawParallelogram.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h $(CLASSHDRDIR)/sun_java2d_loops_DrawParallelogram.h $(CLASSHDRDIR)/sun_java2d_loops_FillParallelogram.h ../../../src/share/javavm/export/classfile_constants.h ../../../src/share/javavm/export/jni.h ../../../src/share/javavm/export/jvm.h ../../../src/share/native/common/gdefs.h ../../../src/share/native/common/jlong.h ../../../src/share/native/common/jni_util.h ../../../src/share/native/sun/awt/debug/debug_assert.h ../../../src/share/native/sun/awt/debug/debug_mem.h ../../../src/share/native/sun/awt/debug/debug_trace.h ../../../src/share/native/sun/awt/debug/debug_util.h ../../../src/share/native/sun/java2d/loops/AlphaMath.h ../../../src/share/native/sun/java2d/loops/GlyphImageRef.h ../../../src/share/native/sun/java2d/loops/GraphicsPrimitiveMgr.h ../../../src/share/native/sun/java2d/loops/LineUtils.h ../../../src/share/native/sun/java2d/loops/LoopMacros.h ../../../src/share/native/sun/java2d/pipe/SpanIterator.h ../../../src/share/native/sun/java2d/SurfaceData.h ../../../src/share/native/sun/java2d/Trace.h ../../../src/windows/javavm/export/jni_md.h ../../../src/windows/javavm/export/jvm_md.h ../../../src/windows/native/common/gdefs_md.h ../../../src/windows/native/common/jlong_md.h ../../../src/windows/native/sun/java2d/j2d_md.h
+$(OBJDIR)/DrawParallelogram.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h $(CLASSHDRDIR)/sun_java2d_loops_DrawParallelogram.h ../../../src/share/javavm/export/classfile_constants.h ../../../src/share/javavm/export/jni.h ../../../src/share/javavm/export/jvm.h ../../../src/share/native/common/gdefs.h ../../../src/share/native/common/jlong.h ../../../src/share/native/common/jni_util.h ../../../src/share/native/sun/awt/debug/debug_assert.h ../../../src/share/native/sun/awt/debug/debug_mem.h ../../../src/share/native/sun/awt/debug/debug_trace.h ../../../src/share/native/sun/awt/debug/debug_util.h ../../../src/share/native/sun/java2d/loops/AlphaMath.h ../../../src/share/native/sun/java2d/loops/GlyphImageRef.h ../../../src/share/native/sun/java2d/loops/GraphicsPrimitiveMgr.h ../../../src/share/native/sun/java2d/loops/LineUtils.h ../../../src/share/native/sun/java2d/loops/ParallelogramUtils.h ../../../src/share/native/sun/java2d/pipe/SpanIterator.h ../../../src/share/native/sun/java2d/SurfaceData.h ../../../src/share/native/sun/java2d/Trace.h ../../../src/windows/javavm/export/jni_md.h ../../../src/windows/javavm/export/jvm_md.h ../../../src/windows/native/common/gdefs_md.h ../../../src/windows/native/common/jlong_md.h ../../../src/windows/native/sun/java2d/j2d_md.h
$(OBJDIR)/DrawPath.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h $(CLASSHDRDIR)/sun_java2d_loops_DrawPath.h ../../../src/share/javavm/export/jni.h ../../../src/share/native/common/gdefs.h ../../../src/share/native/common/jlong.h ../../../src/share/native/common/jni_util.h ../../../src/share/native/sun/java2d/loops/AlphaMath.h ../../../src/share/native/sun/java2d/loops/DrawPath.h ../../../src/share/native/sun/java2d/loops/GlyphImageRef.h ../../../src/share/native/sun/java2d/loops/GraphicsPrimitiveMgr.h ../../../src/share/native/sun/java2d/loops/LineUtils.h ../../../src/share/native/sun/java2d/loops/ProcessPath.h ../../../src/share/native/sun/java2d/pipe/SpanIterator.h ../../../src/share/native/sun/java2d/SurfaceData.h ../../../src/windows/javavm/export/jni_md.h ../../../src/windows/native/common/gdefs_md.h ../../../src/windows/native/common/jlong_md.h ../../../src/windows/native/sun/java2d/j2d_md.h
@@ -232,7 +232,7 @@
$(OBJDIR)/DrawRect.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h $(CLASSHDRDIR)/sun_java2d_loops_DrawRect.h ../../../src/share/javavm/export/jni.h ../../../src/share/native/common/gdefs.h ../../../src/share/native/sun/java2d/loops/AlphaMath.h ../../../src/share/native/sun/java2d/loops/GlyphImageRef.h ../../../src/share/native/sun/java2d/loops/GraphicsPrimitiveMgr.h ../../../src/share/native/sun/java2d/loops/LineUtils.h ../../../src/share/native/sun/java2d/pipe/SpanIterator.h ../../../src/share/native/sun/java2d/SurfaceData.h ../../../src/windows/javavm/export/jni_md.h ../../../src/windows/native/common/gdefs_md.h ../../../src/windows/native/sun/java2d/j2d_md.h
-$(OBJDIR)/FillParallelogram.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h $(CLASSHDRDIR)/sun_java2d_loops_FillParallelogram.h ../../../src/share/javavm/export/jni.h ../../../src/share/native/common/gdefs.h ../../../src/share/native/sun/java2d/loops/AlphaMath.h ../../../src/share/native/sun/java2d/loops/GlyphImageRef.h ../../../src/share/native/sun/java2d/loops/GraphicsPrimitiveMgr.h ../../../src/share/native/sun/java2d/pipe/SpanIterator.h ../../../src/share/native/sun/java2d/SurfaceData.h ../../../src/windows/javavm/export/jni_md.h ../../../src/windows/native/common/gdefs_md.h ../../../src/windows/native/sun/java2d/j2d_md.h
+$(OBJDIR)/FillParallelogram.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h $(CLASSHDRDIR)/sun_java2d_loops_FillParallelogram.h ../../../src/share/javavm/export/jni.h ../../../src/share/native/common/gdefs.h ../../../src/share/native/sun/java2d/loops/AlphaMath.h ../../../src/share/native/sun/java2d/loops/GlyphImageRef.h ../../../src/share/native/sun/java2d/loops/GraphicsPrimitiveMgr.h ../../../src/share/native/sun/java2d/loops/ParallelogramUtils.h ../../../src/share/native/sun/java2d/pipe/SpanIterator.h ../../../src/share/native/sun/java2d/SurfaceData.h ../../../src/windows/javavm/export/jni_md.h ../../../src/windows/native/common/gdefs_md.h ../../../src/windows/native/sun/java2d/j2d_md.h
$(OBJDIR)/FillPath.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h $(CLASSHDRDIR)/sun_java2d_loops_FillPath.h ../../../src/share/javavm/export/jni.h ../../../src/share/native/common/gdefs.h ../../../src/share/native/common/jlong.h ../../../src/share/native/common/jni_util.h ../../../src/share/native/sun/java2d/loops/AlphaMath.h ../../../src/share/native/sun/java2d/loops/DrawPath.h ../../../src/share/native/sun/java2d/loops/GlyphImageRef.h ../../../src/share/native/sun/java2d/loops/GraphicsPrimitiveMgr.h ../../../src/share/native/sun/java2d/loops/LineUtils.h ../../../src/share/native/sun/java2d/loops/ProcessPath.h ../../../src/share/native/sun/java2d/pipe/SpanIterator.h ../../../src/share/native/sun/java2d/SurfaceData.h ../../../src/windows/javavm/export/jni_md.h ../../../src/windows/native/common/gdefs_md.h ../../../src/windows/native/common/jlong_md.h ../../../src/windows/native/sun/java2d/j2d_md.h
@@ -284,7 +284,7 @@
$(OBJDIR)/MaskBlit.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h $(CLASSHDRDIR)/sun_java2d_loops_MaskBlit.h ../../../src/share/javavm/export/jni.h ../../../src/share/native/common/gdefs.h ../../../src/share/native/sun/java2d/loops/AlphaMath.h ../../../src/share/native/sun/java2d/loops/GlyphImageRef.h ../../../src/share/native/sun/java2d/loops/GraphicsPrimitiveMgr.h ../../../src/share/native/sun/java2d/pipe/Region.h ../../../src/share/native/sun/java2d/pipe/SpanIterator.h ../../../src/share/native/sun/java2d/SurfaceData.h ../../../src/windows/javavm/export/jni_md.h ../../../src/windows/native/common/gdefs_md.h ../../../src/windows/native/sun/awt/utility/rect.h ../../../src/windows/native/sun/java2d/j2d_md.h
-$(OBJDIR)/MaskFill.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h $(CLASSHDRDIR)/sun_java2d_loops_MaskFill.h ../../../src/share/javavm/export/jni.h ../../../src/share/native/common/gdefs.h ../../../src/share/native/sun/java2d/loops/AlphaMath.h ../../../src/share/native/sun/java2d/loops/GlyphImageRef.h ../../../src/share/native/sun/java2d/loops/GraphicsPrimitiveMgr.h ../../../src/share/native/sun/java2d/pipe/SpanIterator.h ../../../src/share/native/sun/java2d/SurfaceData.h ../../../src/windows/javavm/export/jni_md.h ../../../src/windows/native/common/gdefs_md.h ../../../src/windows/native/sun/java2d/j2d_md.h
+$(OBJDIR)/MaskFill.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h $(CLASSHDRDIR)/sun_java2d_loops_MaskFill.h ../../../src/share/javavm/export/jni.h ../../../src/share/native/common/gdefs.h ../../../src/share/native/sun/java2d/loops/AlphaMath.h ../../../src/share/native/sun/java2d/loops/GlyphImageRef.h ../../../src/share/native/sun/java2d/loops/GraphicsPrimitiveMgr.h ../../../src/share/native/sun/java2d/loops/ParallelogramUtils.h ../../../src/share/native/sun/java2d/pipe/SpanIterator.h ../../../src/share/native/sun/java2d/SurfaceData.h ../../../src/windows/javavm/export/jni_md.h ../../../src/windows/native/common/gdefs_md.h ../../../src/windows/native/sun/java2d/j2d_md.h
$(OBJDIR)/MouseInfo.obj:: $(CLASSHDRDIR)/java_awt_AWTEvent.h $(CLASSHDRDIR)/java_awt_Component.h $(CLASSHDRDIR)/java_awt_Dimension.h $(CLASSHDRDIR)/java_awt_Event.h $(CLASSHDRDIR)/java_awt_event_FocusEvent.h $(CLASSHDRDIR)/java_awt_event_KeyEvent.h $(CLASSHDRDIR)/java_awt_event_MouseEvent.h $(CLASSHDRDIR)/java_awt_event_WindowEvent.h $(CLASSHDRDIR)/java_awt_Font.h $(CLASSHDRDIR)/sun_awt_FontDescriptor.h $(CLASSHDRDIR)/sun_awt_PlatformFont.h $(CLASSHDRDIR)/sun_awt_windows_WComponentPeer.h $(CLASSHDRDIR)/sun_awt_windows_WFontMetrics.h $(CLASSHDRDIR)/sun_awt_windows_WObjectPeer.h $(CLASSHDRDIR)/sun_awt_windows_WToolkit.h ../../../src/share/javavm/export/classfile_constants.h ../../../src/share/javavm/export/jni.h ../../../src/share/javavm/export/jvm.h ../../../src/share/native/common/jlong.h ../../../src/share/native/common/jni_util.h ../../../src/share/native/sun/awt/debug/debug_assert.h ../../../src/share/native/sun/awt/debug/debug_mem.h ../../../src/share/native/sun/awt/debug/debug_trace.h ../../../src/share/native/sun/awt/debug/debug_util.h ../../../src/share/native/sun/awt/image/cvutils/img_globals.h ../../../src/share/native/sun/java2d/SurfaceData.h ../../../src/share/native/sun/java2d/Trace.h ../../../src/windows/javavm/export/jni_md.h ../../../src/windows/javavm/export/jvm_md.h ../../../src/windows/native/common/jlong_md.h ../../../src/windows/native/sun/java2d/windows/GDIWindowSurfaceData.h ../../../src/windows/native/sun/windows/alloc.h ../../../src/windows/native/sun/windows/awt.h ../../../src/windows/native/sun/windows/awtmsg.h ../../../src/windows/native/sun/windows/awt_Brush.h ../../../src/windows/native/sun/windows/awt_Component.h ../../../src/windows/native/sun/windows/awt_Debug.h ../../../src/windows/native/sun/windows/awt_Font.h ../../../src/windows/native/sun/windows/awt_GDIObject.h ../../../src/windows/native/sun/windows/awt_Object.h ../../../src/windows/native/sun/windows/awt_Palette.h ../../../src/windows/native/sun/windows/awt_Pen.h ../../../src/windows/native/sun/windows/awt_Toolkit.h ../../../src/windows/native/sun/windows/awt_Win32GraphicsDevice.h ../../../src/windows/native/sun/windows/colordata.h ../../../src/windows/native/sun/windows/Devices.h ../../../src/windows/native/sun/windows/GDIHashtable.h ../../../src/windows/native/sun/windows/Hashtable.h ../../../src/windows/native/sun/windows/ObjectList.h ../../../src/windows/native/sun/windows/stdhdrs.h
--- a/jdk/make/sun/awt/mapfile-vers Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/make/sun/awt/mapfile-vers Tue Dec 14 13:25:29 2010 -0800
@@ -118,6 +118,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;
--- a/jdk/make/sun/awt/mapfile-vers-linux Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/make/sun/awt/mapfile-vers-linux Tue Dec 14 13:25:29 2010 -0800
@@ -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_SpanClipRenderer_initIDs;
sun_awt_image_GifImageDecoder_initIDs;
--- a/jdk/src/share/classes/sun/dc/DuctusRenderingEngine.java Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/share/classes/sun/dc/DuctusRenderingEngine.java Tue Dec 14 13:25:29 2010 -0800
@@ -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();
--- a/jdk/src/share/classes/sun/java2d/SurfaceData.java Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/share/classes/sun/java2d/SurfaceData.java Tue Dec 14 13:25:29 2010 -0800
@@ -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);
--- a/jdk/src/share/classes/sun/java2d/loops/CompositeType.java Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/share/classes/sun/java2d/loops/CompositeType.java Tue Dec 14 13:25:29 2010 -0800
@@ -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() {
--- a/jdk/src/share/classes/sun/java2d/loops/MaskFill.java Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/share/classes/sun/java2d/loops/MaskFill.java Tue Dec 14 13:25:29 2010 -0800
@@ -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();
+ }
}
}
--- a/jdk/src/share/classes/sun/java2d/pipe/AAShapePipe.java Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/share/classes/sun/java2d/pipe/AAShapePipe.java Tue Dec 14 13:25:29 2010 -0800
@@ -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],
--- a/jdk/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java Tue Dec 14 13:25:29 2010 -0800
@@ -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);
+ }
}
--- a/jdk/src/share/classes/sun/java2d/pipe/RenderingEngine.java Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/share/classes/sun/java2d/pipe/RenderingEngine.java Tue Dec 14 13:25:29 2010 -0800
@@ -281,6 +281,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
@@ -393,5 +459,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);
+ }
}
}
--- a/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java Tue Dec 14 13:25:29 2010 -0800
@@ -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.
--- a/jdk/src/share/native/sun/java2d/loops/DrawParallelogram.c Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/share/native/sun/java2d/loops/DrawParallelogram.c Tue Dec 14 13:25:29 2010 -0800
@@ -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.
--- a/jdk/src/share/native/sun/java2d/loops/FillParallelogram.c Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/share/native/sun/java2d/loops/FillParallelogram.c Tue Dec 14 13:25:29 2010 -0800
@@ -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);
--- a/jdk/src/share/native/sun/java2d/loops/MaskFill.c Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/share/native/sun/java2d/loops/MaskFill.c Tue Dec 14 13:25:29 2010 -0800
@@ -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);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/native/sun/java2d/loops/ParallelogramUtils.h Tue Dec 14 13:25:29 2010 -0800
@@ -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 */
--- a/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgbPre_Mask.c Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/solaris/native/sun/java2d/loops/vis_IntArgbPre_Mask.c Tue Dec 14 13:25:29 2010 -0800
@@ -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;
}
--- a/jdk/src/solaris/native/sun/java2d/loops/vis_SrcMaskFill.c Fri Dec 10 16:14:04 2010 -0800
+++ b/jdk/src/solaris/native/sun/java2d/loops/vis_SrcMaskFill.c Tue Dec 14 13:25:29 2010 -0800
@@ -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;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Graphics2D/RenderClipTest/6766342.tests Tue Dec 14 13:25:29 2010 -0800
@@ -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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Graphics2D/RenderClipTest/RenderClipTest.java Tue Dec 14 13:25:29 2010 -0800
@@ -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);
+ }
+ }
+}