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 
  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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 }