1 /*
   2  * Copyright (c) 2011, 2013, 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  * @summary  <rdar://problem/4122177> [JavaJDK15] Font bounds returned by TextLayout significantly off.
  27  * @library ../../../regtesthelpers
  28  * @build VisibilityValidator
  29  * @build Waypoint
  30  * @build RobotUtilities
  31  * @build BITestUtils
  32  * @summary com.apple.junit.java.text.TextBounds;
  33  * @run junit Bounds01
  34  */
  35 
  36 /**
  37  *
  38  * Paints a series of red strings, overwrites them with a 1/2 alpha blue wash, then looks for bright red
  39  * uncovered pixels.
  40  *
  41  **/
  42 
  43 // classes necessary for this test
  44 
  45 import junit.framework.*;
  46 
  47 import java.awt.*;
  48 import java.awt.font.FontRenderContext;
  49 import java.awt.font.TextLayout;
  50 import java.awt.geom.Point2D;
  51 import java.awt.geom.Rectangle2D;
  52 import java.awt.image.BufferedImage;
  53 import java.io.File;
  54 import java.util.Vector;
  55 
  56 import test.java.awt.regtesthelpers.VisibilityValidator;
  57 import test.java.awt.regtesthelpers.Waypoint;
  58 import test.java.awt.regtesthelpers.RobotUtilities;
  59 import test.java.awt.regtesthelpers.BITestUtils;
  60 
  61 public class Bounds01 extends TestCase {
  62     static final int WIDTH  = 600;
  63     static final int HEIGHT = 200;
  64 
  65     static final int slop = 1;
  66     static final int sizes[] = { 9, 12, 18, 37 };    // Use reduced set to make the test run fast
  67 
  68     static final int styles[] = { Font.PLAIN, Font.BOLD  };
  69     static final String names[] = { "Serif", "Helvetica", "DIALOG"};
  70 
  71     protected    Vector<Font> fonts;
  72     protected    BufferedImage bi;
  73     protected    Waypoint painted = new Waypoint();
  74     protected    Robot robot;
  75 
  76     class TestFrame extends Frame
  77     {
  78         static final long serialVersionUID = 0;
  79         Image img = null;
  80 
  81         public TestFrame(Image img) {
  82             this.img = img;
  83             setUndecorated(true);
  84             setBounds( 50, 50, Bounds01.WIDTH, Bounds01.HEIGHT);
  85         }
  86 
  87         public void paint( Graphics g ) {
  88             super.paint( g );
  89             g.setColor( Color.black );
  90             if (img != null) {
  91                 g.drawImage( img, 0, 0, this );
  92             }
  93             else {
  94                 g.drawString( "null image", 30, 30);
  95             }
  96             painted.clear();
  97         }
  98     }
  99 
 100     protected void setUp() throws Exception{
 101         // this is to wake the screen out of sleep
 102         robot = new Robot();
 103         robot.mouseMove(10, 10);
 104         robot.mouseMove(0, 0);
 105         Thread.sleep(1000);
 106 
 107         fonts = new Vector<Font>();
 108         for (String name : names) {
 109             for (int size : sizes) {
 110                 for (int style : styles) {
 111                     Font font = new Font(name, style, size);
 112                     assertNotNull(font);
 113                     fonts.add(font);
 114                 }
 115             }
 116         }
 117 
 118         // Get the default (or compatible) buffered image
 119         GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
 120         GraphicsDevice gd = ge.getDefaultScreenDevice();
 121         GraphicsConfiguration gc = gd.getDefaultConfiguration();
 122         bi = gc.createCompatibleImage( WIDTH,HEIGHT );
 123     }
 124 
 125     public void testFontBounds() throws Exception {
 126 
 127         for (Font font : fonts) {
 128 
 129             Graphics2D ig = (Graphics2D) bi.getGraphics();
 130 
 131             // Use the most percise mode available
 132             RenderingHints hints = ig.getRenderingHints();
 133             hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
 134             hints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
 135             ig.setRenderingHints(hints);
 136 
 137             ig.setColor(Color.black);
 138             ig.fillRect(0, 0, Bounds01.WIDTH, Bounds01.HEIGHT);
 139 
 140             String testString = "Covered by bounds rect +1?";
 141 
 142             // Draw centered and a little in from the left
 143             Point2D.Float loc = new Point2D.Float(20, Bounds01.HEIGHT / 2);
 144 
 145             FontRenderContext frc = ig.getFontRenderContext();
 146             assertNotNull(frc);
 147 
 148             TextLayout layout = new TextLayout(testString, font, frc);
 149             assertNotNull(layout);
 150 
 151             Rectangle2D cover1 = layout.getBounds();
 152             assertNotNull(cover1);
 153 
 154             // cutting the cover rect a little slack, since anti-aliasing and fractional text
 155             // can account for off-by-one errors which over/under shoot the bounding box
 156 
 157             cover1.setRect(cover1.getX() + loc.getX() - slop,
 158                     cover1.getY() + loc.getY() - slop,
 159                     cover1.getWidth() + (slop * 2),
 160                     cover1.getHeight() + (slop * 2));
 161 
 162             // Draw test string in red
 163             ig.setColor(Color.red);
 164             layout.draw(ig, (float) loc.getX(), (float) loc.getY());
 165 
 166             // cover1 with blue bounding box
 167             ig.setColor(new Color(0, 0, 0xFF, 0x80));
 168             ig.fill(cover1);
 169             ig.dispose();
 170 
 171             TestFrame frame = new TestFrame(bi);
 172             painted.reset();
 173             try {
 174                 // Bring the window up and make sure at least one paint has occured
 175                 VisibilityValidator.setVisibleAndConfirm(frame);
 176                 painted.requireClear();
 177                 assertTrue("Did not get a paint call", painted.isValid());
 178 
 179                 // Slow machines have a slight delay before the window is truely visible
 180                 // We need time for the tick-flusher to go
 181                 Thread.sleep(150);
 182 
 183                 // Grab the area where text should be and look for bright red pixels.
 184                 Rectangle rect = new Rectangle((int) cover1.getX() - 10 + frame.getX(), (int) cover1.getY() - 10 + frame.getY(), (int) cover1.getWidth() + 20, (int) cover1.getHeight() + 20);
 185                 BufferedImage image = (new Robot()).createScreenCapture(rect);
 186 
 187                 boolean someRed = false;
 188                 boolean pixelsOK = true;
 189                 int sizeX = image.getWidth();
 190                 int sizeY = image.getHeight();
 191 
 192                 File f = null;
 193                 String failurefile = "Bounds01_" + System.currentTimeMillis() + font;
 194                 for (int x = 0; x < sizeX; x++) {
 195                     for (int y = 0; y < sizeY; y++) {
 196                         Color c = new Color(image.getRGB(x, y));
 197                         boolean pixelOK = c.getRed() < 255;
 198 
 199                         if (!pixelOK) {
 200                             pixelsOK = false;
 201                         }
 202 
 203                         if (c.getRed() > 0) {
 204                             someRed = true;
 205                         }
 206                     }
 207                 }
 208 
 209                 if (!pixelsOK) {
 210                     // Dump the screen
 211                     f = RobotUtilities.screenshot(failurefile, new File(System.getProperty("java.io.tmpdir")));
 212                     assertNotNull("Problem creating error file", f);
 213 
 214                     // Dump just the smaller (original) image we checked
 215                     java.io.File outputFile1 = new File(System.getProperty("java.io.tmpdir"), failurefile + "Image.png");
 216                     javax.imageio.ImageIO.write(image, "png", outputFile1);
 217 
 218                     // Dump the image we checked in Hex, too
 219                     BITestUtils.pixelLogfileHex(image, failurefile + "Hex");
 220                 }
 221 
 222                 assertTrue(font + "apparently did not paint at all.  Probable error in testcase.", someRed);
 223                 assertTrue(font + "not covered.  See file " + failurefile, pixelsOK);
 224 
 225             }
 226             finally {
 227                 frame.dispose();
 228             }
 229         }
 230 
 231     }
 232 
 233     // Boilerplate
 234     public static Test suite() {
 235         return new TestSuite(Bounds01.class);
 236     }
 237 
 238     public static void main (String[] args) throws RuntimeException {
 239         String name = System.getProperty("os.name");
 240         if (name.equals("Mac OS X")) {
 241             // This test uses a font that may not exist on other platforms
 242             TestResult tr = junit.textui.TestRunner.run(suite());
 243             if((tr.errorCount() != 0) || (tr.failureCount() != 0)) {
 244                 throw new RuntimeException("### Unexpected JUnit errors or failures.");
 245             }
 246         }
 247     }
 248 }
 249