提交 beff7858 编写于 作者: F flar

6766342: Improve performance of Ductus rasterizer

Reviewed-by: jgodinez, prr
上级 2dfaa02f
......@@ -224,7 +224,7 @@ $(OBJDIR)/DllUtil.obj:: ../../../src/windows/native/sun/windows/DllUtil.h
$(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)/DrawPolygons.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h $(CLASSHDR
$(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)/IntRgbx.obj:: $(CLASSHDRDIR)/java_awt_AlphaComposite.h ../../../src/sh
$(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
......
......@@ -118,6 +118,8 @@ SUNWprivate_1.1 {
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;
......
......@@ -115,6 +115,8 @@ SUNWprivate_1.1 {
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;
......
......@@ -635,6 +635,88 @@ public class DuctusRenderingEngine extends RenderingEngine {
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();
......
......@@ -367,16 +367,17 @@ public abstract class SurfaceData
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 @@ public abstract class SurfaceData
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 @@ public abstract class SurfaceData
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 @@ public abstract class SurfaceData
}
}
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 @@ public abstract class SurfaceData
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 @@ public abstract class SurfaceData
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 @@ public abstract class SurfaceData
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 @@ public abstract class SurfaceData
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 @@ public abstract class SurfaceData
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 @@ public abstract class SurfaceData
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 @@ public abstract class SurfaceData
}
}
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 @@ public abstract class SurfaceData
* 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 abstract class SurfaceData
*/
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);
......
......@@ -27,6 +27,7 @@ package sun.java2d.loops;
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 @@ import java.awt.AlphaComposite;
* 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
......@@ -152,6 +158,22 @@ public final class CompositeType {
public static final CompositeType
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 @@ public final class CompositeType {
}
}
private static int unusedUID = 1;
private int uniqueID;
private String desc;
private CompositeType next;
......@@ -218,14 +239,20 @@ public final class CompositeType {
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() {
......
......@@ -50,6 +50,10 @@ import sun.java2d.SurfaceData;
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 @@ public class MaskFill extends GraphicsPrimitive
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 @@ public class MaskFill extends GraphicsPrimitive
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 @@ public class MaskFill extends GraphicsPrimitive
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 @@ public class MaskFill extends GraphicsPrimitive
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();
}
}
}
......@@ -28,6 +28,7 @@ package sun.java2d.pipe;
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 @@ import sun.java2d.SunGraphics2D;
* 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 @@ public class AAShapePipe implements ShapeDrawPipe {
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 @@ public class AAShapePipe implements ShapeDrawPipe {
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 @@ public class AAShapePipe implements ShapeDrawPipe {
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],
......
......@@ -34,7 +34,7 @@ import sun.java2d.SunGraphics2D;
* 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 class AlphaColorPipe implements CompositePipe {
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);
}
}
......@@ -280,6 +280,72 @@ public abstract class RenderingEngine {
boolean normalize,
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.
......@@ -393,5 +459,24 @@ public abstract class RenderingEngine {
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);
}
}
}
......@@ -557,6 +557,69 @@ public class PiscesRenderingEngine extends RenderingEngine {
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.
......
......@@ -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 @@ DECLARE_SOLID_DRAWLINE(AnyInt);
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 @@ Java_sun_java2d_loops_DrawParallelogram_DrawParallelogram
* 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 @@ Java_sun_java2d_loops_DrawParallelogram_DrawParallelogram
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 @@ Java_sun_java2d_loops_DrawParallelogram_DrawParallelogram
// 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.
......
......@@ -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 @@ Java_sun_java2d_loops_FillParallelogram_FillParallelogram
/*
* 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);
......
......@@ -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 @@ Java_sun_java2d_loops_MaskFill_MaskFill
}
}
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);
}
/*
* 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 */
......@@ -517,13 +517,15 @@ void ADD_SUFF(IntArgbPreSrcMaskFill)(void *rasBase,
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 @@ void ADD_SUFF(FourByteAbgrPreSrcMaskFill)(void *rasBase,
}
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;
}
......
......@@ -150,10 +150,12 @@ void ADD_SUFF(IntArgbSrcMaskFill)(void *rasBase,
}
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 @@ void ADD_SUFF(FourByteAbgrSrcMaskFill)(void *rasBase,
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 @@ void ADD_SUFF(IntRgbSrcMaskFill)(void *rasBase,
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 @@ void ADD_SUFF(IntBgrSrcMaskFill)(void *rasBase,
}
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 @@ void ADD_SUFF(ThreeByteBgrSrcMaskFill)(void *rasBase,
}
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;
}
......
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)
/*
* 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);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册