1 /* 2 * Copyright (c) 2002, 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 4678208 4771101 6328481 6588884 27 * @summary verify the pixelization of degenerate polylines and polygons 28 * @run main PolyVertTest 29 * @run main/othervm -Dsun.java2d.d3d=True PolyVertTest -hwonly 30 * @run main/othervm -Dsun.java2d.opengl=True PolyVertTest -hwonly 31 */ 32 33 import java.awt.*; 34 import java.awt.event.*; 35 import java.awt.geom.*; 36 import java.awt.image.*; 37 38 public class PolyVertTest { 39 static int TESTWIDTH; 40 static int TESTHEIGHT; 41 static final int REG_TEST_WIDTH = 10; 42 static final int REG_TEST_HEIGHT = 10; 43 static final int FULL_TEST_WIDTH = 50; 44 static final int FULL_TEST_HEIGHT = 200; 45 46 static final int FRINGE = 2; 47 static final int GREEN = Color.green.getRGB(); 48 static final int RED = Color.red.getRGB(); 49 50 static BufferedImage refImg; 51 static BufferedImage errorImg; 52 static Graphics errorG; 53 static Component testCanvas; 54 55 static int totalbadpixels; 56 static int totalfuzzypixels; 57 static int numbadtests; 58 static int numfuzzytests; 59 static int numframes; 60 static int fuzzystarty; 61 62 static boolean counting; 63 static boolean showerrors; 64 static boolean showresults; 65 static boolean fringe; 66 static boolean forceerror; 67 static boolean fulltest = true; 68 static boolean hwonly; 69 70 static WindowListener windowCloser = new WindowAdapter() { 71 public void windowClosing(WindowEvent e) { 72 e.getWindow().hide(); 73 if (--numframes <= 0) { 74 System.exit(0); 75 } 76 } 77 }; 78 79 public PolyVertTest() { 80 /* 81 setBackground(Color.white); 82 setForeground(Color.black); 83 */ 84 } 85 86 static int polypts[][][] = { 87 { 88 // void polygon (no points) 89 {}, {}, 90 }, 91 { 92 // one point 93 { 0 }, { 0 }, 94 }, 95 { 96 // two points 97 { 0, 5 }, { 0, 0 }, 98 { 0, 0, 6, 1, 99 10, 0, 6, 1, 100 20, 0, 6, 1 }, 101 { 0, 0, 6, 1, 102 10, 0, 1, 1, 15, 0, 1, 1, 103 20, 0, 1, 1, 25, 0, 1, 1 }, 104 { 10, 0, 1, 1, 105 20, 0, 1, 1 }, 106 }, 107 { 108 // open triangle 109 { 0, 5, 5 }, { 0, 0, 5 }, 110 111 { 0, 0, 6, 1, 5, 1, 1, 5, 112 113 10, 0, 6, 1, 15, 1, 1, 5, 11, 1, 1, 1, 114 12, 2, 1, 1, 13, 3, 1, 1, 14, 4, 1, 1, 115 116 20, 0, 6, 1, 25, 1, 1, 5, 21, 1, 1, 1, 117 22, 2, 1, 1, 23, 3, 1, 1, 24, 4, 1, 1 }, 118 119 { 0, 0, 6, 1, 5, 1, 1, 5, 120 121 10, 0, 6, 1, 15, 1, 1, 5, 11, 1, 1, 1, 122 12, 2, 1, 1, 13, 3, 1, 1, 14, 4, 1, 1, 123 124 20, 0, 6, 1, 25, 1, 1, 5, 21, 1, 1, 1, 125 22, 2, 1, 1, 23, 3, 1, 1, 24, 4, 1, 1 }, 126 127 { 10, 0, 1, 1, 128 20, 0, 1, 1 }, 129 }, 130 { 131 // closed triangle 132 { 0, 5, 5, 0 }, { 0, 0, 5, 0 }, 133 134 { 0, 0, 6, 1, 5, 1, 1, 5, 1, 1, 1, 1, 135 2, 2, 1, 1, 3, 3, 1, 1, 4, 4, 1, 1, 136 137 10, 0, 6, 1, 15, 1, 1, 5, 11, 1, 1, 1, 138 12, 2, 1, 1, 13, 3, 1, 1, 14, 4, 1, 1, 139 140 20, 0, 6, 1, 25, 1, 1, 5, 21, 1, 1, 1, 141 22, 2, 1, 1, 23, 3, 1, 1, 24, 4, 1, 1 }, 142 143 { 1, 0, 5, 1, 5, 1, 1, 5, 1, 1, 1, 1, 144 2, 2, 1, 1, 3, 3, 1, 1, 4, 4, 1, 1, 145 146 10, 0, 6, 1, 15, 1, 1, 5, 11, 1, 1, 1, 147 12, 2, 1, 1, 13, 3, 1, 1, 14, 4, 1, 1, 148 149 20, 0, 6, 1, 25, 1, 1, 5, 21, 1, 1, 1, 150 22, 2, 1, 1, 23, 3, 1, 1, 24, 4, 1, 1 }, 151 152 { 0, 0, 1, 1, 153 10, 0, 1, 1, 154 20, 0, 1, 1 }, 155 }, 156 { 157 // empty line 158 { 0, 0 }, { 0, 0 }, 159 { 0, 0, 1, 1, 160 10, 0, 1, 1, 161 20, 0, 1, 1 }, 162 }, 163 { 164 // empty triangle 165 { 0, 0, 0 }, { 0, 0, 0 }, 166 { 0, 0, 1, 1, 167 10, 0, 1, 1, 168 20, 0, 1, 1 }, 169 }, 170 }; 171 172 public static void render(Graphics2D g2d) { 173 g2d.setColor(Color.white); 174 g2d.fillRect(0, 0, TESTWIDTH, TESTHEIGHT); 175 g2d.setColor(Color.black); 176 177 if (forceerror) { 178 g2d.fillRect(2, 2, 2, 2); 179 g2d.fillRect(15, 5, 1, 1); 180 } 181 182 if (!fulltest) { 183 g2d.draw(new Rectangle2D.Double(5, 5, 0, 0)); 184 return; 185 } 186 187 g2d.drawLine(10, 10, 10, 10); 188 g2d.draw(new Line2D.Double(20, 10, 20, 10)); 189 190 g2d.drawRect(10, 20, 0, 0); 191 g2d.draw(new Rectangle2D.Double(20, 20, 0, 0)); 192 193 g2d.setXORMode(Color.white); 194 195 g2d.drawLine(10, 30, 10, 30); 196 g2d.draw(new Line2D.Double(20, 30, 20, 30)); 197 198 g2d.drawRect(10, 40, 0, 0); 199 g2d.draw(new Rectangle2D.Double(20, 40, 0, 0)); 200 201 g2d.setPaintMode(); 202 203 int y = 50; 204 for (int i = 0; i < polypts.length; i++) { 205 int data[][] = polypts[i]; 206 int xpoints[] = data[0]; 207 int ypoints[] = data[1]; 208 int npoints = xpoints.length; 209 g2d.translate(10, y); 210 g2d.drawPolyline(xpoints, ypoints, npoints); 211 g2d.translate(10, 0); 212 g2d.drawPolygon(xpoints, ypoints, npoints); 213 g2d.translate(10, 0); 214 g2d.draw(new Polygon(xpoints, ypoints, npoints)); 215 g2d.translate(-30, -y); 216 y += 10; 217 } 218 g2d.setXORMode(Color.white); 219 for (int i = 0; i < polypts.length; i++) { 220 int data[][] = polypts[i]; 221 int xpoints[] = data[0]; 222 int ypoints[] = data[1]; 223 int npoints = xpoints.length; 224 g2d.translate(10, y); 225 g2d.drawPolyline(xpoints, ypoints, npoints); 226 g2d.translate(10, 0); 227 g2d.drawPolygon(xpoints, ypoints, npoints); 228 g2d.translate(10, 0); 229 g2d.draw(new Polygon(xpoints, ypoints, npoints)); 230 g2d.translate(-30, -y); 231 y += 10; 232 } 233 g2d.setPaintMode(); 234 } 235 236 public Dimension getPreferredSize() { 237 return new Dimension(500, 500); 238 } 239 240 public static void usage(int exitcode) { 241 System.err.println("usage: java PolyVertTest [<option>]*"); 242 System.err.println(" -usage "+ 243 "print this usage summary"); 244 System.err.println(" -count "+ 245 "run all tests and accumulate error counts"); 246 System.err.println(" -forceerror "+ 247 "force at least one error in each test"); 248 System.err.println(" -fringe "+ 249 "draw a yellow fringe around problems"); 250 System.err.println(" -showerrors "+ 251 "display results window for tests with problems"); 252 System.err.println(" -showresults "+ 253 "display results window for all tests"); 254 System.err.println(" -quicktest "+ 255 "only run test cases reported in bug reports"); 256 System.err.println(" -fulltest "+ 257 "run full suite of test cases for a 'unit test'"); 258 System.err.println(" -hwonly "+ 259 "only run tests for screen and VolatileImage"); 260 System.exit(exitcode); 261 } 262 263 public static void main(String argv[]) { 264 for (int i = 0; i < argv.length; i++) { 265 String arg = argv[i]; 266 if (arg.equalsIgnoreCase("-count")) { 267 counting = true; 268 } else if (arg.equalsIgnoreCase("-forceerror")) { 269 forceerror = true; 270 } else if (arg.equalsIgnoreCase("-fringe")) { 271 fringe = true; 272 } else if (arg.equalsIgnoreCase("-showerrors")) { 273 showerrors = true; 274 } else if (arg.equalsIgnoreCase("-showresults")) { 275 showresults = true; 276 } else if (arg.equalsIgnoreCase("-quicktest")) { 277 fulltest = false; 278 } else if (arg.equalsIgnoreCase("-fulltest")) { 279 fulltest = true; 280 } else if (arg.equalsIgnoreCase("-hwonly")) { 281 hwonly = true; 282 } else if (arg.equalsIgnoreCase("-usage")) { 283 usage(0); 284 } else { 285 System.err.println("unknown option: "+arg); 286 usage(1); 287 } 288 } 289 290 if (fulltest) { 291 TESTWIDTH = FULL_TEST_WIDTH; 292 TESTHEIGHT = FULL_TEST_HEIGHT; 293 } else { 294 TESTWIDTH = REG_TEST_WIDTH; 295 TESTHEIGHT = REG_TEST_HEIGHT; 296 } 297 298 // Prevents premature exit by the WindowAdapter if the user 299 // closes the last visible results window before we've 300 // finished our tests. 301 numframes++; 302 303 makeReferenceImage(); 304 testScreen(); 305 testVolatileImage(); 306 if (!hwonly) { 307 testBufferedImage(); 308 testOffscreen(); 309 testCompatibleImages(); 310 } 311 if (totalfuzzypixels > 0) { 312 System.err.println(totalfuzzypixels+" fuzzy pixels found in "+ 313 numfuzzytests+" tests"); 314 } 315 if (totalbadpixels > 0) { 316 throw new RuntimeException(totalbadpixels+" bad pixels found in "+ 317 numbadtests+" tests"); 318 } 319 System.out.println("Test done - no bad pixels found"); 320 321 --numframes; 322 323 if (counting || ((showresults || showerrors) && numframes == 0)) { 324 System.exit(0); 325 } 326 } 327 328 public static void makeReferenceImage() { 329 refImg = new BufferedImage(TESTWIDTH, TESTHEIGHT, 330 BufferedImage.TYPE_INT_RGB); 331 Graphics g = refImg.getGraphics(); 332 333 g.setColor(Color.white); 334 g.fillRect(0, 0, TESTWIDTH, TESTHEIGHT); 335 336 g.setColor(Color.black); 337 338 if (!fulltest) { 339 g.fillRect(5, 5, 1, 1); 340 g.dispose(); 341 return; 342 } 343 344 for (int y = 10; y < 50; y += 10) { 345 g.fillRect(10, y, 1, 1); 346 g.fillRect(20, y, 1, 1); 347 } 348 int y = 50; 349 for (int i = 0; i < polypts.length; i++) { 350 int data[][] = polypts[i]; 351 g.translate(10, y); 352 if (data.length > 2) { 353 int rectvals[] = data[2]; 354 for (int j = 0; j < rectvals.length; j += 4) { 355 g.fillRect(rectvals[j+0], rectvals[j+1], 356 rectvals[j+2], rectvals[j+3]); 357 } 358 } 359 g.translate(-10, -y); 360 y += 10; 361 } 362 fuzzystarty = y; 363 for (int i = 0; i < polypts.length; i++) { 364 int data[][] = polypts[i]; 365 g.translate(10, y); 366 if (data.length > 2) { 367 int rectvals[] = data.length > 3 ? data[3] : data[2]; 368 for (int j = 0; j < rectvals.length; j += 4) { 369 g.fillRect(rectvals[j+0], rectvals[j+1], 370 rectvals[j+2], rectvals[j+3]); 371 } 372 } 373 g.translate(-10, -y); 374 y += 10; 375 } 376 g.dispose(); 377 } 378 379 public static void initerrorbuf() { 380 if (errorImg == null) { 381 droperrorbuf(); 382 errorImg = new BufferedImage(TESTWIDTH, TESTHEIGHT, 383 BufferedImage.TYPE_INT_RGB); 384 } 385 if (errorG == null) { 386 errorG = errorImg.getGraphics(); 387 } 388 errorG.setColor(Color.green); 389 errorG.fillRect(0, 0, TESTWIDTH, TESTHEIGHT); 390 errorG.setColor(Color.red); 391 } 392 393 public static void droperrorbuf() { 394 errorImg = null; 395 if (errorG != null) { 396 errorG.dispose(); 397 } 398 errorG = null; 399 } 400 401 public static void test(Image img, String name) { 402 Graphics2D g2d = (Graphics2D) img.getGraphics(); 403 render(g2d); 404 g2d.dispose(); 405 verify(img, name); 406 } 407 408 public static void test(BufferedImage bimg, String name) { 409 Graphics2D g2d = bimg.createGraphics(); 410 render(g2d); 411 g2d.dispose(); 412 verify(bimg, name); 413 } 414 415 public static void verify(Image img, String name) { 416 BufferedImage bimg; 417 if (img instanceof BufferedImage) { 418 bimg = (BufferedImage) img; 419 } else { 420 bimg = new BufferedImage(TESTWIDTH, TESTHEIGHT, 421 BufferedImage.TYPE_INT_RGB); 422 Graphics g = bimg.getGraphics(); 423 g.drawImage(img, 0, 0, null); 424 g.dispose(); 425 } 426 verify(bimg, name); 427 } 428 429 public static boolean isFuzzyPixel(int X, int Y) { 430 int ytrans = fuzzystarty; 431 if (!fulltest || Y < ytrans) { 432 return false; 433 } 434 for (int i = 0; i < polypts.length; i++) { 435 int data[][] = polypts[i]; 436 if (data.length > 4) { 437 int rectvals[] = data[4]; 438 for (int j = 0; j < rectvals.length; j += 4) { 439 int rectx = rectvals[j+0] + 10; 440 int recty = rectvals[j+1] + ytrans; 441 int rectw = rectvals[j+2]; 442 int recth = rectvals[j+3]; 443 if (X >= rectx && Y >= recty && 444 X < rectx + rectw && Y < recty + recth) 445 { 446 return true; 447 } 448 } 449 } 450 ytrans += 10; 451 } 452 return false; 453 } 454 455 public static void verify(BufferedImage bimg, String name) { 456 int numbadpixels = 0; 457 int numfuzzypixels = 0; 458 for (int y = 0; y < TESTHEIGHT; y++) { 459 for (int x = 0; x < TESTWIDTH; x++) { 460 if (refImg.getRGB(x, y) != bimg.getRGB(x, y)) { 461 boolean isfuzzy = isFuzzyPixel(x, y); 462 if (showerrors || showresults) { 463 if (errorG == null) { 464 initerrorbuf(); 465 } 466 errorG.setColor(isfuzzy ? Color.blue : Color.red); 467 errorG.fillRect(x, y, 1, 1); 468 } else if (!counting && !isfuzzy) { 469 throw new RuntimeException("Error at "+x+", "+y+ 470 " while testing: "+name); 471 } 472 if (isfuzzy) { 473 numfuzzypixels++; 474 } else { 475 numbadpixels++; 476 } 477 } 478 } 479 } 480 if (numbadpixels > 0 || numfuzzypixels > 0) { 481 if (numbadpixels > 0) { 482 totalbadpixels += numbadpixels; 483 numbadtests++; 484 } 485 if (numfuzzypixels > 0) { 486 totalfuzzypixels += numfuzzypixels; 487 numfuzzytests++; 488 } 489 System.out.println(numbadpixels+" bad pixels and "+ 490 numfuzzypixels+" questionable pixels "+ 491 "found while testing "+name); 492 if (showerrors || showresults) { 493 displaydiffs(bimg, name); 494 } 495 } else if (showresults) { 496 if (errorG == null) { 497 initerrorbuf(); 498 } 499 displaydiffs(bimg, name); 500 } 501 } 502 503 public static void displaydiffs(BufferedImage bimg, String name) { 504 if (fringe) { 505 errorG.setColor(Color.yellow); 506 for (int y = 0; y < TESTHEIGHT; y++) { 507 for (int x = 0; x < TESTWIDTH; x++) { 508 if (errorImg.getRGB(x, y) == RED) { 509 for (int iy = y-FRINGE; iy <= y+FRINGE; iy++) { 510 for (int ix = x-FRINGE; ix <= x+FRINGE; ix++) { 511 if (ix >= 0 && ix < TESTWIDTH && 512 iy >= 0 && iy < TESTHEIGHT && 513 errorImg.getRGB(ix, iy) == GREEN) 514 { 515 errorG.fillRect(ix, iy, 1, 1); 516 } 517 } 518 } 519 } 520 } 521 } 522 } 523 Frame f = new Frame("Results for "+name); 524 f.setLayout(new BorderLayout()); 525 f.addWindowListener(windowCloser); 526 ++numframes; 527 Panel p = new Panel(); 528 p.add(new ImageCanvas(bimg)); 529 p.add(new ImageCanvas(errorImg)); 530 p.add(new ImageCanvas(refImg)); 531 f.add(p, "Center"); 532 droperrorbuf(); 533 f.pack(); 534 f.show(); 535 } 536 537 public static void testBufferedImage() { 538 testBufferedImage(BufferedImage.TYPE_INT_RGB, "IntXrgb"); 539 testBufferedImage(BufferedImage.TYPE_INT_ARGB, "IntArgb"); 540 testBufferedImage(BufferedImage.TYPE_3BYTE_BGR, "ThreeByte"); 541 testBufferedImage(BufferedImage.TYPE_4BYTE_ABGR, "FourByte"); 542 testBufferedImage(BufferedImage.TYPE_USHORT_555_RGB, "UShort555"); 543 testBufferedImage(BufferedImage.TYPE_BYTE_GRAY, "ByteGray"); 544 testBufferedImage(BufferedImage.TYPE_BYTE_INDEXED, "Indexed"); 545 } 546 547 public static void testBufferedImage(int type, String name) { 548 BufferedImage bimg = new BufferedImage(TESTWIDTH, TESTHEIGHT, type); 549 test(bimg, name); 550 } 551 552 public static void testScreen() { 553 Frame f = new Frame("PolyVertTest"); 554 TestCanvas child = new TestCanvas(); 555 testCanvas = child; 556 f.add(child); 557 f.pack(); 558 f.show(); 559 BufferedImage bimg = child.getImage(); 560 f.hide(); 561 verify(bimg, "Screen"); 562 } 563 564 public static void testOffscreen() { 565 Image img = testCanvas.createImage(TESTWIDTH, TESTHEIGHT); 566 test(img, "Offscreen"); 567 } 568 569 public static void testCompatibleImages() { 570 GraphicsEnvironment genv = 571 GraphicsEnvironment.getLocalGraphicsEnvironment(); 572 GraphicsDevice gdevs[] = genv.getScreenDevices(); 573 for (int i = 0; i < gdevs.length; i++) { 574 testCompatibleImages(gdevs[i]); 575 } 576 } 577 578 public static void testCompatibleImages(GraphicsDevice gdev) { 579 GraphicsConfiguration gconfigs[] = gdev.getConfigurations(); 580 for (int i = 0; i < gconfigs.length; i++) { 581 testCompatibleImages(gconfigs[i]); 582 } 583 } 584 585 public static void testCompatibleImages(GraphicsConfiguration gconfig) { 586 test(gconfig.createCompatibleImage(TESTWIDTH, TESTHEIGHT), 587 gconfig+".createCompat()"); 588 test(gconfig.createCompatibleImage(TESTWIDTH, TESTHEIGHT, 589 Transparency.OPAQUE), 590 gconfig+".createCompat(OPAQUE)"); 591 test(gconfig.createCompatibleImage(TESTWIDTH, TESTHEIGHT, 592 Transparency.BITMASK), 593 gconfig+".createCompat(BITMASK)"); 594 test(gconfig.createCompatibleImage(TESTWIDTH, TESTHEIGHT, 595 Transparency.TRANSLUCENT), 596 gconfig+".createCompat(TRANSLUCENT)"); 597 test(gconfig.createCompatibleVolatileImage(TESTWIDTH, TESTHEIGHT), 598 gconfig+".createCompatVolatile()"); 599 } 600 601 public static void testVolatileImage() { 602 Image img = testCanvas.createVolatileImage(TESTWIDTH, TESTHEIGHT); 603 test(img, "Volatile"); 604 } 605 606 public static class ImageCanvas extends Canvas { 607 BufferedImage bimg; 608 609 public ImageCanvas(BufferedImage bimg) { 610 this.bimg = bimg; 611 } 612 613 public Dimension getPreferredSize() { 614 return new Dimension(bimg.getWidth(), bimg.getHeight()); 615 } 616 617 public void paint(Graphics g) { 618 g.drawImage(bimg, 0, 0, null); 619 } 620 } 621 622 public static class TestCanvas extends Canvas { 623 BufferedImage bimg; 624 625 public Dimension getPreferredSize() { 626 return new Dimension(TESTWIDTH, TESTHEIGHT); 627 } 628 629 public void paint(Graphics g) { 630 if (bimg != null || 631 getWidth() < TESTWIDTH || 632 getHeight() < TESTHEIGHT) 633 { 634 return; 635 } 636 render((Graphics2D) g); 637 Toolkit.getDefaultToolkit().sync(); 638 Point p = getLocationOnScreen(); 639 Rectangle r = new Rectangle(p.x, p.y, TESTWIDTH, TESTHEIGHT); 640 try { 641 bimg = new Robot().createScreenCapture(r); 642 } catch (AWTException e) { 643 e.printStackTrace(); 644 } 645 synchronized (this) { 646 notifyAll(); 647 } 648 } 649 650 public synchronized BufferedImage getImage() { 651 while (bimg == null) { 652 try { 653 wait(); 654 } catch (InterruptedException e) { 655 return null; 656 } 657 } 658 return bimg; 659 } 660 } 661 }