< prev index next >

test/jdk/sun/java2d/marlin/ClipShapeTest.java

Print this page




  37 import java.util.Arrays;
  38 import java.util.Iterator;
  39 import java.util.Locale;
  40 import java.util.Random;
  41 import java.util.concurrent.atomic.AtomicBoolean;
  42 import java.util.concurrent.atomic.AtomicInteger;
  43 import java.util.logging.Handler;
  44 import java.util.logging.LogRecord;
  45 import java.util.logging.Logger;
  46 import javax.imageio.IIOImage;
  47 import javax.imageio.ImageIO;
  48 import javax.imageio.ImageWriteParam;
  49 import javax.imageio.ImageWriter;
  50 import javax.imageio.stream.ImageOutputStream;
  51 
  52 /**
  53  * @test
  54  * @bug 8191814
  55  * @summary Verifies that Marlin rendering generates the same
  56  * images with and without clipping optimization with all possible
  57  * stroke (cap/join) and fill modes (EO rules)

  58  * Note: Use the argument -slow to run more intensive tests (too much time)
  59  * @run main/othervm/timeout=120 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest
  60  * @run main/othervm/timeout=120 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest
  61  */







  62 public final class ClipShapeTest {
  63 



  64     static final boolean TEST_STROKER = true;
  65     static final boolean TEST_FILLER = true;
  66 
  67     // complementary tests in slow mode:
  68     static boolean USE_DASHES = false;
  69     static boolean USE_VAR_STROKE = false;
  70 
  71     static int NUM_TESTS = 5000;
  72     static final int TESTW = 100;
  73     static final int TESTH = 100;
  74 
  75     // shape settings:
  76     static final ShapeMode SHAPE_MODE = ShapeMode.NINE_LINE_POLYS;




  77     static final boolean SHAPE_REPEAT = true;
  78 
  79     // dump path on console:
  80     static final boolean DUMP_SHAPE = true;
  81 
  82     static final boolean SHOW_DETAILS = true;
  83     static final boolean SHOW_OUTLINE = true;
  84     static final boolean SHOW_POINTS = true;
  85     static final boolean SHOW_INFO = false;
  86 
  87     static final int MAX_SHOW_FRAMES = 10;

  88 
  89     // use fixed seed to reproduce always same polygons between tests
  90     static final boolean FIXED_SEED = false;
  91     static final double RAND_SCALE = 3.0;
  92     static final double RANDW = TESTW * RAND_SCALE;
  93     static final double OFFW = (TESTW - RANDW) / 2.0;
  94     static final double RANDH = TESTH * RAND_SCALE;
  95     static final double OFFH = (TESTH - RANDH) / 2.0;
  96 
  97     static enum ShapeMode {
  98         TWO_CUBICS,
  99         FOUR_QUADS,
 100         FIVE_LINE_POLYS,
 101         NINE_LINE_POLYS,
 102         FIFTY_LINE_POLYS,
 103         MIXED
 104     }
 105 
 106     static final long SEED = 1666133789L;
 107     // Fixed seed to avoid any difference between runs:
 108     static final Random RANDOM = new Random(SEED);
 109 
 110     static final File OUTPUT_DIR = new File(".");
 111 
 112     /**
 113      * Test
 114      * @param args
 115      */
 116     public static void main(String[] args) {
 117         boolean runSlowTests = (args.length != 0 && "-slow".equals(args[0]));
 118 
 119         if (runSlowTests) {
 120             NUM_TESTS = 20000; // or 100000 (very slow)
 121             USE_DASHES = true;
 122             USE_VAR_STROKE = true;
 123         }
 124 

 125         Locale.setDefault(Locale.US);
 126 
 127         // Get Marlin runtime state from its log:
 128         final AtomicBoolean isMarlin = new AtomicBoolean();
 129         final AtomicBoolean isClipRuntime = new AtomicBoolean();
 130 
 131         // initialize j.u.l Looger:
 132         final Logger log = Logger.getLogger("sun.java2d.marlin");
 133         log.addHandler(new Handler() {
 134             @Override
 135             public void publish(LogRecord record) {
 136                 final String msg = record.getMessage();
 137                 if (msg != null) {
 138                     // last space to avoid matching other settings:
 139                     if (msg.startsWith("sun.java2d.renderer ")) {
 140                         isMarlin.set(msg.contains("MarlinRenderingEngine"));
 141                     }
 142                     if (msg.startsWith("sun.java2d.renderer.clip.runtime.enable")) {
 143                         isClipRuntime.set(msg.contains("true"));
 144                     }
 145                 }
 146 
 147                 final Throwable th = record.getThrown();
 148                 // detect any Throwable:
 149                 if (th != null) {


 154                 }
 155             }
 156 
 157             @Override
 158             public void flush() {
 159             }
 160 
 161             @Override
 162             public void close() throws SecurityException {
 163             }
 164         });
 165 
 166         // enable Marlin logging & internal checks:
 167         System.setProperty("sun.java2d.renderer.log", "true");
 168         System.setProperty("sun.java2d.renderer.useLogger", "true");
 169 
 170         // disable static clipping setting:
 171         System.setProperty("sun.java2d.renderer.clip", "false");
 172         System.setProperty("sun.java2d.renderer.clip.runtime.enable", "true");
 173 

























































































 174         System.out.println("ClipShapeTests: image = " + TESTW + " x " + TESTH);
 175 
 176         int failures = 0;
 177         final long start = System.nanoTime();
 178         try {
 179             // TODO: test affine transforms ?
 180 
 181             if (TEST_STROKER) {
 182                 final float[][] dashArrays = (USE_DASHES)
 183                         ? new float[][]{null, new float[]{1f, 2f}}






 184                         : new float[][]{null};
 185 
 186                 System.out.println("dashes: " + Arrays.toString(dashArrays));
 187 
 188                 final float[] strokeWidths = (USE_VAR_STROKE)
 189                         ? new float[5] : new float[]{8f};

 190 
 191                 int nsw = 0;
 192                 if (USE_VAR_STROKE) {
 193                     for (float width = 0.1f; width < 110f; width *= 5f) {
 194                         strokeWidths[nsw++] = width;
 195                     }
 196                 } else {
 197                     nsw = 1;
 198                 }
 199 
 200                 System.out.println("stroke widths: " + Arrays.toString(strokeWidths));
 201 
 202                 // Stroker tests:
 203                 for (int w = 0; w < nsw; w++) {
 204                     final float width = strokeWidths[w];
 205 
 206                     for (float[] dashes : dashArrays) {
 207 
 208                         for (int cap = 0; cap <= 2; cap++) {
 209 


 273             for (int n = 0; n < NUM_TESTS; n++) {
 274                 genShape(p2d, ts);
 275 
 276                 // Runtime clip setting OFF:
 277                 paintShape(p2d, g2dOff, fill, false);
 278 
 279                 // Runtime clip setting ON:
 280                 paintShape(p2d, g2dOn, fill, true);
 281 
 282                 /* compute image difference if possible */
 283                 diffImage = computeDiffImage(testCtx, imgOn, imgOff, imgDiff, globalCtx);
 284 
 285                 final String testName = "Setup_" + ts.id + "_test_" + n;
 286 
 287                 if (diffImage != null) {
 288                     nd++;
 289 
 290                     final double ratio = (100.0 * testCtx.histPix.count) / testCtx.histAll.count;
 291                     System.out.println("Diff ratio: " + testName + " = " + trimTo3Digits(ratio) + " %");
 292 
 293                     if (false) {
 294                         saveImage(diffImage, OUTPUT_DIR, testName + "-diff.png");
 295                     }
 296 
 297                     if (DUMP_SHAPE) {
 298                         dumpShape(p2d);
 299                     }
 300                     if (nd < MAX_SHOW_FRAMES) {
 301                         if (SHOW_DETAILS) {
 302                             paintShapeDetails(g2dOff, p2d);
 303                             paintShapeDetails(g2dOn, p2d);
 304                         }
 305 
 306                         saveImage(imgOff, OUTPUT_DIR, testName + "-off.png");
 307                         saveImage(imgOn, OUTPUT_DIR, testName + "-on.png");
 308                         saveImage(diffImage, OUTPUT_DIR, testName + "-diff.png");





 309                     }
 310                 }
 311             }
 312         } finally {
 313             g2dOff.dispose();
 314             g2dOn.dispose();
 315 
 316             if (nd != 0) {
 317                 System.out.println("paintPaths: " + NUM_TESTS + " paths - "
 318                         + "Number of differences = " + nd
 319                         + " ratio = " + (100f * nd) / NUM_TESTS + " %");
 320             }
 321 
 322             globalCtx.dump();
 323         }
 324         System.out.println("paintPaths: duration= " + (1e-6 * (System.nanoTime() - start)) + " ms.");
 325         return nd;
 326     }
 327 
 328     private static void paintShape(final Path2D p2d, final Graphics2D g2d,


 334         if (fill) {
 335             g2d.fill(p2d);
 336         } else {
 337             g2d.draw(p2d);
 338         }
 339     }
 340 
 341     private static Graphics2D initialize(final BufferedImage img,
 342                                          final TestSetup ts) {
 343         final Graphics2D g2d = (Graphics2D) img.getGraphics();
 344         g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
 345                 RenderingHints.VALUE_RENDER_QUALITY);
 346         g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
 347                 RenderingHints.VALUE_STROKE_PURE);
 348 
 349         if (ts.isStroke()) {
 350             g2d.setStroke(createStroke(ts));
 351         }
 352         g2d.setColor(Color.GRAY);
 353 









 354         return g2d;
 355     }
 356 
 357     private static void reset(final Graphics2D g2d) {
 358         // Disable antialiasing:
 359         g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 360                 RenderingHints.VALUE_ANTIALIAS_OFF);
 361         g2d.setBackground(Color.WHITE);
 362         g2d.clearRect(0, 0, TESTW, TESTH);
 363     }
 364 
 365     private static void setClip(final Graphics2D g2d, final boolean clip) {
 366         // Enable antialiasing:
 367         g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 368                 RenderingHints.VALUE_ANTIALIAS_ON);
 369 
 370         // Enable or Disable clipping:
 371         System.setProperty("sun.java2d.renderer.clip.runtime", (clip) ? "true" : "false");
 372     }
 373 


 453             g2d.setColor(COLOR_LINETO_ODD);
 454             g2d.draw(shape);
 455         }
 456 
 457         final float[] coords = new float[6];
 458         float px, py;
 459 
 460         int nMove = 0;
 461         int nLine = 0;
 462         int n = 0;
 463 
 464         for (final PathIterator it = shape.getPathIterator(null); !it.isDone(); it.next()) {
 465             int type = it.currentSegment(coords);
 466             switch (type) {
 467                 case PathIterator.SEG_MOVETO:
 468                     if (SHOW_POINTS) {
 469                         g2d.setColor(COLOR_MOVETO);
 470                     }
 471                     break;
 472                 case PathIterator.SEG_LINETO:


 473                     if (SHOW_POINTS) {
 474                         g2d.setColor((nLine % 2 == 0) ? COLOR_LINETO_ODD : COLOR_LINETO_EVEN);
 475                     }
 476                     nLine++;
 477                     break;
 478                 case PathIterator.SEG_CLOSE:
 479                     continue;
 480                 default:
 481                     System.out.println("unsupported segment type= " + type);
 482                     continue;
 483             }
 484             px = coords[0];
 485             py = coords[1];
 486 
 487             if (SHOW_INFO) {
 488                 System.out.println("point[" + (n++) + "|seg=" + type + "]: " + px + " " + py);
 489             }
 490 
 491             if (SHOW_POINTS) {
 492                 ELL_POINT.setFrame(px - POINT_RADIUS, py - POINT_RADIUS,


 498             System.out.println("Path moveTo=" + nMove + ", lineTo=" + nLine);
 499             System.out.println("--------------------------------------------------");
 500         }
 501 
 502         g2d.setStroke(oldStroke);
 503         g2d.setColor(oldColor);
 504     }
 505 
 506     private static void dumpShape(final Shape shape) {
 507         final float[] coords = new float[6];
 508 
 509         for (final PathIterator it = shape.getPathIterator(null); !it.isDone(); it.next()) {
 510             final int type = it.currentSegment(coords);
 511             switch (type) {
 512                 case PathIterator.SEG_MOVETO:
 513                     System.out.println("p2d.moveTo(" + coords[0] + ", " + coords[1] + ");");
 514                     break;
 515                 case PathIterator.SEG_LINETO:
 516                     System.out.println("p2d.lineTo(" + coords[0] + ", " + coords[1] + ");");
 517                     break;






 518                 case PathIterator.SEG_CLOSE:
 519                     System.out.println("p2d.closePath();");
 520                     break;
 521                 default:
 522                     System.out.println("// Unsupported segment type= " + type);
 523             }
 524         }
 525         System.out.println("--------------------------------------------------");
 526     }
 527 
 528     static double randX() {
 529         return RANDOM.nextDouble() * RANDW + OFFW;
 530     }
 531 
 532     static double randY() {
 533         return RANDOM.nextDouble() * RANDH + OFFH;
 534     }
 535 
 536     private static BasicStroke createStroke(final TestSetup ts) {
 537         return new BasicStroke(ts.strokeWidth, ts.strokeCap, ts.strokeJoin, 10.0f, ts.dashes, 0.0f);


 563             this.dashes = dashes;
 564             this.windingRule = Path2D.WIND_NON_ZERO;
 565         }
 566 
 567         TestSetup(ShapeMode shapeMode, final boolean closed, final int windingRule) {
 568             this.id = COUNT.incrementAndGet();
 569             this.shapeMode = shapeMode;
 570             this.closed = closed;
 571             this.strokeWidth = 0f;
 572             this.strokeCap = this.strokeJoin = -1; // invalid
 573             this.dashes = null;
 574             this.windingRule = windingRule;
 575         }
 576 
 577         boolean isStroke() {
 578             return this.strokeWidth > 0f;
 579         }
 580 
 581         @Override
 582         public String toString() {






 583             return "TestSetup{id=" + id + ", shapeMode=" + shapeMode + ", closed=" + closed
 584                     + ", strokeWidth=" + strokeWidth + ", strokeCap=" + strokeCap + ", strokeJoin=" + strokeJoin
 585                     + ((dashes != null) ? ", dashes: " + Arrays.toString(dashes) : "")
 586                     + ", windingRule=" + windingRule + '}';






































 587         }
 588     }
 589 
 590     // --- utilities ---
 591     private static final int DCM_ALPHA_MASK = 0xff000000;
 592 
 593     public static BufferedImage newImage(final int w, final int h) {
 594         return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
 595     }
 596 
 597     public static BufferedImage computeDiffImage(final DiffContext localCtx,
 598                                                  final BufferedImage tstImage,
 599                                                  final BufferedImage refImage,
 600                                                  final BufferedImage diffImage,
 601                                                  final DiffContext globalCtx) {
 602 
 603         final int[] aRefPix = ((DataBufferInt) refImage.getRaster().getDataBuffer()).getData();
 604         final int[] aTstPix = ((DataBufferInt) tstImage.getRaster().getDataBuffer()).getData();
 605         final int[] aDifPix = ((DataBufferInt) diffImage.getRaster().getDataBuffer()).getData();
 606 
 607         // reset local diff context:
 608         localCtx.reset();
 609 
 610         int ref, tst, dg, v;
 611         for (int i = 0, len = aRefPix.length; i < len; i++) {
 612             ref = aRefPix[i];
 613             tst = aTstPix[i];
 614 
 615             // grayscale diff:
 616             dg = (r(ref) + g(ref) + b(ref)) - (r(tst) + g(tst) + b(tst));
 617 
 618             // max difference on grayscale values:
 619             v = (int) Math.ceil(Math.abs(dg / 3.0));
 620 
 621             aDifPix[i] = toInt(v, v, v);




 622 
 623             localCtx.add(v);

 624             globalCtx.add(v);
 625         }
 626 
 627         if (!localCtx.isDiff()) {
 628             return null;
 629         }


 630 
 631         return diffImage;
 632     }
 633 
 634     static void saveImage(final BufferedImage image, final File resDirectory, final String imageFileName) throws IOException {
 635         final Iterator<ImageWriter> itWriters = ImageIO.getImageWritersByFormatName("PNG");
 636         if (itWriters.hasNext()) {
 637             final ImageWriter writer = itWriters.next();
 638 
 639             final ImageWriteParam writerParams = writer.getDefaultWriteParam();
 640             writerParams.setProgressiveMode(ImageWriteParam.MODE_DISABLED);
 641 
 642             final File imgFile = new File(resDirectory, imageFileName);
 643 
 644             if (!imgFile.exists() || imgFile.canWrite()) {
 645                 System.out.println("saveImage: saving image as PNG [" + imgFile + "]...");
 646                 imgFile.delete();
 647 
 648                 // disable cache in temporary files:
 649                 ImageIO.setUseCache(false);




  37 import java.util.Arrays;
  38 import java.util.Iterator;
  39 import java.util.Locale;
  40 import java.util.Random;
  41 import java.util.concurrent.atomic.AtomicBoolean;
  42 import java.util.concurrent.atomic.AtomicInteger;
  43 import java.util.logging.Handler;
  44 import java.util.logging.LogRecord;
  45 import java.util.logging.Logger;
  46 import javax.imageio.IIOImage;
  47 import javax.imageio.ImageIO;
  48 import javax.imageio.ImageWriteParam;
  49 import javax.imageio.ImageWriter;
  50 import javax.imageio.stream.ImageOutputStream;
  51 
  52 /**
  53  * @test
  54  * @bug 8191814
  55  * @summary Verifies that Marlin rendering generates the same
  56  * images with and without clipping optimization with all possible
  57  * stroke (cap/join) and/or dashes or fill modes (EO rules)
  58  * for paths made of either 9 lines, 4 quads, 2 cubics (random)
  59  * Note: Use the argument -slow to run more intensive tests (too much time)
  60  *
  61  * @run main/othervm/timeout=120 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -poly
  62  * @run main/othervm/timeout=240 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -poly -doDash
  63  * @run main/othervm/timeout=120 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -cubic
  64  * @run main/othervm/timeout=240 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -cubic -doDash
  65  * @run main/othervm/timeout=120 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -poly
  66  * @run main/othervm/timeout=240 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -poly -doDash
  67  * @run main/othervm/timeout=120 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -cubic
  68  * @run main/othervm/timeout=240 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -cubic -doDash
  69 */
  70 public final class ClipShapeTest {
  71 
  72     static boolean TX_SCALE = false;
  73     static boolean TX_SHEAR = false;
  74 
  75     static final boolean TEST_STROKER = true;
  76     static final boolean TEST_FILLER = true;
  77 
  78     // complementary tests in slow mode:
  79     static boolean USE_DASHES = false;
  80     static boolean USE_VAR_STROKE = false;
  81 
  82     static int NUM_TESTS = 5000;
  83     static final int TESTW = 100;
  84     static final int TESTH = 100;
  85 
  86     // shape settings:
  87     static ShapeMode SHAPE_MODE = ShapeMode.NINE_LINE_POLYS;
  88 
  89     static int THRESHOLD_DELTA;
  90     static long THRESHOLD_NBPIX;
  91 
  92     static final boolean SHAPE_REPEAT = true;
  93 
  94     // dump path on console:
  95     static final boolean DUMP_SHAPE = true;
  96 
  97     static final boolean SHOW_DETAILS = false; // disabled
  98     static final boolean SHOW_OUTLINE = true;
  99     static final boolean SHOW_POINTS = true;
 100     static final boolean SHOW_INFO = false;
 101 
 102     static final int MAX_SHOW_FRAMES = 10;
 103     static final int MAX_SAVE_FRAMES = 100;
 104 
 105     // use fixed seed to reproduce always same polygons between tests
 106     static final boolean FIXED_SEED = false;
 107     static final double RAND_SCALE = 3.0;
 108     static final double RANDW = TESTW * RAND_SCALE;
 109     static final double OFFW = (TESTW - RANDW) / 2.0;
 110     static final double RANDH = TESTH * RAND_SCALE;
 111     static final double OFFH = (TESTH - RANDH) / 2.0;
 112 
 113     static enum ShapeMode {
 114         TWO_CUBICS,
 115         FOUR_QUADS,
 116         FIVE_LINE_POLYS,
 117         NINE_LINE_POLYS,
 118         FIFTY_LINE_POLYS,
 119         MIXED
 120     }
 121 
 122     static final long SEED = 1666133789L;
 123     // Fixed seed to avoid any difference between runs:
 124     static final Random RANDOM = new Random(SEED);
 125 
 126     static final File OUTPUT_DIR = new File(".");
 127 
 128     static final AtomicBoolean isMarlin = new AtomicBoolean();
 129     static final AtomicBoolean isClipRuntime = new AtomicBoolean();










 130 
 131     static {
 132         Locale.setDefault(Locale.US);
 133 
 134         // FIRST: Get Marlin runtime state from its log:


 135 
 136         // initialize j.u.l Looger:
 137         final Logger log = Logger.getLogger("sun.java2d.marlin");
 138         log.addHandler(new Handler() {
 139             @Override
 140             public void publish(LogRecord record) {
 141                 final String msg = record.getMessage();
 142                 if (msg != null) {
 143                     // last space to avoid matching other settings:
 144                     if (msg.startsWith("sun.java2d.renderer ")) {
 145                         isMarlin.set(msg.contains("MarlinRenderingEngine"));
 146                     }
 147                     if (msg.startsWith("sun.java2d.renderer.clip.runtime.enable")) {
 148                         isClipRuntime.set(msg.contains("true"));
 149                     }
 150                 }
 151 
 152                 final Throwable th = record.getThrown();
 153                 // detect any Throwable:
 154                 if (th != null) {


 159                 }
 160             }
 161 
 162             @Override
 163             public void flush() {
 164             }
 165 
 166             @Override
 167             public void close() throws SecurityException {
 168             }
 169         });
 170 
 171         // enable Marlin logging & internal checks:
 172         System.setProperty("sun.java2d.renderer.log", "true");
 173         System.setProperty("sun.java2d.renderer.useLogger", "true");
 174 
 175         // disable static clipping setting:
 176         System.setProperty("sun.java2d.renderer.clip", "false");
 177         System.setProperty("sun.java2d.renderer.clip.runtime.enable", "true");
 178 
 179         // enable subdivider:
 180         System.setProperty("sun.java2d.renderer.clip.subdivider", "true");
 181 
 182         // disable min length check: always subdivide curves at clip edges
 183         System.setProperty("sun.java2d.renderer.clip.subdivider.minLength", "-1");
 184 
 185         // If any curve, increase curve accuracy:
 186         // curve length max error:
 187         System.setProperty("sun.java2d.renderer.curve_len_err", "1e-4");
 188 
 189         // quad max error:
 190         System.setProperty("sun.java2d.renderer.quad_dec_d2", "5e-4");
 191 
 192         // cubic min/max error:
 193         System.setProperty("sun.java2d.renderer.cubic_dec_d2", "1e-3");
 194         System.setProperty("sun.java2d.renderer.cubic_inc_d1", "1e-4"); // or disabled ~ 1e-6
 195     }
 196 
 197     /**
 198      * Test
 199      * @param args
 200      */
 201     public static void main(String[] args) {
 202         boolean runSlowTests = false;
 203 
 204         for (String arg : args) {
 205             if ("-slow".equals(arg)) {
 206                 System.out.println("slow: enabled.");
 207                 runSlowTests = true;
 208             } else if ("-doScale".equals(arg)) {
 209                 System.out.println("doScale: enabled.");
 210                 TX_SCALE = true;
 211             } else if ("-doShear".equals(arg)) {
 212                 System.out.println("doShear: enabled.");
 213                 TX_SHEAR = true;
 214             } else if ("-doDash".equals(arg)) {
 215                 System.out.println("doDash: enabled.");
 216                 USE_DASHES = true;
 217             } else if ("-doVarStroke".equals(arg)) {
 218                 System.out.println("doVarStroke: enabled.");
 219                 USE_VAR_STROKE = true;
 220             }
 221             // shape mode:
 222             else if (arg.equalsIgnoreCase("-poly")) {
 223                 SHAPE_MODE = ShapeMode.NINE_LINE_POLYS;
 224             } else if (arg.equalsIgnoreCase("-bigpoly")) {
 225                 SHAPE_MODE = ShapeMode.FIFTY_LINE_POLYS;
 226             } else if (arg.equalsIgnoreCase("-quad")) {
 227                 SHAPE_MODE = ShapeMode.FOUR_QUADS;
 228             } else if (arg.equalsIgnoreCase("-cubic")) {
 229                 SHAPE_MODE = ShapeMode.TWO_CUBICS;
 230             } else if (arg.equalsIgnoreCase("-mixed")) {
 231                 SHAPE_MODE = ShapeMode.MIXED;
 232             }
 233         }
 234 
 235         System.out.println("Shape mode: " + SHAPE_MODE);
 236 
 237         // adjust image comparison thresholds:
 238         switch(SHAPE_MODE) {
 239             case TWO_CUBICS:
 240                 // Define uncertainty for curves:
 241                 THRESHOLD_DELTA = 32; //  / 256
 242                 THRESHOLD_NBPIX = 128; //  / 10000
 243                 break;
 244             case FOUR_QUADS:
 245             case MIXED:
 246                 // Define uncertainty for quads:
 247                 // curve subdivision causes curves to be smaller
 248                 // then curve offsets are different (more accurate)
 249                 THRESHOLD_DELTA = 64;  // 64 / 256
 250                 THRESHOLD_NBPIX = 256; // 256 / 10000
 251                 break;
 252             default:
 253                 // Define uncertainty for lines:
 254                 // float variant have higher uncertainty
 255                 THRESHOLD_DELTA = 8;
 256                 THRESHOLD_NBPIX = 8;
 257         }
 258 
 259         System.out.println("THRESHOLD_DELTA: "+THRESHOLD_DELTA);
 260         System.out.println("THRESHOLD_NBPIX: "+THRESHOLD_NBPIX);
 261 
 262         if (runSlowTests) {
 263             NUM_TESTS = 10000; // or 100000 (very slow)
 264             USE_DASHES = true;
 265             USE_VAR_STROKE = true;
 266         }
 267 
 268         System.out.println("ClipShapeTests: image = " + TESTW + " x " + TESTH);
 269 
 270         int failures = 0;
 271         final long start = System.nanoTime();
 272         try {
 273             // TODO: test affine transforms ?
 274 
 275             if (TEST_STROKER) {
 276                 final float[][] dashArrays = (USE_DASHES) ?
 277 // small
 278 //                        new float[][]{new float[]{1f, 2f}}
 279 // normal
 280                         new float[][]{new float[]{13f, 7f}}
 281 // large (prime)
 282 //                        new float[][]{new float[]{41f, 7f}}
 283 // none
 284                         : new float[][]{null};
 285 
 286                 System.out.println("dashes: " + Arrays.deepToString(dashArrays));
 287 
 288                 final float[] strokeWidths = (USE_VAR_STROKE)
 289                                                 ? new float[5] :
 290                                                   new float[]{10f};
 291 
 292                 int nsw = 0;
 293                 if (USE_VAR_STROKE) {
 294                     for (float width = 0.1f; width < 110f; width *= 5f) {
 295                         strokeWidths[nsw++] = width;
 296                     }
 297                 } else {
 298                     nsw = 1;
 299                 }
 300 
 301                 System.out.println("stroke widths: " + Arrays.toString(strokeWidths));
 302 
 303                 // Stroker tests:
 304                 for (int w = 0; w < nsw; w++) {
 305                     final float width = strokeWidths[w];
 306 
 307                     for (float[] dashes : dashArrays) {
 308 
 309                         for (int cap = 0; cap <= 2; cap++) {
 310 


 374             for (int n = 0; n < NUM_TESTS; n++) {
 375                 genShape(p2d, ts);
 376 
 377                 // Runtime clip setting OFF:
 378                 paintShape(p2d, g2dOff, fill, false);
 379 
 380                 // Runtime clip setting ON:
 381                 paintShape(p2d, g2dOn, fill, true);
 382 
 383                 /* compute image difference if possible */
 384                 diffImage = computeDiffImage(testCtx, imgOn, imgOff, imgDiff, globalCtx);
 385 
 386                 final String testName = "Setup_" + ts.id + "_test_" + n;
 387 
 388                 if (diffImage != null) {
 389                     nd++;
 390 
 391                     final double ratio = (100.0 * testCtx.histPix.count) / testCtx.histAll.count;
 392                     System.out.println("Diff ratio: " + testName + " = " + trimTo3Digits(ratio) + " %");
 393 







 394                     if (nd < MAX_SHOW_FRAMES) {
 395                         if (SHOW_DETAILS) {
 396                             paintShapeDetails(g2dOff, p2d);
 397                             paintShapeDetails(g2dOn, p2d);
 398                         }
 399 
 400                         if (nd < MAX_SAVE_FRAMES) {
 401                             if (DUMP_SHAPE) {
 402                                 dumpShape(p2d);
 403                             }
 404                             saveImage(imgOff, OUTPUT_DIR, testName + "-off.png");
 405                             saveImage(imgOn, OUTPUT_DIR, testName + "-on.png");
 406                             saveImage(diffImage, OUTPUT_DIR, testName + "-diff.png");
 407                         }
 408                     }
 409                 }
 410             }
 411         } finally {
 412             g2dOff.dispose();
 413             g2dOn.dispose();
 414 
 415             if (nd != 0) {
 416                 System.out.println("paintPaths: " + NUM_TESTS + " paths - "
 417                         + "Number of differences = " + nd
 418                         + " ratio = " + (100f * nd) / NUM_TESTS + " %");
 419             }
 420 
 421             globalCtx.dump();
 422         }
 423         System.out.println("paintPaths: duration= " + (1e-6 * (System.nanoTime() - start)) + " ms.");
 424         return nd;
 425     }
 426 
 427     private static void paintShape(final Path2D p2d, final Graphics2D g2d,


 433         if (fill) {
 434             g2d.fill(p2d);
 435         } else {
 436             g2d.draw(p2d);
 437         }
 438     }
 439 
 440     private static Graphics2D initialize(final BufferedImage img,
 441                                          final TestSetup ts) {
 442         final Graphics2D g2d = (Graphics2D) img.getGraphics();
 443         g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
 444                 RenderingHints.VALUE_RENDER_QUALITY);
 445         g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
 446                 RenderingHints.VALUE_STROKE_PURE);
 447 
 448         if (ts.isStroke()) {
 449             g2d.setStroke(createStroke(ts));
 450         }
 451         g2d.setColor(Color.GRAY);
 452 
 453         // Test scale
 454         if (TX_SCALE) {
 455             g2d.scale(1.2, 1.2);
 456         }
 457         // Test shear
 458         if (TX_SHEAR) {
 459             g2d.shear(0.1, 0.2);
 460         }
 461 
 462         return g2d;
 463     }
 464 
 465     private static void reset(final Graphics2D g2d) {
 466         // Disable antialiasing:
 467         g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 468                 RenderingHints.VALUE_ANTIALIAS_OFF);
 469         g2d.setBackground(Color.WHITE);
 470         g2d.clearRect(0, 0, TESTW, TESTH);
 471     }
 472 
 473     private static void setClip(final Graphics2D g2d, final boolean clip) {
 474         // Enable antialiasing:
 475         g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 476                 RenderingHints.VALUE_ANTIALIAS_ON);
 477 
 478         // Enable or Disable clipping:
 479         System.setProperty("sun.java2d.renderer.clip.runtime", (clip) ? "true" : "false");
 480     }
 481 


 561             g2d.setColor(COLOR_LINETO_ODD);
 562             g2d.draw(shape);
 563         }
 564 
 565         final float[] coords = new float[6];
 566         float px, py;
 567 
 568         int nMove = 0;
 569         int nLine = 0;
 570         int n = 0;
 571 
 572         for (final PathIterator it = shape.getPathIterator(null); !it.isDone(); it.next()) {
 573             int type = it.currentSegment(coords);
 574             switch (type) {
 575                 case PathIterator.SEG_MOVETO:
 576                     if (SHOW_POINTS) {
 577                         g2d.setColor(COLOR_MOVETO);
 578                     }
 579                     break;
 580                 case PathIterator.SEG_LINETO:
 581                 case PathIterator.SEG_QUADTO:
 582                 case PathIterator.SEG_CUBICTO:
 583                     if (SHOW_POINTS) {
 584                         g2d.setColor((nLine % 2 == 0) ? COLOR_LINETO_ODD : COLOR_LINETO_EVEN);
 585                     }
 586                     nLine++;
 587                     break;
 588                 case PathIterator.SEG_CLOSE:
 589                     continue;
 590                 default:
 591                     System.out.println("unsupported segment type= " + type);
 592                     continue;
 593             }
 594             px = coords[0];
 595             py = coords[1];
 596 
 597             if (SHOW_INFO) {
 598                 System.out.println("point[" + (n++) + "|seg=" + type + "]: " + px + " " + py);
 599             }
 600 
 601             if (SHOW_POINTS) {
 602                 ELL_POINT.setFrame(px - POINT_RADIUS, py - POINT_RADIUS,


 608             System.out.println("Path moveTo=" + nMove + ", lineTo=" + nLine);
 609             System.out.println("--------------------------------------------------");
 610         }
 611 
 612         g2d.setStroke(oldStroke);
 613         g2d.setColor(oldColor);
 614     }
 615 
 616     private static void dumpShape(final Shape shape) {
 617         final float[] coords = new float[6];
 618 
 619         for (final PathIterator it = shape.getPathIterator(null); !it.isDone(); it.next()) {
 620             final int type = it.currentSegment(coords);
 621             switch (type) {
 622                 case PathIterator.SEG_MOVETO:
 623                     System.out.println("p2d.moveTo(" + coords[0] + ", " + coords[1] + ");");
 624                     break;
 625                 case PathIterator.SEG_LINETO:
 626                     System.out.println("p2d.lineTo(" + coords[0] + ", " + coords[1] + ");");
 627                     break;
 628                 case PathIterator.SEG_QUADTO:
 629                     System.out.println("p2d.quadTo(" + coords[0] + ", " + coords[1] + ", " + coords[2] + ", " + coords[3] + ");");
 630                     break;
 631                 case PathIterator.SEG_CUBICTO:
 632                     System.out.println("p2d.curveTo(" + coords[0] + ", " + coords[1] + ", " + coords[2] + ", " + coords[3] + ", " + coords[4] + ", " + coords[5] + ");");
 633                     break;
 634                 case PathIterator.SEG_CLOSE:
 635                     System.out.println("p2d.closePath();");
 636                     break;
 637                 default:
 638                     System.out.println("// Unsupported segment type= " + type);
 639             }
 640         }
 641         System.out.println("--------------------------------------------------");
 642     }
 643 
 644     static double randX() {
 645         return RANDOM.nextDouble() * RANDW + OFFW;
 646     }
 647 
 648     static double randY() {
 649         return RANDOM.nextDouble() * RANDH + OFFH;
 650     }
 651 
 652     private static BasicStroke createStroke(final TestSetup ts) {
 653         return new BasicStroke(ts.strokeWidth, ts.strokeCap, ts.strokeJoin, 10.0f, ts.dashes, 0.0f);


 679             this.dashes = dashes;
 680             this.windingRule = Path2D.WIND_NON_ZERO;
 681         }
 682 
 683         TestSetup(ShapeMode shapeMode, final boolean closed, final int windingRule) {
 684             this.id = COUNT.incrementAndGet();
 685             this.shapeMode = shapeMode;
 686             this.closed = closed;
 687             this.strokeWidth = 0f;
 688             this.strokeCap = this.strokeJoin = -1; // invalid
 689             this.dashes = null;
 690             this.windingRule = windingRule;
 691         }
 692 
 693         boolean isStroke() {
 694             return this.strokeWidth > 0f;
 695         }
 696 
 697         @Override
 698         public String toString() {
 699             if (isStroke()) {
 700                 return "TestSetup{id=" + id + ", shapeMode=" + shapeMode + ", closed=" + closed
 701                         + ", strokeWidth=" + strokeWidth + ", strokeCap=" + getCap(strokeCap) + ", strokeJoin=" + getJoin(strokeJoin)
 702                         + ((dashes != null) ? ", dashes: " + Arrays.toString(dashes) : "")
 703                         + '}';
 704             }
 705             return "TestSetup{id=" + id + ", shapeMode=" + shapeMode + ", closed=" + closed
 706                     + ", fill"
 707                     + ", windingRule=" + getWindingRule(windingRule) + '}';
 708         }
 709 
 710         private static String getCap(final int cap) {
 711             switch (cap) {
 712                 case BasicStroke.CAP_BUTT:
 713                     return "CAP_BUTT";
 714                 case BasicStroke.CAP_ROUND:
 715                     return "CAP_ROUND";
 716                 case BasicStroke.CAP_SQUARE:
 717                     return "CAP_SQUARE";
 718                 default:
 719                     return "";
 720             }
 721 
 722         }
 723 
 724         private static String getJoin(final int join) {
 725             switch (join) {
 726                 case BasicStroke.JOIN_MITER:
 727                     return "JOIN_MITER";
 728                 case BasicStroke.JOIN_ROUND:
 729                     return "JOIN_ROUND";
 730                 case BasicStroke.JOIN_BEVEL:
 731                     return "JOIN_BEVEL";
 732                 default:
 733                     return "";
 734             }
 735 
 736         }
 737 
 738         private static String getWindingRule(final int rule) {
 739             switch (rule) {
 740                 case PathIterator.WIND_EVEN_ODD:
 741                     return "WIND_EVEN_ODD";
 742                 case PathIterator.WIND_NON_ZERO:
 743                     return "WIND_NON_ZERO";
 744                 default:
 745                     return "";
 746             }
 747         }
 748     }
 749 
 750     // --- utilities ---
 751     private static final int DCM_ALPHA_MASK = 0xff000000;
 752 
 753     public static BufferedImage newImage(final int w, final int h) {
 754         return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
 755     }
 756 
 757     public static BufferedImage computeDiffImage(final DiffContext localCtx,
 758                                                  final BufferedImage tstImage,
 759                                                  final BufferedImage refImage,
 760                                                  final BufferedImage diffImage,
 761                                                  final DiffContext globalCtx) {
 762 
 763         final int[] aRefPix = ((DataBufferInt) refImage.getRaster().getDataBuffer()).getData();
 764         final int[] aTstPix = ((DataBufferInt) tstImage.getRaster().getDataBuffer()).getData();
 765         final int[] aDifPix = ((DataBufferInt) diffImage.getRaster().getDataBuffer()).getData();
 766 
 767         // reset local diff context:
 768         localCtx.reset();
 769 
 770         int ref, tst, dg, v;
 771         for (int i = 0, len = aRefPix.length; i < len; i++) {
 772             ref = aRefPix[i];
 773             tst = aTstPix[i];
 774 
 775             // grayscale diff:
 776             dg = (r(ref) + g(ref) + b(ref)) - (r(tst) + g(tst) + b(tst));
 777 
 778             // max difference on grayscale values:
 779             v = (int) Math.ceil(Math.abs(dg / 3.0));
 780 
 781 // TODO: count warnings
 782             if (v <= THRESHOLD_DELTA) {
 783                 aDifPix[i] = 0;
 784             } else {
 785                 aDifPix[i] = toInt(v, v, v);
 786 
 787                 localCtx.add(v);
 788             }
 789             globalCtx.add(v);
 790         }
 791 
 792         if (!localCtx.isDiff() || (localCtx.histPix.count <= THRESHOLD_NBPIX)) {
 793             return null;
 794         }
 795 
 796         localCtx.dump();
 797 
 798         return diffImage;
 799     }
 800 
 801     static void saveImage(final BufferedImage image, final File resDirectory, final String imageFileName) throws IOException {
 802         final Iterator<ImageWriter> itWriters = ImageIO.getImageWritersByFormatName("PNG");
 803         if (itWriters.hasNext()) {
 804             final ImageWriter writer = itWriters.next();
 805 
 806             final ImageWriteParam writerParams = writer.getDefaultWriteParam();
 807             writerParams.setProgressiveMode(ImageWriteParam.MODE_DISABLED);
 808 
 809             final File imgFile = new File(resDirectory, imageFileName);
 810 
 811             if (!imgFile.exists() || imgFile.canWrite()) {
 812                 System.out.println("saveImage: saving image as PNG [" + imgFile + "]...");
 813                 imgFile.delete();
 814 
 815                 // disable cache in temporary files:
 816                 ImageIO.setUseCache(false);


< prev index next >