blob: 29ef55d2ab4d7a05bfbfe85b28702faf44e9f237 [file] [log] [blame]
/*
* Copyright (c) 2008, 2010, 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);
}
}
}