1 /* 2 * Copyright (c) 2001, 2019, 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.Graphics; 27 import java.awt.Graphics2D; 28 import java.awt.GraphicsConfiguration; 29 import java.awt.GraphicsEnvironment; 30 import java.awt.Image; 31 import java.awt.image.BufferedImage; 32 import java.awt.image.VolatileImage; 33 import java.io.File; 34 import java.io.IOException; 35 import java.util.Arrays; 36 import java.util.stream.Collectors; 37 import javax.imageio.ImageIO; 38 39 import static java.awt.image.BufferedImage.TYPE_INT_RGB; 40 41 /* 42 * @test 43 * @bug 4469881 8217263 44 * @summary Verifies that dashed rectangles drawn to the screen line 45 * up with their undashed counterparts 46 * @author flar 47 * @run main/othervm -Dsun.java2d.uiScale=1 DashOffset 48 */ 49 public class DashOffset { 50 51 private static final BasicStroke dash = 52 new BasicStroke(1.0f, BasicStroke.CAP_BUTT, 53 BasicStroke.JOIN_MITER, 10.0f, 54 new float[] {2.0f, 2.0f}, 0.0f); 55 56 private static final Color COLOR1 = Color.BLUE; 57 private static final Color COLOR2 = Color.GREEN; 58 59 private static final Color BACKGROUND = Color.WHITE; 60 61 private static final int WIDTH = 20; 62 private static final int HEIGHT = 20; 63 64 private static final int OFFSET = 2; 65 66 private static final int MAX_DASH_LENGTH = 3; 67 main(String[] argv)68 public static void main(String[] argv) throws Exception { 69 final boolean saveImage = argv.length > 0 && "-save".equals(argv[0]); 70 71 final BufferedImage img = new BufferedImage(WIDTH, HEIGHT, 72 TYPE_INT_RGB); 73 try { 74 draw(img); 75 validate(img); 76 } finally { 77 if (saveImage) { 78 save(img, "bufferedImage.png"); 79 } 80 } 81 82 if (GraphicsEnvironment.isHeadless()) { 83 return; 84 } 85 86 BufferedImage snapshot = null; 87 try { 88 final GraphicsConfiguration gc = 89 GraphicsEnvironment.getLocalGraphicsEnvironment() 90 .getDefaultScreenDevice() 91 .getDefaultConfiguration(); 92 93 VolatileImage vi = gc.createCompatibleVolatileImage(WIDTH, HEIGHT); 94 int attempt = 0; 95 do { 96 vi.validate(gc); 97 draw(vi); 98 snapshot = vi.getSnapshot(); 99 } while (vi.contentsLost() && ++attempt <= 10); 100 if (attempt > 10) { 101 throw new RuntimeException("Too many attempts: " + attempt); 102 } 103 validate(snapshot); 104 } finally { 105 if (saveImage) { 106 save(snapshot, "volatileImage.png"); 107 } 108 } 109 } 110 draw(final Image img)111 private static void draw(final Image img) { 112 Graphics g = img.getGraphics(); 113 g.setColor(BACKGROUND); 114 g.fillRect(0, 0, WIDTH, HEIGHT); 115 g.setColor(COLOR1); 116 g.drawRect(OFFSET, OFFSET, WIDTH - OFFSET * 2, HEIGHT - OFFSET * 2); 117 g.setColor(COLOR2); 118 g.clipRect(OFFSET, OFFSET, WIDTH - OFFSET * 2 + 1, HEIGHT - OFFSET * 2 + 1); 119 ((Graphics2D) g).setStroke(dash); 120 g.drawRect(OFFSET, OFFSET, WIDTH - OFFSET * 2, HEIGHT - OFFSET * 2); 121 g.dispose(); 122 } 123 validate(final BufferedImage img)124 private static void validate(final BufferedImage img) { 125 checkHorizontalLine(img, OFFSET); 126 checkHorizontalLine(img, HEIGHT - OFFSET); 127 checkVerticalLine(img, OFFSET); 128 checkVerticalLine(img, WIDTH - OFFSET); 129 checkCorners(img); 130 } 131 checkHorizontalLine(final BufferedImage img, final int y)132 private static void checkHorizontalLine(final BufferedImage img, 133 final int y) { 134 int prev = img.getRGB(OFFSET, y); 135 int curr; 136 int count = 1; 137 checkColor(OFFSET, y, prev, COLOR1, COLOR2); 138 for (int x = OFFSET + 1; x <= WIDTH - OFFSET; x++) { 139 curr = img.getRGB(x, y); 140 if (curr != prev) { 141 checkColor(x, y, curr, COLOR1, COLOR2); 142 checkCount(x, y, count); 143 prev = curr; 144 count = 1; 145 } else { 146 count++; 147 } 148 if (x < WIDTH - OFFSET) { 149 checkColor(x, y - 1, img.getRGB(x, y - 1), BACKGROUND); 150 checkColor(x, y + 1, img.getRGB(x, y + 1), BACKGROUND); 151 } 152 } 153 checkCount(WIDTH - OFFSET, y, count); 154 } 155 checkVerticalLine(final BufferedImage img, final int x)156 private static void checkVerticalLine(final BufferedImage img, 157 final int x) { 158 int prev = img.getRGB(x, OFFSET); 159 checkColor(x, OFFSET, prev, COLOR1, COLOR2); 160 int count = 1; 161 for (int y = OFFSET + 1; y <= HEIGHT - OFFSET; y++) { 162 int curr = img.getRGB(x, y); 163 if (curr != prev) { 164 checkColor(x, y, curr, COLOR1, COLOR2); 165 checkCount(x, y, count); 166 prev = curr; 167 count = 1; 168 } else { 169 count++; 170 } 171 if (y < HEIGHT - OFFSET) { 172 checkColor(x - 1, y, img.getRGB(x - 1, y), BACKGROUND); 173 checkColor(x + 1, y, img.getRGB(x + 1, y), BACKGROUND); 174 } 175 } 176 checkCount(x, HEIGHT - OFFSET, count); 177 } 178 checkCorners(final BufferedImage img)179 private static void checkCorners(final BufferedImage img) { 180 int[][] corners = { 181 {OFFSET - 1, OFFSET - 1}, 182 {OFFSET, OFFSET - 1}, 183 {OFFSET - 1, OFFSET + 1}, 184 185 {OFFSET - 1, HEIGHT - OFFSET}, 186 {OFFSET - 1, HEIGHT - OFFSET + 1}, 187 {OFFSET, HEIGHT - OFFSET + 1}, 188 189 {WIDTH - OFFSET, OFFSET - 1}, 190 {WIDTH - OFFSET + 1, OFFSET - 1}, 191 {WIDTH - OFFSET + 1, OFFSET}, 192 193 {WIDTH - OFFSET + 1, HEIGHT - OFFSET}, 194 {WIDTH - OFFSET + 1, HEIGHT - OFFSET + 1}, 195 {WIDTH - OFFSET, HEIGHT - OFFSET + 1}, 196 }; 197 198 for (int[] corner : corners) { 199 int color = img.getRGB(corner[0], corner[1]); 200 checkColor(corner[0], corner[1], color, BACKGROUND); 201 } 202 } 203 checkColor(final int x, final int y, final int color, final Color... validColors)204 private static void checkColor(final int x, final int y, 205 final int color, 206 final Color... validColors) { 207 checkColor(x, y, color, Arrays.stream(validColors) 208 .mapToInt(Color::getRGB) 209 .toArray()); 210 } 211 checkColor(final int x, final int y, final int color, final int... validColors)212 private static void checkColor(final int x, final int y, 213 final int color, 214 final int... validColors) { 215 for (int valid : validColors) { 216 if (color == valid) { 217 return; 218 } 219 } 220 throw new RuntimeException("Unexpected color at " + x + ", " + y 221 + ": " + Integer.toHexString(color) + "; expected: " 222 + Arrays.stream(validColors) 223 .mapToObj(Integer::toHexString) 224 .collect(Collectors.joining(", "))); 225 } 226 checkCount(final int x, final int y, final int count)227 private static void checkCount(final int x, final int y, final int count) { 228 if (count > MAX_DASH_LENGTH) { 229 throw new RuntimeException("Dash is longer than " + MAX_DASH_LENGTH 230 + " at " + x + ", " + y); 231 } 232 } 233 save(final BufferedImage img, final String fileName)234 private static void save(final BufferedImage img, 235 final String fileName) throws IOException { 236 ImageIO.write(img, "png", new File(fileName)); 237 } 238 239 } 240