1 /* 2 * Copyright (c) 2015, 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 import java.awt.BasicStroke; 25 import java.awt.Color; 26 import java.awt.Font; 27 import java.awt.Graphics2D; 28 import java.awt.RenderingHints; 29 import java.awt.Shape; 30 import java.awt.font.FontRenderContext; 31 import java.awt.font.GlyphVector; 32 import java.awt.geom.AffineTransform; 33 import java.awt.geom.Line2D; 34 import java.awt.geom.Path2D; 35 import java.awt.geom.PathIterator; 36 import static java.awt.geom.PathIterator.SEG_CLOSE; 37 import static java.awt.geom.PathIterator.SEG_CUBICTO; 38 import static java.awt.geom.PathIterator.SEG_LINETO; 39 import static java.awt.geom.PathIterator.SEG_MOVETO; 40 import static java.awt.geom.PathIterator.SEG_QUADTO; 41 import java.awt.image.BufferedImage; 42 import java.io.File; 43 import java.io.IOException; 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 import java.util.Locale; 47 import java.util.logging.Handler; 48 import java.util.logging.LogRecord; 49 import java.util.logging.Logger; 50 import javax.imageio.ImageIO; 51 52 /** 53 * @test @bug 8144718 54 * @summary Check the Stroker.drawBezApproxForArc() bug (stoke with round 55 * joins): if cosext2 > 0.5, it generates curves with NaN coordinates 56 * @run main TextClipErrorTest 57 */ 58 public class TextClipErrorTest { 59 60 static final boolean SAVE_IMAGE = false; 61 static final boolean SERIALIZE = false; 62 63 public static void main(String[] args) { 64 Locale.setDefault(Locale.US); 65 66 // initialize j.u.l Looger: 67 final Logger log = Logger.getLogger("sun.java2d.marlin"); 68 log.addHandler(new Handler() { 69 @Override 70 public void publish(LogRecord record) { 71 Throwable th = record.getThrown(); 72 // detect any Throwable: 73 if (th != null) { 74 System.out.println("Test failed:\n" + record.getMessage()); 75 th.printStackTrace(System.out); 76 77 throw new RuntimeException("Test failed: ", th); 78 } 79 } 80 81 @Override 82 public void flush() { 83 } 84 85 @Override 86 public void close() throws SecurityException { 87 } 88 }); 89 90 log.info("TextClipErrorTest: start"); 91 92 // enable Marlin logging & internal checks: 93 System.setProperty("sun.java2d.renderer.log", "true"); 94 System.setProperty("sun.java2d.renderer.useLogger", "true"); 95 System.setProperty("sun.java2d.renderer.doChecks", "true"); 96 97 BufferedImage image = new BufferedImage(256, 256, 98 BufferedImage.TYPE_INT_ARGB); 99 100 Graphics2D g2d = image.createGraphics(); 101 g2d.setColor(Color.red); 102 try { 103 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 104 RenderingHints.VALUE_ANTIALIAS_ON); 105 106 Font font = g2d.getFont(); 107 FontRenderContext frc = new FontRenderContext( 108 new AffineTransform(), true, true); 109 110 g2d.setStroke(new BasicStroke(4.0f, 111 BasicStroke.CAP_ROUND, 112 BasicStroke.JOIN_ROUND)); 113 114 final Shape badShape; 115 if (SERIALIZE) { 116 final GlyphVector gv1 = font.createGlyphVector(frc, "\u00d6"); 117 final Shape textShape = gv1.getOutline(); 118 119 final AffineTransform at1 = AffineTransform.getTranslateInstance( 120 -2091202.554154681, 5548.601436981691); 121 badShape = at1.createTransformedShape(textShape); 122 serializeShape(badShape); 123 } else { 124 badShape = deserializeShape(); 125 } 126 127 g2d.draw(badShape); 128 129 // Draw anything within bounds and it fails: 130 g2d.draw(new Line2D.Double(10, 20, 30, 40)); 131 132 if (SAVE_IMAGE) { 133 final File file = new File("TextClipErrorTest.png"); 134 System.out.println("Writing file: " + file.getAbsolutePath()); 135 ImageIO.write(image, "PNG", file); 136 } 137 } catch (IOException ex) { 138 ex.printStackTrace(); 139 } finally { 140 g2d.dispose(); 141 log.info("TextClipErrorTest: end"); 142 } 143 } 144 145 private static void serializeShape(Shape shape) { 146 final double[] coords = new double[6]; 147 148 final int len = 32; 149 final ArrayList<Integer> typeList = new ArrayList<Integer>(len); 150 final ArrayList<double[]> coordsList = new ArrayList<double[]>(len); 151 152 for (PathIterator pi = shape.getPathIterator(null); 153 !pi.isDone(); pi.next()) 154 { 155 switch (pi.currentSegment(coords)) { 156 case SEG_MOVETO: 157 typeList.add(SEG_MOVETO); 158 coordsList.add(Arrays.copyOf(coords, 2)); 159 break; 160 case SEG_LINETO: 161 typeList.add(SEG_LINETO); 162 coordsList.add(Arrays.copyOf(coords, 2)); 163 break; 164 case SEG_QUADTO: 165 typeList.add(SEG_QUADTO); 166 coordsList.add(Arrays.copyOf(coords, 4)); 167 break; 168 case SEG_CUBICTO: 169 typeList.add(SEG_CUBICTO); 170 coordsList.add(Arrays.copyOf(coords, 6)); 171 break; 172 case SEG_CLOSE: 173 typeList.add(SEG_CLOSE); 174 coordsList.add(null); 175 break; 176 default: 177 } 178 } 179 180 final StringBuilder sb = new StringBuilder(1024); 181 // types: 182 sb.append("private static final int[] SHAPE_TYPES = new int[]{\n"); 183 for (Integer i : typeList) { 184 sb.append(i).append(",\n"); 185 } 186 sb.append("};\n"); 187 188 // coords: 189 sb.append("private static final double[][] SHAPE_COORDS = new double[][]{\n"); 190 for (double[] c : coordsList) { 191 if (c == null) { 192 sb.append("null,\n"); 193 } else { 194 sb.append("new double[]{"); 195 for (int i = 0; i < c.length; i++) { 196 sb.append(c[i]).append(","); 197 } 198 sb.append("},\n"); 199 } 200 } 201 sb.append("};\n"); 202 203 System.out.println("Shape size: " + typeList.size()); 204 System.out.println("Serialized shape:\n" + sb.toString()); 205 } 206 207 private static Shape deserializeShape() { 208 final Path2D.Double path = new Path2D.Double(); 209 210 for (int i = 0; i < SHAPE_TYPES.length; i++) { 211 double[] coords = SHAPE_COORDS[i]; 212 213 switch (SHAPE_TYPES[i]) { 214 case SEG_MOVETO: 215 path.moveTo(coords[0], coords[1]); 216 break; 217 case SEG_LINETO: 218 path.lineTo(coords[0], coords[1]); 219 break; 220 case SEG_QUADTO: 221 path.quadTo(coords[0], coords[1], 222 coords[2], coords[3]); 223 break; 224 case SEG_CUBICTO: 225 path.curveTo(coords[0], coords[1], 226 coords[2], coords[3], 227 coords[4], coords[5]); 228 break; 229 case SEG_CLOSE: 230 path.closePath(); 231 break; 232 default: 233 } 234 } 235 236 return path; 237 } 238 239 // generated code: 240 private static final int[] SHAPE_TYPES = new int[]{ 241 0, 242 2, 243 2, 244 2, 245 2, 246 2, 247 2, 248 2, 249 2, 250 4, 251 0, 252 2, 253 2, 254 2, 255 2, 256 2, 257 2, 258 2, 259 2, 260 4, 261 0, 262 1, 263 1, 264 1, 265 1, 266 4, 267 0, 268 1, 269 1, 270 1, 271 1, 272 4, 273 }; 274 275 private static final double[][] SHAPE_COORDS = new double[][]{ 276 new double[]{-2091197.819779681, 5540.648311981691,}, 277 new double[]{-2091199.116654681, 5540.648311981691, -2091199.874467181, 5541.609249481691,}, 278 new double[]{-2091200.632279681, 5542.570186981691, -2091200.632279681, 5544.242061981691,}, 279 new double[]{-2091200.632279681, 5545.882686981691, -2091199.874467181, 5546.843624481691,}, 280 new double[]{-2091199.116654681, 5547.804561981691, -2091197.819779681, 5547.804561981691,}, 281 new double[]{-2091196.538529681, 5547.804561981691, -2091195.780717181, 5546.843624481691,}, 282 new double[]{-2091195.022904681, 5545.882686981691, -2091195.022904681, 5544.242061981691,}, 283 new double[]{-2091195.022904681, 5542.570186981691, -2091195.780717181, 5541.609249481691,}, 284 new double[]{-2091196.538529681, 5540.648311981691, -2091197.819779681, 5540.648311981691,}, 285 null, 286 new double[]{-2091197.819779681, 5539.695186981691,}, 287 new double[]{-2091195.991654681, 5539.695186981691, -2091194.890092181, 5540.929561981691,}, 288 new double[]{-2091193.788529681, 5542.163936981691, -2091193.788529681, 5544.242061981691,}, 289 new double[]{-2091193.788529681, 5546.304561981691, -2091194.890092181, 5547.538936981691,}, 290 new double[]{-2091195.991654681, 5548.773311981691, -2091197.819779681, 5548.773311981691,}, 291 new double[]{-2091199.663529681, 5548.773311981691, -2091200.772904681, 5547.538936981691,}, 292 new double[]{-2091201.882279681, 5546.304561981691, -2091201.882279681, 5544.242061981691,}, 293 new double[]{-2091201.882279681, 5542.163936981691, -2091200.772904681, 5540.929561981691,}, 294 new double[]{-2091199.663529681, 5539.695186981691, -2091197.819779681, 5539.695186981691,}, 295 null, 296 new double[]{-2091197.210404681, 5537.835811981691,}, 297 new double[]{-2091196.022904681, 5537.835811981691,}, 298 new double[]{-2091196.022904681, 5539.023311981691,}, 299 new double[]{-2091197.210404681, 5539.023311981691,}, 300 new double[]{-2091197.210404681, 5537.835811981691,}, 301 null, 302 new double[]{-2091199.632279681, 5537.835811981691,}, 303 new double[]{-2091198.444779681, 5537.835811981691,}, 304 new double[]{-2091198.444779681, 5539.023311981691,}, 305 new double[]{-2091199.632279681, 5539.023311981691,}, 306 new double[]{-2091199.632279681, 5537.835811981691,}, 307 null, 308 }; 309 310 }