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