1 /* 2 * Copyright (c) 2005, 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 /** 25 * @test 26 * @bug 6271221 8145584 27 * @summary ask a text layout for its pixel bounds, then render it and 28 * compute the actual pixel bounds when rendering-- the actual pixel bounds 29 * must be contained within the bounds reported by the text layout, or there 30 * will be an exception. 31 */ 32 33 /* 34 * Copyright 2005 IBM Corp. All Rights Reserved. 35 */ 36 37 import java.awt.*; 38 import java.awt.font.*; 39 import java.awt.geom.*; 40 import java.awt.image.*; 41 import java.text.*; 42 import java.util.*; 43 44 import static java.awt.Font.*; 45 import static java.awt.font.GraphicAttribute.*; 46 import static java.awt.font.ShapeGraphicAttribute.*; 47 import static java.awt.font.TextAttribute.*; 48 49 public class TestGetPixelBounds { 50 static final boolean DEBUG; 51 static final boolean DUMP; 52 static { 53 String dbg = null; 54 String dmp = null; 55 try { 56 dbg = System.getProperty("DEBUG"); 57 dmp = System.getProperty("DUMP"); 58 } 59 catch (SecurityException e) { 60 dbg = dmp = null; 61 } 62 63 DEBUG = dbg != null; 64 DUMP = dmp != null; 65 } 66 main(String[] args)67 public static void main(String[] args) { 68 float x = 0; 69 float y = 0; 70 boolean rotate = false; 71 boolean underline = false; 72 boolean graphic = false; 73 String text = "Ping"; 74 for (int i = 0; i < args.length; ++i) { 75 String arg = args[i]; 76 if (arg.startsWith("-x")) { 77 x = Float.parseFloat(args[++i]); 78 } else if (arg.startsWith("-y")) { 79 y = Float.parseFloat(args[++i]); 80 } else if (arg.startsWith("-r")) { 81 rotate = true; 82 } else if (arg.startsWith("-u")) { 83 underline = true; 84 } else if (arg.startsWith("-g")) { 85 graphic = true; 86 } else if (arg.startsWith("-t")) { 87 text = args[++i]; 88 } 89 } 90 FontRenderContext frc = new FontRenderContext(null, true, true); 91 Map<TextAttribute, Object> m = new HashMap<TextAttribute, Object>(); 92 m.put(FAMILY, SANS_SERIF); 93 m.put(SIZE, 16); 94 if (underline) { 95 m.put(UNDERLINE, UNDERLINE_ON); 96 } 97 if (rotate) { 98 m.put(TRANSFORM, AffineTransform.getRotateInstance(Math.PI/4)); 99 } 100 Font font = Font.getFont(m); 101 102 AttributedString as; 103 if (graphic) { 104 GraphicAttribute sga = new ShapeGraphicAttribute( 105 new Ellipse2D.Float(0,-10,10,20), TOP_ALIGNMENT, STROKE); 106 as = new AttributedString(text + '*' + text, m); 107 as.addAttribute(CHAR_REPLACEMENT, sga, text.length(), text.length() + 1); 108 } else { 109 as = new AttributedString(text, m); 110 } 111 TextLayout tl = new TextLayout(as.getIterator(), frc); 112 System.out.println("tl bounds: " + tl.getBounds()); 113 System.out.println("tl compute: " + computeLayoutBounds(tl, x, y, frc)); 114 System.out.println("tl pixel bounds: " + tl.getPixelBounds(frc, x, y)); 115 System.out.println(" again int off: " + tl.getPixelBounds(frc, x+2, y - 2)); 116 System.out.println(" again frac off: " + tl.getPixelBounds(frc, x+.5f, y + .2f)); 117 System.out.println(" again frc: " + tl.getPixelBounds( 118 new FontRenderContext(AffineTransform.getScaleInstance(100, 100), true, true), x, y)); 119 System.out.println(" again int off: " + tl.getPixelBounds(frc, x-2, y+2)); 120 121 GlyphVector gv = font.createGlyphVector(frc, text); 122 System.out.println("gv bounds: " + gv.getPixelBounds(frc, x, y)); 123 System.out.println("gv compute: " + computeLayoutBounds(gv, x, y, frc)); 124 125 if (!tl.getPixelBounds(frc, x, y).contains(computeLayoutBounds(tl, x, y, frc))) { 126 throw new RuntimeException("error, tl.bounds does not contain computed bounds"); 127 } 128 } 129 computeLayoutBounds(TextLayout tl, float x, float y, FontRenderContext frc)130 static Rectangle computeLayoutBounds(TextLayout tl, float x, float y, FontRenderContext frc) { 131 Rectangle bounds = tl.getBounds().getBounds(); 132 BufferedImage im = new BufferedImage(bounds.width + 4, bounds.height + 4, 133 BufferedImage.TYPE_INT_ARGB); 134 135 Graphics2D g2d = im.createGraphics(); 136 g2d.setColor(Color.WHITE); 137 g2d.fillRect(0, 0, im.getWidth(), im.getHeight()); 138 139 float fx = (float)Math.IEEEremainder(x,1); 140 float fy = (float)Math.IEEEremainder(y,1); 141 g2d.setColor(Color.BLACK); 142 tl.draw(g2d, fx + 2 - bounds.x, fy + 2 - bounds.y); 143 144 Rectangle r = computePixelBounds(im); 145 r.x += (int)Math.floor(x) - 2 + bounds.x; 146 r.y += (int)Math.floor(y) - 2 + bounds.y; 147 148 return r; 149 } 150 computeLayoutBounds(GlyphVector gv, float x, float y, FontRenderContext frc)151 static Rectangle computeLayoutBounds(GlyphVector gv, float x, float y, FontRenderContext frc) { 152 Rectangle bounds = gv.getVisualBounds().getBounds(); 153 BufferedImage im = new BufferedImage(bounds.width + 4, bounds.height + 4, 154 BufferedImage.TYPE_INT_ARGB); 155 156 Graphics2D g2d = im.createGraphics(); 157 g2d.setColor(Color.WHITE); 158 g2d.fillRect(0, 0, im.getWidth(), im.getHeight()); 159 160 float fx = (float)Math.IEEEremainder(x,1); 161 float fy = (float)Math.IEEEremainder(y,1); 162 g2d.setColor(Color.BLACK); 163 g2d.drawGlyphVector(gv, fx + 2 - bounds.x, fy + 2 - bounds.y); 164 165 Rectangle r = computePixelBounds(im); 166 r.x += (int)Math.floor(x) - 2 + bounds.x; 167 r.y += (int)Math.floor(y) - 2 + bounds.y; 168 169 return r; 170 } 171 computePixelBounds(BufferedImage im)172 static Rectangle computePixelBounds(BufferedImage im) { 173 int w = im.getWidth(); 174 int h = im.getHeight(); 175 176 Formatter fmt = DEBUG ? new Formatter(System.err) : null; 177 178 // dump 179 if (DUMP && DEBUG) { 180 fmt.format(" "); 181 for (int j = 0; j < w; ++j) { 182 fmt.format("%2d", j); 183 } 184 for (int i = 0; i < h; ++i) { 185 fmt.format("\n[%2d] ", i); 186 for (int j = 0; j < w; ++j) { 187 fmt.format("%c ", im.getRGB(j, i) == -1 ? ' ' : '*'); 188 } 189 } 190 fmt.format("\n"); 191 } 192 193 int l = -1, t = -1, r = w, b = h; 194 195 { 196 // get top 197 int[] buf = new int[w]; 198 loop: 199 while (++t < h) { 200 im.getRGB(0, t, buf.length, 1, buf, 0, w); // w ignored 201 for (int i = 0; i < buf.length; i++) { 202 if (buf[i] != -1) { 203 if (DEBUG) fmt.format("top pixel at %d,%d = 0x%08x\n", i, t, buf[i]); 204 break loop; 205 } 206 } 207 } 208 if (DEBUG) fmt.format("t: %d\n", t); 209 } 210 211 // get bottom 212 { 213 int[] buf = new int[w]; 214 loop: 215 while (--b > t) { 216 im.getRGB(0, b, buf.length, 1, buf, 0, w); // w ignored 217 for (int i = 0; i < buf.length; ++i) { 218 if (buf[i] != -1) { 219 if (DEBUG) fmt.format("bottom pixel at %d,%d = 0x%08x\n", i, b, buf[i]); 220 break loop; 221 } 222 } 223 } 224 ++b; 225 if (DEBUG) fmt.format("b: %d\n", b); 226 } 227 228 // get left 229 { 230 loop: 231 while (++l < r) { 232 for (int i = t; i < b; ++i) { 233 int v = im.getRGB(l, i); 234 if (v != -1) { 235 if (DEBUG) fmt.format("left pixel at %d,%d = 0x%08x\n", l, i, v); 236 break loop; 237 } 238 } 239 } 240 if (DEBUG) fmt.format("l: %d\n", l); 241 } 242 243 // get right 244 { 245 loop: 246 while (--r > l) { 247 for (int i = t; i < b; ++i) { 248 int v = im.getRGB(r, i); 249 if (v != -1) { 250 if (DEBUG) fmt.format("right pixel at %d,%d = 0x%08x\n", r, i, v); 251 break loop; 252 } 253 } 254 } 255 ++r; 256 if (DEBUG) fmt.format("r: %d\n", r); 257 } 258 259 return new Rectangle(l, t, r-l, b-t); 260 } 261 } 262