| /* |
| * 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); |
| } |
| } |
| } |