Path: blob/master/test/jdk/java/awt/Graphics2D/RenderClipTest/RenderClipTest.java
41153 views
/*1* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 676634226* @summary Tests clipping invariance for AA rectangle and line primitives27* @run main RenderClipTest -strict -readfile 6766342.tests28* @run main RenderClipTest -rectsuite -count 1029*/3031import java.awt.*;32import java.awt.geom.*;33import java.awt.image.*;34import java.awt.event.*;35import java.util.Vector;36import java.io.*;3738public class RenderClipTest {39public static double randDblCoord() {40return Math.random()*60 - 10;41}4243public static float randFltCoord() {44return (float) randDblCoord();45}4647public static int randIntCoord() {48return (int) Math.round(randDblCoord());49}5051public static int randInt(int n) {52return ((int) (Math.random() * (n*4))) >> 2;53}5455static int numtests;56static int numerrors;57static int numfillfailures;58static int numstrokefailures;59static int maxerr;6061static boolean useAA;62static boolean strokePure;63static boolean testFill;64static boolean testDraw;65static boolean silent;66static boolean verbose;67static boolean strict;68static boolean showErrors;69static float lw;70static double rot;7172static BufferedImage imgref;73static BufferedImage imgtst;7475static Graphics2D grefclear;76static Graphics2D gtstclear;77static Graphics2D grefrender;78static Graphics2D gtstrender;7980public static abstract class AnnotatedRenderOp {81public static AnnotatedRenderOp parse(String str) {82AnnotatedRenderOp ar;83if (((ar = Cubic.tryparse(str)) != null) ||84((ar = Quad.tryparse(str)) != null) ||85((ar = Poly.tryparse(str)) != null) ||86((ar = Path.tryparse(str)) != null) ||87((ar = Rect.tryparse(str)) != null) ||88((ar = Line.tryparse(str)) != null) ||89((ar = RectMethod.tryparse(str)) != null) ||90((ar = LineMethod.tryparse(str)) != null))91{92return ar;93}94System.err.println("Unable to parse shape: "+str);95return null;96}9798public abstract void randomize();99100public abstract void fill(Graphics2D g2d);101102public abstract void draw(Graphics2D g2d);103}104105public static abstract class AnnotatedShapeOp extends AnnotatedRenderOp {106public abstract Shape getShape();107108public void fill(Graphics2D g2d) {109g2d.fill(getShape());110}111112public void draw(Graphics2D g2d) {113g2d.draw(getShape());114}115}116117public static void usage(String err) {118if (err != null) {119System.err.println(err);120}121System.err.println("usage: java RenderClipTest "+122"[-read[file F]] [-rectsuite] [-fill] [-draw]");123System.err.println(" "+124"[-aa] [-pure] [-lw N] [-rot N]");125System.err.println(" "+126"[-rectmethod] [-linemethod] [-rect] [-line]");127System.err.println(" "+128"[-cubic] [-quad] [-poly] [-path]");129System.err.println(" "+130"[-silent] [-verbose] [-showerr] [-count N]");131System.err.println(" "+132"[-strict] [-usage]");133System.err.println(" -read Read test data from stdin");134System.err.println(" -readfile F Read test data from file F");135System.err.println(" -rectsuite Run a suite of rect/line tests");136System.err.println(" -fill Test g.fill*(...)");137System.err.println(" -draw Test g.draw*(...)");138System.err.println(" -aa Use antialiased rendering");139System.err.println(" -pure Use STROKE_PURE hint");140System.err.println(" -lw N Test line widths of N "+141"(default 1.0)");142System.err.println(" -rot N Test rotation by N degrees "+143"(default 0.0)");144System.err.println(" -rectmethod Test fillRect/drawRect methods");145System.err.println(" -linemethod Test drawLine method");146System.err.println(" -rect Test Rectangle2D shapes");147System.err.println(" -line Test Line2D shapes");148System.err.println(" -cubic Test CubicCurve2D shapes");149System.err.println(" -quad Test QuadCurve2D shapes");150System.err.println(" -poly Test Polygon shapes");151System.err.println(" -path Test GeneralPath shapes");152System.err.println(" -silent Do not print out error curves");153System.err.println(" -verbose Print out progress info");154System.err.println(" -showerr Display errors on screen");155System.err.println(" -count N N tests per shape, then exit "+156"(default 1000)");157System.err.println(" -strict All failures are important");158System.err.println(" -usage Print this help, then exit");159System.exit((err != null) ? -1 : 0);160}161162public static void main(String argv[]) {163boolean readTests = false;164String readFile = null;165boolean rectsuite = false;166int count = 1000;167lw = 1.0f;168rot = 0.0;169Vector<AnnotatedRenderOp> testOps = new Vector<AnnotatedRenderOp>();170for (int i = 0; i < argv.length; i++) {171String arg = argv[i].toLowerCase();172if (arg.equals("-aa")) {173useAA = true;174} else if (arg.equals("-pure")) {175strokePure = true;176} else if (arg.equals("-fill")) {177testFill = true;178} else if (arg.equals("-draw")) {179testDraw = true;180} else if (arg.equals("-lw")) {181if (i+1 >= argv.length) {182usage("Missing argument: "+argv[i]);183}184lw = Float.parseFloat(argv[++i]);185} else if (arg.equals("-rot")) {186if (i+1 >= argv.length) {187usage("Missing argument: "+argv[i]);188}189rot = Double.parseDouble(argv[++i]);190} else if (arg.equals("-cubic")) {191testOps.add(new Cubic());192} else if (arg.equals("-quad")) {193testOps.add(new Quad());194} else if (arg.equals("-poly")) {195testOps.add(new Poly());196} else if (arg.equals("-path")) {197testOps.add(new Path());198} else if (arg.equals("-rect")) {199testOps.add(new Rect());200} else if (arg.equals("-line")) {201testOps.add(new Line());202} else if (arg.equals("-rectmethod")) {203testOps.add(new RectMethod());204} else if (arg.equals("-linemethod")) {205testOps.add(new LineMethod());206} else if (arg.equals("-verbose")) {207verbose = true;208} else if (arg.equals("-strict")) {209strict = true;210} else if (arg.equals("-silent")) {211silent = true;212} else if (arg.equals("-showerr")) {213showErrors = true;214} else if (arg.equals("-readfile")) {215if (i+1 >= argv.length) {216usage("Missing argument: "+argv[i]);217}218readTests = true;219readFile = argv[++i];220} else if (arg.equals("-read")) {221readTests = true;222readFile = null;223} else if (arg.equals("-rectsuite")) {224rectsuite = true;225} else if (arg.equals("-count")) {226if (i+1 >= argv.length) {227usage("Missing argument: "+argv[i]);228}229count = Integer.parseInt(argv[++i]);230} else if (arg.equals("-usage")) {231usage(null);232} else {233usage("Unknown argument: "+argv[i]);234}235}236if (readTests) {237if (rectsuite || testDraw || testFill ||238useAA || strokePure ||239lw != 1.0f || rot != 0.0 ||240testOps.size() > 0)241{242usage("Should not specify test types with -read options");243}244} else if (rectsuite) {245if (testDraw || testFill ||246useAA || strokePure ||247lw != 1.0f || rot != 0.0 ||248testOps.size() > 0)249{250usage("Should not specify test types with -rectsuite option");251}252} else {253if (!testDraw && !testFill) {254usage("No work: Must specify one or both of "+255"-fill or -draw");256}257if (testOps.size() == 0) {258usage("No work: Must specify one or more of "+259"-rect[method], -line[method], "+260"-cubic, -quad, -poly, or -path");261}262}263initImages();264if (readTests) {265try {266InputStream is;267if (readFile == null) {268is = System.in;269} else {270File f =271new File(System.getProperty("test.src", "."),272readFile);273is = new FileInputStream(f);274}275parseAndRun(is);276} catch (IOException e) {277throw new RuntimeException(e);278}279} else if (rectsuite) {280runRectSuite(count);281} else {282initGCs();283for (int k = 0; k < testOps.size(); k++) {284AnnotatedRenderOp ar = testOps.get(k);285runRandomTests(ar, count);286}287disposeGCs();288}289grefclear.dispose();290gtstclear.dispose();291grefclear = gtstclear = null;292reportStatistics();293}294295public static int reportStatistics() {296String connector = "";297if (numfillfailures > 0) {298System.out.print(numfillfailures+" fills ");299connector = "and ";300}301if (numstrokefailures > 0) {302System.out.print(connector+numstrokefailures+" strokes ");303}304int totalfailures = numfillfailures + numstrokefailures;305if (totalfailures == 0) {306System.out.print("0 ");307}308System.out.println("out of "+numtests+" tests failed...");309int critical = numerrors;310if (strict) {311critical += totalfailures;312}313if (critical > 0) {314throw new RuntimeException(critical+" tests had critical errors");315}316System.out.println("No tests had critical errors");317return (numerrors+totalfailures);318}319320public static void runRectSuite(int count) {321AnnotatedRenderOp ops[] = {322new Rect(),323new RectMethod(),324new Line(),325new LineMethod(),326};327// Sometimes different fill algorithms are chosen for328// thin and wide line modes, make sure we test both...329float filllinewidths[] = { 0.0f, 2.0f };330float drawlinewidths[] = { 0.0f, 0.5f, 1.0f,3312.0f, 2.5f,3325.0f, 5.3f };333double rotations[] = { 0.0, 15.0, 90.0,334135.0, 180.0,335200.0, 270.0,336300.0};337for (AnnotatedRenderOp ar: ops) {338for (double r: rotations) {339rot = r;340for (int i = 0; i < 8; i++) {341float linewidths[];342if ((i & 1) == 0) {343if ((ar instanceof Line) ||344(ar instanceof LineMethod))345{346continue;347}348testFill = true;349testDraw = false;350linewidths = filllinewidths;351} else {352testFill = false;353testDraw = true;354linewidths = drawlinewidths;355}356useAA = ((i & 2) != 0);357strokePure = ((i & 4) != 0);358for (float w : linewidths) {359lw = w;360runSuiteTests(ar, count);361}362}363}364}365}366367public static void runSuiteTests(AnnotatedRenderOp ar, int count) {368if (verbose) {369System.out.print("Running ");370System.out.print(testFill ? "Fill " : "Draw ");371System.out.print(BaseName(ar));372if (useAA) {373System.out.print(" AA");374}375if (strokePure) {376System.out.print(" Pure");377}378if (lw != 1.0f) {379System.out.print(" lw="+lw);380}381if (rot != 0.0f) {382System.out.print(" rot="+rot);383}384System.out.println();385}386initGCs();387runRandomTests(ar, count);388disposeGCs();389}390391public static String BaseName(AnnotatedRenderOp ar) {392String s = ar.toString();393int leftparen = s.indexOf('(');394if (leftparen >= 0) {395s = s.substring(0, leftparen);396}397return s;398}399400public static void runRandomTests(AnnotatedRenderOp ar, int count) {401for (int i = 0; i < count; i++) {402ar.randomize();403if (testDraw) {404test(ar, false);405}406if (testFill) {407test(ar, true);408}409}410}411412public static void initImages() {413imgref = new BufferedImage(40, 40, BufferedImage.TYPE_INT_RGB);414imgtst = new BufferedImage(40, 40, BufferedImage.TYPE_INT_RGB);415grefclear = imgref.createGraphics();416gtstclear = imgtst.createGraphics();417grefclear.setColor(Color.white);418gtstclear.setColor(Color.white);419}420421public static void initGCs() {422grefrender = imgref.createGraphics();423gtstrender = imgtst.createGraphics();424gtstrender.clipRect(10, 10, 20, 20);425grefrender.setColor(Color.blue);426gtstrender.setColor(Color.blue);427if (lw != 1.0f) {428BasicStroke bs = new BasicStroke(lw);429grefrender.setStroke(bs);430gtstrender.setStroke(bs);431}432if (rot != 0.0) {433double rotrad = Math.toRadians(rot);434grefrender.rotate(rotrad, 20, 20);435gtstrender.rotate(rotrad, 20, 20);436}437if (strokePure) {438grefrender.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,439RenderingHints.VALUE_STROKE_PURE);440gtstrender.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,441RenderingHints.VALUE_STROKE_PURE);442}443if (useAA) {444grefrender.setRenderingHint(RenderingHints.KEY_ANTIALIASING,445RenderingHints.VALUE_ANTIALIAS_ON);446gtstrender.setRenderingHint(RenderingHints.KEY_ANTIALIASING,447RenderingHints.VALUE_ANTIALIAS_ON);448maxerr = 1;449}450}451452public static void disposeGCs() {453grefrender.dispose();454gtstrender.dispose();455grefrender = gtstrender = null;456}457458public static void parseAndRun(InputStream in) throws IOException {459BufferedReader br = new BufferedReader(new InputStreamReader(in));460String str;461while ((str = br.readLine()) != null) {462if (str.startsWith("Stroked ") || str.startsWith("Filled ")) {463parseTest(str);464continue;465}466if (str.startsWith("Running ")) {467continue;468}469if (str.startsWith("Failed: ")) {470continue;471}472if (str.indexOf(" out of ") > 0 &&473str.indexOf(" tests failed...") > 0)474{475continue;476}477if (str.indexOf(" tests had critical errors") > 0) {478continue;479}480System.err.println("Unparseable line: "+str);481}482}483484public static void parseTest(String origstr) {485String str = origstr;486boolean isfill = false;487useAA = strokePure = false;488lw = 1.0f;489rot = 0.0;490if (str.startsWith("Stroked ")) {491str = str.substring(8);492isfill = false;493} else if (str.startsWith("Filled ")) {494str = str.substring(7);495isfill = true;496} else {497System.err.println("Unparseable test line: "+origstr);498}499if (str.startsWith("AA ")) {500str = str.substring(3);501useAA = true;502}503if (str.startsWith("Pure ")) {504str = str.substring(5);505strokePure = true;506}507if (str.startsWith("Lw=")) {508int index = str.indexOf(' ', 3);509if (index > 0) {510lw = Float.parseFloat(str.substring(3, index));511str = str.substring(index+1);512}513}514if (str.startsWith("Rot=")) {515int index = str.indexOf(' ', 4);516if (index > 0) {517rot = Double.parseDouble(str.substring(4, index));518str = str.substring(index+1);519}520}521AnnotatedRenderOp ar = AnnotatedRenderOp.parse(str);522if (ar != null) {523initGCs();524test(ar, isfill);525disposeGCs();526} else {527System.err.println("Unparseable test line: "+origstr);528}529}530531public static void test(AnnotatedRenderOp ar, boolean isfill) {532grefclear.fillRect(0, 0, 40, 40);533gtstclear.fillRect(0, 0, 40, 40);534if (isfill) {535ar.fill(grefrender);536ar.fill(gtstrender);537} else {538ar.draw(grefrender);539ar.draw(gtstrender);540}541check(imgref, imgtst, ar, isfill);542}543544public static int[] getData(BufferedImage img) {545Raster r = img.getRaster();546DataBufferInt dbi = (DataBufferInt) r.getDataBuffer();547return dbi.getData();548}549550public static int getScan(BufferedImage img) {551Raster r = img.getRaster();552SinglePixelPackedSampleModel sppsm =553(SinglePixelPackedSampleModel) r.getSampleModel();554return sppsm.getScanlineStride();555}556557public static int getOffset(BufferedImage img) {558Raster r = img.getRaster();559SinglePixelPackedSampleModel sppsm =560(SinglePixelPackedSampleModel) r.getSampleModel();561return sppsm.getOffset(-r.getSampleModelTranslateX(),562-r.getSampleModelTranslateY());563}564565final static int opaque = 0xff000000;566final static int whitergb = Color.white.getRGB();567568public static final int maxdiff(int rgb1, int rgb2) {569int maxd = 0;570for (int i = 0; i < 32; i += 8) {571int c1 = (rgb1 >> i) & 0xff;572int c2 = (rgb2 >> i) & 0xff;573int d = Math.abs(c1-c2);574if (maxd < d) {575maxd = d;576}577}578return maxd;579}580581public static void check(BufferedImage imgref, BufferedImage imgtst,582AnnotatedRenderOp ar, boolean wasfill)583{584numtests++;585int dataref[] = getData(imgref);586int datatst[] = getData(imgtst);587int scanref = getScan(imgref);588int scantst = getScan(imgtst);589int offref = getOffset(imgref);590int offtst = getOffset(imgtst);591592// We want to check for errors outside the clip at a higher593// priority than errors involving different pixels touched594// inside the clip.595596// Check above clip597if (check(ar, wasfill,598null, 0, 0,599datatst, scantst, offtst,6000, 0, 40, 10))601{602return;603}604// Check below clip605if (check(ar, wasfill,606null, 0, 0,607datatst, scantst, offtst,6080, 30, 40, 40))609{610return;611}612// Check left of clip613if (check(ar, wasfill,614null, 0, 0,615datatst, scantst, offtst,6160, 10, 10, 30))617{618return;619}620// Check right of clip621if (check(ar, wasfill,622null, 0, 0,623datatst, scantst, offtst,62430, 10, 40, 30))625{626return;627}628// Check inside clip629check(ar, wasfill,630dataref, scanref, offref,631datatst, scantst, offtst,63210, 10, 30, 30);633}634635public static boolean check(AnnotatedRenderOp ar, boolean wasfill,636int dataref[], int scanref, int offref,637int datatst[], int scantst, int offtst,638int x0, int y0, int x1, int y1)639{640offref += scanref * y0;641offtst += scantst * y0;642for (int y = y0; y < y1; y++) {643for (int x = x0; x < x1; x++) {644boolean failed;645String reason;646int rgbref;647int rgbtst;648649rgbtst = datatst[offtst+x] | opaque;650if (dataref == null) {651/* Outside of clip, must be white, no error tolerance */652rgbref = whitergb;653failed = (rgbtst != rgbref);654reason = "stray pixel rendered outside of clip";655} else {656/* Inside of clip, check for maxerr delta in components */657rgbref = dataref[offref+x] | opaque;658failed = (rgbref != rgbtst &&659maxdiff(rgbref, rgbtst) > maxerr);660reason = "different pixel rendered inside clip";661}662if (failed) {663if (dataref == null) {664numerrors++;665}666if (wasfill) {667numfillfailures++;668} else {669numstrokefailures++;670}671if (!silent) {672System.out.println("Failed: "+reason+" at "+x+", "+y+673" ["+Integer.toHexString(rgbref)+674" != "+Integer.toHexString(rgbtst)+675"]");676System.out.print(wasfill ? "Filled " : "Stroked ");677if (useAA) System.out.print("AA ");678if (strokePure) System.out.print("Pure ");679if (lw != 1) System.out.print("Lw="+lw+" ");680if (rot != 0) System.out.print("Rot="+rot+" ");681System.out.println(ar);682}683if (showErrors) {684show(imgref, imgtst);685}686return true;687}688}689offref += scanref;690offtst += scantst;691}692return false;693}694695static ErrorWindow errw;696697public static void show(BufferedImage imgref, BufferedImage imgtst) {698ErrorWindow errw = new ErrorWindow();699errw.setImages(imgref, imgtst);700errw.setVisible(true);701errw.waitForHide();702errw.dispose();703}704705public static class Cubic extends AnnotatedShapeOp {706public static Cubic tryparse(String str) {707str = str.trim();708if (!str.startsWith("Cubic(")) {709return null;710}711str = str.substring(6);712double coords[] = new double[8];713boolean foundparen = false;714for (int i = 0; i < coords.length; i++) {715int index = str.indexOf(",");716if (index < 0) {717if (i < coords.length-1) {718return null;719}720index = str.indexOf(")");721if (index < 0) {722return null;723}724foundparen = true;725}726String num = str.substring(0, index);727try {728coords[i] = Double.parseDouble(num);729} catch (NumberFormatException nfe) {730return null;731}732str = str.substring(index+1);733}734if (!foundparen || str.length() > 0) {735return null;736}737Cubic c = new Cubic();738c.cubic.setCurve(coords[0], coords[1],739coords[2], coords[3],740coords[4], coords[5],741coords[6], coords[7]);742return c;743}744745private CubicCurve2D cubic = new CubicCurve2D.Double();746747public void randomize() {748cubic.setCurve(randDblCoord(), randDblCoord(),749randDblCoord(), randDblCoord(),750randDblCoord(), randDblCoord(),751randDblCoord(), randDblCoord());752}753754public Shape getShape() {755return cubic;756}757758public String toString() {759return ("Cubic("+760cubic.getX1()+", "+761cubic.getY1()+", "+762cubic.getCtrlX1()+", "+763cubic.getCtrlY1()+", "+764cubic.getCtrlX2()+", "+765cubic.getCtrlY2()+", "+766cubic.getX2()+", "+767cubic.getY2()768+")");769}770}771772public static class Quad extends AnnotatedShapeOp {773public static Quad tryparse(String str) {774str = str.trim();775if (!str.startsWith("Quad(")) {776return null;777}778str = str.substring(5);779double coords[] = new double[6];780boolean foundparen = false;781for (int i = 0; i < coords.length; i++) {782int index = str.indexOf(",");783if (index < 0) {784if (i < coords.length-1) {785return null;786}787index = str.indexOf(")");788if (index < 0) {789return null;790}791foundparen = true;792}793String num = str.substring(0, index);794try {795coords[i] = Double.parseDouble(num);796} catch (NumberFormatException nfe) {797return null;798}799str = str.substring(index+1);800}801if (!foundparen || str.length() > 0) {802return null;803}804Quad c = new Quad();805c.quad.setCurve(coords[0], coords[1],806coords[2], coords[3],807coords[4], coords[5]);808return c;809}810811private QuadCurve2D quad = new QuadCurve2D.Double();812813public void randomize() {814quad.setCurve(randDblCoord(), randDblCoord(),815randDblCoord(), randDblCoord(),816randDblCoord(), randDblCoord());817}818819public Shape getShape() {820return quad;821}822823public String toString() {824return ("Quad("+825quad.getX1()+", "+826quad.getY1()+", "+827quad.getCtrlX()+", "+828quad.getCtrlY()+", "+829quad.getX2()+", "+830quad.getY2()831+")");832}833}834835public static class Poly extends AnnotatedShapeOp {836public static Poly tryparse(String str) {837str = str.trim();838if (!str.startsWith("Poly(")) {839return null;840}841str = str.substring(5);842Polygon p = new Polygon();843while (true) {844int x, y;845str = str.trim();846if (str.startsWith(")")) {847str = str.substring(1);848break;849}850if (p.npoints > 0) {851if (str.startsWith(",")) {852str = str.substring(2).trim();853} else {854return null;855}856}857if (str.startsWith("[")) {858str = str.substring(1);859} else {860return null;861}862int index = str.indexOf(",");863if (index < 0) {864return null;865}866String num = str.substring(0, index);867try {868x = Integer.parseInt(num);869} catch (NumberFormatException nfe) {870return null;871}872str = str.substring(index+1);873index = str.indexOf("]");874if (index < 0) {875return null;876}877num = str.substring(0, index).trim();878try {879y = Integer.parseInt(num);880} catch (NumberFormatException nfe) {881return null;882}883str = str.substring(index+1);884p.addPoint(x, y);885}886if (str.length() > 0) {887return null;888}889if (p.npoints < 3) {890return null;891}892return new Poly(p);893}894895private Polygon poly;896897public Poly() {898this.poly = new Polygon();899}900901private Poly(Polygon p) {902this.poly = p;903}904905public void randomize() {906poly.reset();907poly.addPoint(randIntCoord(), randIntCoord());908poly.addPoint(randIntCoord(), randIntCoord());909poly.addPoint(randIntCoord(), randIntCoord());910poly.addPoint(randIntCoord(), randIntCoord());911poly.addPoint(randIntCoord(), randIntCoord());912}913914public Shape getShape() {915return poly;916}917918public String toString() {919StringBuffer sb = new StringBuffer(100);920sb.append("Poly(");921for (int i = 0; i < poly.npoints; i++) {922if (i != 0) {923sb.append(", ");924}925sb.append("[");926sb.append(poly.xpoints[i]);927sb.append(", ");928sb.append(poly.ypoints[i]);929sb.append("]");930}931sb.append(")");932return sb.toString();933}934}935936public static class Path extends AnnotatedShapeOp {937public static Path tryparse(String str) {938str = str.trim();939if (!str.startsWith("Path(")) {940return null;941}942str = str.substring(5);943GeneralPath gp = new GeneralPath();944float coords[] = new float[6];945int numsegs = 0;946while (true) {947int type;948int n;949str = str.trim();950if (str.startsWith(")")) {951str = str.substring(1);952break;953}954if (str.startsWith("M[")) {955type = PathIterator.SEG_MOVETO;956n = 2;957} else if (str.startsWith("L[")) {958type = PathIterator.SEG_LINETO;959n = 2;960} else if (str.startsWith("Q[")) {961type = PathIterator.SEG_QUADTO;962n = 4;963} else if (str.startsWith("C[")) {964type = PathIterator.SEG_CUBICTO;965n = 6;966} else if (str.startsWith("E[")) {967type = PathIterator.SEG_CLOSE;968n = 0;969} else {970return null;971}972str = str.substring(2);973if (n == 0) {974if (str.startsWith("]")) {975str = str.substring(1);976} else {977return null;978}979}980for (int i = 0; i < n; i++) {981int index;982if (i < n-1) {983index = str.indexOf(",");984} else {985index = str.indexOf("]");986}987if (index < 0) {988return null;989}990String num = str.substring(0, index);991try {992coords[i] = Float.parseFloat(num);993} catch (NumberFormatException nfe) {994return null;995}996str = str.substring(index+1).trim();997}998switch (type) {999case PathIterator.SEG_MOVETO:1000gp.moveTo(coords[0], coords[1]);1001break;1002case PathIterator.SEG_LINETO:1003gp.lineTo(coords[0], coords[1]);1004break;1005case PathIterator.SEG_QUADTO:1006gp.quadTo(coords[0], coords[1],1007coords[2], coords[3]);1008break;1009case PathIterator.SEG_CUBICTO:1010gp.curveTo(coords[0], coords[1],1011coords[2], coords[3],1012coords[4], coords[5]);1013break;1014case PathIterator.SEG_CLOSE:1015gp.closePath();1016break;1017}1018numsegs++;1019}1020if (str.length() > 0) {1021return null;1022}1023if (numsegs < 2) {1024return null;1025}1026return new Path(gp);1027}10281029private GeneralPath path;10301031public Path() {1032this.path = new GeneralPath();1033}10341035private Path(GeneralPath gp) {1036this.path = gp;1037}10381039public void randomize() {1040path.reset();1041path.moveTo(randFltCoord(), randFltCoord());1042for (int i = randInt(5)+3; i > 0; --i) {1043switch(randInt(5)) {1044case 0:1045path.moveTo(randFltCoord(), randFltCoord());1046break;1047case 1:1048path.lineTo(randFltCoord(), randFltCoord());1049break;1050case 2:1051path.quadTo(randFltCoord(), randFltCoord(),1052randFltCoord(), randFltCoord());1053break;1054case 3:1055path.curveTo(randFltCoord(), randFltCoord(),1056randFltCoord(), randFltCoord(),1057randFltCoord(), randFltCoord());1058break;1059case 4:1060path.closePath();1061break;1062}1063}1064}10651066public Shape getShape() {1067return path;1068}10691070public String toString() {1071StringBuffer sb = new StringBuffer(100);1072sb.append("Path(");1073PathIterator pi = path.getPathIterator(null);1074float coords[] = new float[6];1075boolean first = true;1076while (!pi.isDone()) {1077int n;1078char c;1079switch(pi.currentSegment(coords)) {1080case PathIterator.SEG_MOVETO:1081c = 'M';1082n = 2;1083break;1084case PathIterator.SEG_LINETO:1085c = 'L';1086n = 2;1087break;1088case PathIterator.SEG_QUADTO:1089c = 'Q';1090n = 4;1091break;1092case PathIterator.SEG_CUBICTO:1093c = 'C';1094n = 6;1095break;1096case PathIterator.SEG_CLOSE:1097c = 'E';1098n = 0;1099break;1100default:1101throw new InternalError("Unknown segment!");1102}1103sb.append(c);1104sb.append("[");1105for (int i = 0; i < n; i++) {1106if (i != 0) {1107sb.append(",");1108}1109sb.append(coords[i]);1110}1111sb.append("]");1112pi.next();1113}1114sb.append(")");1115return sb.toString();1116}1117}11181119public static class Rect extends AnnotatedShapeOp {1120public static Rect tryparse(String str) {1121str = str.trim();1122if (!str.startsWith("Rect(")) {1123return null;1124}1125str = str.substring(5);1126double coords[] = new double[4];1127boolean foundparen = false;1128for (int i = 0; i < coords.length; i++) {1129int index = str.indexOf(",");1130if (index < 0) {1131if (i < coords.length-1) {1132return null;1133}1134index = str.indexOf(")");1135if (index < 0) {1136return null;1137}1138foundparen = true;1139}1140String num = str.substring(0, index);1141try {1142coords[i] = Double.parseDouble(num);1143} catch (NumberFormatException nfe) {1144return null;1145}1146str = str.substring(index+1);1147}1148if (!foundparen || str.length() > 0) {1149return null;1150}1151Rect r = new Rect();1152r.rect.setRect(coords[0], coords[1],1153coords[2], coords[3]);1154return r;1155}11561157private Rectangle2D rect = new Rectangle2D.Double();11581159public void randomize() {1160rect.setRect(randDblCoord(), randDblCoord(),1161randDblCoord(), randDblCoord());1162}11631164public Shape getShape() {1165return rect;1166}11671168public String toString() {1169return ("Rect("+1170rect.getX()+", "+1171rect.getY()+", "+1172rect.getWidth()+", "+1173rect.getHeight()1174+")");1175}1176}11771178public static class Line extends AnnotatedShapeOp {1179public static Line tryparse(String str) {1180str = str.trim();1181if (!str.startsWith("Line(")) {1182return null;1183}1184str = str.substring(5);1185double coords[] = new double[4];1186boolean foundparen = false;1187for (int i = 0; i < coords.length; i++) {1188int index = str.indexOf(",");1189if (index < 0) {1190if (i < coords.length-1) {1191return null;1192}1193index = str.indexOf(")");1194if (index < 0) {1195return null;1196}1197foundparen = true;1198}1199String num = str.substring(0, index);1200try {1201coords[i] = Double.parseDouble(num);1202} catch (NumberFormatException nfe) {1203return null;1204}1205str = str.substring(index+1);1206}1207if (!foundparen || str.length() > 0) {1208return null;1209}1210Line l = new Line();1211l.line.setLine(coords[0], coords[1],1212coords[2], coords[3]);1213return l;1214}12151216private Line2D line = new Line2D.Double();12171218public void randomize() {1219line.setLine(randDblCoord(), randDblCoord(),1220randDblCoord(), randDblCoord());1221}12221223public Shape getShape() {1224return line;1225}12261227public String toString() {1228return ("Line("+1229line.getX1()+", "+1230line.getY1()+", "+1231line.getX2()+", "+1232line.getY2()1233+")");1234}1235}12361237public static class RectMethod extends AnnotatedRenderOp {1238public static RectMethod tryparse(String str) {1239str = str.trim();1240if (!str.startsWith("RectMethod(")) {1241return null;1242}1243str = str.substring(11);1244int coords[] = new int[4];1245boolean foundparen = false;1246for (int i = 0; i < coords.length; i++) {1247int index = str.indexOf(",");1248if (index < 0) {1249if (i < coords.length-1) {1250return null;1251}1252index = str.indexOf(")");1253if (index < 0) {1254return null;1255}1256foundparen = true;1257}1258String num = str.substring(0, index).trim();1259try {1260coords[i] = Integer.parseInt(num);1261} catch (NumberFormatException nfe) {1262return null;1263}1264str = str.substring(index+1);1265}1266if (!foundparen || str.length() > 0) {1267return null;1268}1269RectMethod rm = new RectMethod();1270rm.rect.setBounds(coords[0], coords[1],1271coords[2], coords[3]);1272return rm;1273}12741275private Rectangle rect = new Rectangle();12761277public void randomize() {1278rect.setBounds(randIntCoord(), randIntCoord(),1279randIntCoord(), randIntCoord());1280}12811282public void fill(Graphics2D g2d) {1283g2d.fillRect(rect.x, rect.y, rect.width, rect.height);1284}12851286public void draw(Graphics2D g2d) {1287g2d.drawRect(rect.x, rect.y, rect.width, rect.height);1288}12891290public String toString() {1291return ("RectMethod("+1292rect.x+", "+1293rect.y+", "+1294rect.width+", "+1295rect.height1296+")");1297}1298}12991300public static class LineMethod extends AnnotatedRenderOp {1301public static LineMethod tryparse(String str) {1302str = str.trim();1303if (!str.startsWith("LineMethod(")) {1304return null;1305}1306str = str.substring(11);1307int coords[] = new int[4];1308boolean foundparen = false;1309for (int i = 0; i < coords.length; i++) {1310int index = str.indexOf(",");1311if (index < 0) {1312if (i < coords.length-1) {1313return null;1314}1315index = str.indexOf(")");1316if (index < 0) {1317return null;1318}1319foundparen = true;1320}1321String num = str.substring(0, index).trim();1322try {1323coords[i] = Integer.parseInt(num);1324} catch (NumberFormatException nfe) {1325return null;1326}1327str = str.substring(index+1);1328}1329if (!foundparen || str.length() > 0) {1330return null;1331}1332LineMethod lm = new LineMethod();1333lm.line = coords;1334return lm;1335}13361337private int line[] = new int[4];13381339public void randomize() {1340line[0] = randIntCoord();1341line[1] = randIntCoord();1342line[2] = randIntCoord();1343line[3] = randIntCoord();1344}13451346public void fill(Graphics2D g2d) {1347}13481349public void draw(Graphics2D g2d) {1350g2d.drawLine(line[0], line[1], line[2], line[3]);1351}13521353public String toString() {1354return ("LineMethod("+1355line[0]+", "+1356line[1]+", "+1357line[2]+", "+1358line[3]1359+")");1360}1361}13621363public static class ErrorWindow extends Frame {1364ImageCanvas unclipped;1365ImageCanvas reference;1366ImageCanvas actual;1367ImageCanvas diff;13681369public ErrorWindow() {1370super("Error Comparison Window");13711372unclipped = new ImageCanvas();1373reference = new ImageCanvas();1374actual = new ImageCanvas();1375diff = new ImageCanvas();13761377setLayout(new SmartGridLayout(0, 2, 5, 5));1378addImagePanel(unclipped, "Unclipped rendering");1379addImagePanel(reference, "Clipped reference");1380addImagePanel(actual, "Actual clipped");1381addImagePanel(diff, "Difference");13821383addWindowListener(new WindowAdapter() {1384public void windowClosing(WindowEvent e) {1385setVisible(false);1386}1387});1388}13891390public void addImagePanel(ImageCanvas ic, String label) {1391add(ic);1392add(new Label(label));1393}13941395public void setImages(BufferedImage imgref, BufferedImage imgtst) {1396unclipped.setImage(imgref);1397reference.setReference(imgref);1398actual.setImage(imgtst);1399diff.setDiff(reference.getImage(), imgtst);1400invalidate();1401pack();1402repaint();1403}14041405public void setVisible(boolean vis) {1406super.setVisible(vis);1407synchronized (this) {1408notifyAll();1409}1410}14111412public synchronized void waitForHide() {1413while (isShowing()) {1414try {1415wait();1416} catch (InterruptedException e) {1417System.exit(2);1418}1419}1420}1421}14221423public static class SmartGridLayout implements LayoutManager {1424int rows;1425int cols;1426int hgap;1427int vgap;14281429public SmartGridLayout(int r, int c, int h, int v) {1430this.rows = r;1431this.cols = c;1432this.hgap = h;1433this.vgap = v;1434}14351436public void addLayoutComponent(String name, Component comp) {1437}14381439public void removeLayoutComponent(Component comp) {1440}14411442public int[][] getGridSizes(Container parent, boolean min) {1443int ncomponents = parent.getComponentCount();1444int nrows = rows;1445int ncols = cols;14461447if (nrows > 0) {1448ncols = (ncomponents + nrows - 1) / nrows;1449} else {1450nrows = (ncomponents + ncols - 1) / ncols;1451}1452int widths[] = new int[ncols+1];1453int heights[] = new int[nrows+1];1454int x = 0;1455int y = 0;1456for (int i = 0 ; i < ncomponents ; i++) {1457Component comp = parent.getComponent(i);1458Dimension d = (min1459? comp.getMinimumSize()1460: comp.getPreferredSize());1461if (widths[x] < d.width) {1462widths[x] = d.width;1463}1464if (heights[y] < d.height) {1465heights[y] = d.height;1466}1467x++;1468if (x >= ncols) {1469x = 0;1470y++;1471}1472}1473for (int i = 0; i < ncols; i++) {1474widths[ncols] += widths[i];1475}1476for (int i = 0; i < nrows; i++) {1477heights[nrows] += heights[i];1478}1479return new int[][] { widths, heights };1480}14811482public Dimension getSize(Container parent, boolean min) {1483int sizes[][] = getGridSizes(parent, min);1484int widths[] = sizes[0];1485int heights[] = sizes[1];1486int nrows = heights.length-1;1487int ncols = widths.length-1;1488int w = widths[ncols];1489int h = heights[nrows];1490Insets insets = parent.getInsets();1491return new Dimension(insets.left+insets.right + w+(ncols+1)*hgap,1492insets.top+insets.bottom + h+(nrows+1)*vgap);1493}14941495public Dimension preferredLayoutSize(Container parent) {1496return getSize(parent, false);1497}14981499public Dimension minimumLayoutSize(Container parent) {1500return getSize(parent, true);1501}15021503public void layoutContainer(Container parent) {1504int pref[][] = getGridSizes(parent, false);1505int min[][] = getGridSizes(parent, true);1506int minwidths[] = min[0];1507int minheights[] = min[1];1508int prefwidths[] = pref[0];1509int prefheights[] = pref[1];1510int nrows = minheights.length - 1;1511int ncols = minwidths.length - 1;1512Insets insets = parent.getInsets();1513int w = parent.getWidth() - insets.left - insets.right;1514int h = parent.getHeight() - insets.top - insets.bottom;1515w = w - (ncols+1)*hgap;1516h = h - (nrows+1)*vgap;1517int widths[] = calculateSizes(w, ncols, minwidths, prefwidths);1518int heights[] = calculateSizes(h, nrows, minheights, prefheights);1519int ncomponents = parent.getComponentCount();1520int x = insets.left + hgap;1521int y = insets.top + vgap;1522int r = 0;1523int c = 0;1524for (int i = 0; i < ncomponents; i++) {1525parent.getComponent(i).setBounds(x, y, widths[c], heights[r]);1526x += widths[c++] + hgap;1527if (c >= ncols) {1528c = 0;1529x = insets.left + hgap;1530y += heights[r++] + vgap;1531if (r >= nrows) {1532// just in case1533break;1534}1535}1536}1537}15381539public static int[] calculateSizes(int total, int num,1540int minsizes[], int prefsizes[])1541{1542if (total <= minsizes[num]) {1543return minsizes;1544}1545if (total >= prefsizes[num]) {1546return prefsizes;1547}1548int sizes[] = new int[total];1549int prevhappy = 0;1550int nhappy = 0;1551int happysize = 0;1552do {1553int addsize = (total - happysize) / (num - nhappy);1554happysize = 0;1555for (int i = 0; i < num; i++) {1556if (sizes[i] >= prefsizes[i] ||1557minsizes[i] + addsize > prefsizes[i])1558{1559happysize += (sizes[i] = prefsizes[i]);1560nhappy++;1561} else {1562sizes[i] = minsizes[i] + addsize;1563}1564}1565} while (nhappy < num && nhappy > prevhappy);1566return sizes;1567}1568}15691570public static class ImageCanvas extends Canvas {1571BufferedImage image;15721573public void setImage(BufferedImage img) {1574this.image = img;1575}15761577public BufferedImage getImage() {1578return image;1579}15801581public void checkImage(int w, int h) {1582if (image == null ||1583image.getWidth() < w ||1584image.getHeight() < h)1585{1586image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);1587}1588}15891590public void setReference(BufferedImage img) {1591checkImage(img.getWidth(), img.getHeight());1592Graphics g = image.createGraphics();1593g.drawImage(img, 0, 0, null);1594g.setColor(Color.white);1595g.fillRect(0, 0, 30, 10);1596g.fillRect(30, 0, 10, 30);1597g.fillRect(10, 30, 30, 10);1598g.fillRect(0, 10, 10, 30);1599g.dispose();1600}16011602public void setDiff(BufferedImage imgref, BufferedImage imgtst) {1603int w = Math.max(imgref.getWidth(), imgtst.getWidth());1604int h = Math.max(imgref.getHeight(), imgtst.getHeight());1605checkImage(w, h);1606Graphics g = image.createGraphics();1607g.drawImage(imgref, 0, 0, null);1608g.setXORMode(Color.white);1609g.drawImage(imgtst, 0, 0, null);1610g.setPaintMode();1611g.setColor(new Color(1f, 1f, 0f, 0.25f));1612g.fillRect(10, 10, 20, 20);1613g.setColor(new Color(1f, 0f, 0f, 0.25f));1614g.fillRect(0, 0, 30, 10);1615g.fillRect(30, 0, 10, 30);1616g.fillRect(10, 30, 30, 10);1617g.fillRect(0, 10, 10, 30);1618g.dispose();1619}16201621public Dimension getPreferredSize() {1622if (image == null) {1623return new Dimension();1624} else {1625return new Dimension(image.getWidth(), image.getHeight());1626}1627}16281629public void paint(Graphics g) {1630g.drawImage(image, 0, 0, null);1631}1632}1633}163416351636