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