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