1 /*
2  * Copyright (c) 2016, 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.AlphaComposite;
25 import java.awt.BasicStroke;
26 import java.awt.Color;
27 import java.awt.Graphics2D;
28 import java.awt.Rectangle;
29 import java.awt.geom.Area;
30 import java.awt.image.BufferedImage;
31 import java.io.File;
32 
33 import javax.imageio.ImageIO;
34 
35 import static java.awt.RenderingHints.KEY_STROKE_CONTROL;
36 import static java.awt.RenderingHints.VALUE_STROKE_PURE;
37 import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
38 
39 /**
40  * @test
41  * @key headful
42  * @bug 8167310
43  * @summary The clip should be correct if the scale is fractional
44  */
45 public final class IncorrectFractionalClip {
46 
47     private static final int SIZE = 128;
48 
49     public static final Color RED = new Color(255, 0, 0, 100);
50 
51     public static final Color GREEN = new Color(0, 255, 0, 100);
52 
53     public static final Color WHITE = new Color(0, 0, 0, 0);
54 
55     public static final BasicStroke STROKE = new BasicStroke(2.01f);
56 
57     private static final double[] SCALES = {
58             0.1, 0.25, 0.4, 0.5, 0.6, 1, 1.4, 1.5, 1.6, 2.0, 2.4, 2.5, 2.6, 4
59     };
60 
61     static BufferedImage bi;
62 
63     static BufferedImage gold;
64 
65     static BufferedImage redI;
66 
67     static BufferedImage greenI;
68 
main(final String[] args)69     public static void main(final String[] args) throws Exception {
70         bi = new BufferedImage(SIZE, SIZE, TYPE_INT_ARGB);
71         gold = new BufferedImage(SIZE, SIZE, TYPE_INT_ARGB);
72         redI = createImage(RED);
73         greenI = createImage(GREEN);
74 
75         System.out.println("Will test fillRect");
76         test(0, true);
77         test(0, false);
78         System.out.println("Will test DrawImage");
79         test(1, true);
80         test(1, false);
81         System.out.println("Will test drawLine");
82         test(2, true);
83         test(2, false);
84     }
85 
86     /**
87      * This method draws/fills a number of rectangle, images and lines. Each
88      * time the clip is set as one vertical/horizontal line. The resulted image
89      * should not have any overlapping of different colors. The clip is set via
90      * rectangle(test) and via shape(gold). Both images should be identical.
91      */
test(final int testId, final boolean horiz)92     private static void test(final int testId, final boolean horiz)
93             throws Exception {
94         for (final double scale : SCALES) {
95             // Initialize the test and gold images
96             drawToImage(testId, horiz, scale, bi, /* Rectangle */ false);
97             drawToImage(testId, horiz, scale, gold, /* Shape */ true);
98             validate(bi, gold, testId);
99         }
100     }
101 
drawToImage(int testId, boolean horiz, double scale, BufferedImage image, boolean shape)102     private static void drawToImage(int testId, boolean horiz, double scale,
103                                     BufferedImage image, boolean shape) {
104         Graphics2D g = image.createGraphics();
105         g.setComposite(AlphaComposite.Src);
106         g.setColor(WHITE);
107         g.fillRect(0, 0, bi.getWidth(), bi.getHeight());
108         g.setComposite(AlphaComposite.SrcOver);
109         g.setRenderingHint(KEY_STROKE_CONTROL, VALUE_STROKE_PURE);
110 
111         // set the scale in one direction
112         if (horiz) {
113             g.scale(scale, 1);
114         } else {
115             g.scale(1, scale);
116         }
117         // cover all units in the user space to touch all pixels in the
118         // image after transform
119         final int destSize = (int) Math.ceil(SIZE / scale);
120         final int destW;
121         final int destH;
122         if (horiz) {
123             destW = destSize;
124             destH = SIZE;
125         } else {
126             destW = SIZE;
127             destH = destSize;
128         }
129         for (int step = 0; step < destSize; ++step) {
130             if (horiz) {
131                 if (!shape) {
132                     g.setClip(step, 0, 1, SIZE);
133                 } else{
134                     g.setClip(new Area(new Rectangle(step, 0, 1, SIZE)));
135                 }
136             } else {
137                 if (!shape) {
138                     g.setClip(0, step, SIZE, 1);
139                 }else{
140                     g.setClip(new Area(new Rectangle(0, step, SIZE, 1)));
141                 }
142             }
143             switch (testId) {
144                 case 0:
145                     g.setColor(step % 2 == 0 ? RED : GREEN);
146                     g.fillRect(0, 0, destW, destH);
147                     break;
148                 case 1:
149                     g.drawImage(step % 2 == 0 ? redI : greenI, 0, 0,
150                                 destW, destH, null);
151                     break;
152                 case 2:
153                     g.setColor(step % 2 == 0 ? RED : GREEN);
154                     g.setStroke(STROKE);
155                     if (horiz) {
156                         g.drawLine(step, 0, step, SIZE);
157                     } else {
158                         g.drawLine(0, step, SIZE, step);
159                     }
160                     break;
161                 default:
162                     throw new RuntimeException();
163             }
164         }
165         g.dispose();
166     }
167 
validate(final BufferedImage bi, BufferedImage gold, final int testID)168     private static void validate(final BufferedImage bi, BufferedImage gold,
169                                  final int testID) throws Exception {
170         for (int x = 0; x < SIZE; ++x) {
171             for (int y = 0; y < SIZE; ++y) {
172                 int rgb = bi.getRGB(x, y);
173                 int goldRGB = gold.getRGB(x, y);
174                 if ((rgb != GREEN.getRGB() && rgb != RED.getRGB())
175                         || rgb != goldRGB) {
176                     ImageIO.write(bi, "png", new File("image.png"));
177                     ImageIO.write(gold, "png", new File("gold.png"));
178                     throw new RuntimeException("Test failed.");
179                 }
180             }
181         }
182     }
183 
createImage(final Color color)184     private static BufferedImage createImage(final Color color) {
185         BufferedImage bi = new BufferedImage(SIZE, SIZE, TYPE_INT_ARGB);
186         Graphics2D g = bi.createGraphics();
187         g.setComposite(AlphaComposite.Src);
188         g.setColor(color);
189         g.fillRect(0, 0, bi.getWidth(), bi.getHeight());
190         g.dispose();
191         return bi;
192     }
193 }
194