--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/java2d/marlin/TextClipErrorTest.java Wed Dec 16 13:24:19 2015 -0800
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Line2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
+import static java.awt.geom.PathIterator.SEG_CLOSE;
+import static java.awt.geom.PathIterator.SEG_CUBICTO;
+import static java.awt.geom.PathIterator.SEG_LINETO;
+import static java.awt.geom.PathIterator.SEG_MOVETO;
+import static java.awt.geom.PathIterator.SEG_QUADTO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javax.imageio.ImageIO;
+
+/**
+ * @test @bug 8144718
+ * @summary Check the Stroker.drawBezApproxForArc() bug (stoke with round
+ * joins): if cosext2 > 0.5, it generates curves with NaN coordinates
+ * @run main TextClipErrorTest
+ */
+public class TextClipErrorTest {
+
+ static final boolean SAVE_IMAGE = false;
+ static final boolean SERIALIZE = false;
+
+ public static void main(String[] args) {
+ Locale.setDefault(Locale.US);
+
+ // initialize j.u.l Looger:
+ final Logger log = Logger.getLogger("sun.java2d.marlin");
+ log.addHandler(new Handler() {
+ @Override
+ public void publish(LogRecord record) {
+ Throwable th = record.getThrown();
+ // detect potential Throwable thrown by XxxArrayCache.check():
+ if (th != null && th.getClass() == Throwable.class) {
+ StackTraceElement[] stackElements = th.getStackTrace();
+
+ for (int i = 0; i < stackElements.length; i++) {
+ StackTraceElement e = stackElements[i];
+
+ if (e.getClassName().startsWith("sun.java2d.marlin")
+ && e.getClassName().contains("ArrayCache")
+ && "check".equals(e.getMethodName()))
+ {
+ System.out.println("Test failed:\n"
+ + record.getMessage());
+ th.printStackTrace(System.out);
+
+ throw new RuntimeException("Test failed: ", th);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void close() throws SecurityException {
+ }
+ });
+
+ log.info("TextClipErrorTest: start");
+
+ // enable Marlin logging & internal checks:
+ System.setProperty("sun.java2d.renderer.log", "true");
+ System.setProperty("sun.java2d.renderer.useLogger", "true");
+ System.setProperty("sun.java2d.renderer.doChecks", "true");
+
+ BufferedImage image = new BufferedImage(256, 256,
+ BufferedImage.TYPE_INT_ARGB);
+
+ Graphics2D g2d = image.createGraphics();
+ g2d.setColor(Color.red);
+ try {
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+
+ Font font = g2d.getFont();
+ FontRenderContext frc = new FontRenderContext(
+ new AffineTransform(), true, true);
+
+ g2d.setStroke(new BasicStroke(4.0f,
+ BasicStroke.CAP_ROUND,
+ BasicStroke.JOIN_ROUND));
+
+ final Shape badShape;
+ if (SERIALIZE) {
+ final GlyphVector gv1 = font.createGlyphVector(frc, "\u00d6");
+ final Shape textShape = gv1.getOutline();
+
+ final AffineTransform at1 = AffineTransform.getTranslateInstance(
+ -2091202.554154681, 5548.601436981691);
+ badShape = at1.createTransformedShape(textShape);
+ serializeShape(badShape);
+ } else {
+ badShape = deserializeShape();
+ }
+
+ g2d.draw(badShape);
+
+ // Draw anything within bounds and it fails:
+ g2d.draw(new Line2D.Double(10, 20, 30, 40));
+
+ if (SAVE_IMAGE) {
+ final File file = new File("TextClipErrorTest.png");
+ System.out.println("Writing file: " + file.getAbsolutePath());
+ ImageIO.write(image, "PNG", file);
+ }
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ } finally {
+ g2d.dispose();
+ log.info("TextClipErrorTest: end");
+ }
+ }
+
+ private static void serializeShape(Shape shape) {
+ final double[] coords = new double[6];
+
+ final int len = 32;
+ final ArrayList<Integer> typeList = new ArrayList<Integer>(len);
+ final ArrayList<double[]> coordsList = new ArrayList<double[]>(len);
+
+ for (PathIterator pi = shape.getPathIterator(null);
+ !pi.isDone(); pi.next())
+ {
+ switch (pi.currentSegment(coords)) {
+ case SEG_MOVETO:
+ typeList.add(SEG_MOVETO);
+ coordsList.add(Arrays.copyOf(coords, 2));
+ break;
+ case SEG_LINETO:
+ typeList.add(SEG_LINETO);
+ coordsList.add(Arrays.copyOf(coords, 2));
+ break;
+ case SEG_QUADTO:
+ typeList.add(SEG_QUADTO);
+ coordsList.add(Arrays.copyOf(coords, 4));
+ break;
+ case SEG_CUBICTO:
+ typeList.add(SEG_CUBICTO);
+ coordsList.add(Arrays.copyOf(coords, 6));
+ break;
+ case SEG_CLOSE:
+ typeList.add(SEG_CLOSE);
+ coordsList.add(null);
+ break;
+ default:
+ }
+ }
+
+ final StringBuilder sb = new StringBuilder(1024);
+ // types:
+ sb.append("private static final int[] SHAPE_TYPES = new int[]{\n");
+ for (Integer i : typeList) {
+ sb.append(i).append(",\n");
+ }
+ sb.append("};\n");
+
+ // coords:
+ sb.append("private static final double[][] SHAPE_COORDS = new double[][]{\n");
+ for (double[] c : coordsList) {
+ if (c == null) {
+ sb.append("null,\n");
+ } else {
+ sb.append("new double[]{");
+ for (int i = 0; i < c.length; i++) {
+ sb.append(c[i]).append(",");
+ }
+ sb.append("},\n");
+ }
+ }
+ sb.append("};\n");
+
+ System.out.println("Shape size: " + typeList.size());
+ System.out.println("Serialized shape:\n" + sb.toString());
+ }
+
+ private static Shape deserializeShape() {
+ final Path2D.Double path = new Path2D.Double();
+
+ for (int i = 0; i < SHAPE_TYPES.length; i++) {
+ double[] coords = SHAPE_COORDS[i];
+
+ switch (SHAPE_TYPES[i]) {
+ case SEG_MOVETO:
+ path.moveTo(coords[0], coords[1]);
+ break;
+ case SEG_LINETO:
+ path.lineTo(coords[0], coords[1]);
+ break;
+ case SEG_QUADTO:
+ path.quadTo(coords[0], coords[1],
+ coords[2], coords[3]);
+ break;
+ case SEG_CUBICTO:
+ path.curveTo(coords[0], coords[1],
+ coords[2], coords[3],
+ coords[4], coords[5]);
+ break;
+ case SEG_CLOSE:
+ path.closePath();
+ break;
+ default:
+ }
+ }
+
+ return path;
+ }
+
+ // generated code:
+ private static final int[] SHAPE_TYPES = new int[]{
+ 0,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 4,
+ 0,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 4,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 4,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 4,
+ };
+
+ private static final double[][] SHAPE_COORDS = new double[][]{
+ new double[]{-2091197.819779681, 5540.648311981691,},
+ new double[]{-2091199.116654681, 5540.648311981691, -2091199.874467181, 5541.609249481691,},
+ new double[]{-2091200.632279681, 5542.570186981691, -2091200.632279681, 5544.242061981691,},
+ new double[]{-2091200.632279681, 5545.882686981691, -2091199.874467181, 5546.843624481691,},
+ new double[]{-2091199.116654681, 5547.804561981691, -2091197.819779681, 5547.804561981691,},
+ new double[]{-2091196.538529681, 5547.804561981691, -2091195.780717181, 5546.843624481691,},
+ new double[]{-2091195.022904681, 5545.882686981691, -2091195.022904681, 5544.242061981691,},
+ new double[]{-2091195.022904681, 5542.570186981691, -2091195.780717181, 5541.609249481691,},
+ new double[]{-2091196.538529681, 5540.648311981691, -2091197.819779681, 5540.648311981691,},
+ null,
+ new double[]{-2091197.819779681, 5539.695186981691,},
+ new double[]{-2091195.991654681, 5539.695186981691, -2091194.890092181, 5540.929561981691,},
+ new double[]{-2091193.788529681, 5542.163936981691, -2091193.788529681, 5544.242061981691,},
+ new double[]{-2091193.788529681, 5546.304561981691, -2091194.890092181, 5547.538936981691,},
+ new double[]{-2091195.991654681, 5548.773311981691, -2091197.819779681, 5548.773311981691,},
+ new double[]{-2091199.663529681, 5548.773311981691, -2091200.772904681, 5547.538936981691,},
+ new double[]{-2091201.882279681, 5546.304561981691, -2091201.882279681, 5544.242061981691,},
+ new double[]{-2091201.882279681, 5542.163936981691, -2091200.772904681, 5540.929561981691,},
+ new double[]{-2091199.663529681, 5539.695186981691, -2091197.819779681, 5539.695186981691,},
+ null,
+ new double[]{-2091197.210404681, 5537.835811981691,},
+ new double[]{-2091196.022904681, 5537.835811981691,},
+ new double[]{-2091196.022904681, 5539.023311981691,},
+ new double[]{-2091197.210404681, 5539.023311981691,},
+ new double[]{-2091197.210404681, 5537.835811981691,},
+ null,
+ new double[]{-2091199.632279681, 5537.835811981691,},
+ new double[]{-2091198.444779681, 5537.835811981691,},
+ new double[]{-2091198.444779681, 5539.023311981691,},
+ new double[]{-2091199.632279681, 5539.023311981691,},
+ new double[]{-2091199.632279681, 5537.835811981691,},
+ null,
+ };
+
+}