1/*
2 * Copyright (c) 2006, 2017, 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 * @bug 5076878
27 * @summary Test verifies that ImageIO creates BMP images with correct bpp
28 */
29
30import java.awt.Color;
31import java.awt.Graphics;
32import java.awt.Transparency;
33import java.awt.color.ColorSpace;
34import java.awt.image.BufferedImage;
35import java.awt.image.ColorModel;
36import java.awt.image.ComponentColorModel;
37import java.awt.image.DataBuffer;
38import java.awt.image.DirectColorModel;
39import java.awt.image.Raster;
40import java.awt.image.WritableRaster;
41import java.io.File;
42import java.io.FileInputStream;
43import java.io.IOException;
44import java.util.Hashtable;
45
46import javax.imageio.IIOImage;
47import javax.imageio.ImageIO;
48import javax.imageio.ImageReader;
49import javax.imageio.metadata.IIOMetadata;
50import javax.imageio.stream.ImageInputStream;
51
52import org.w3c.dom.Node;
53
54public class NoExtraBytesTest {
55
56    private static Hashtable<Integer, Integer> tests = null;
57    private static Color[] usedColors = new Color[] { Color.red, Color.green,     Color.blue, Color.yellow, Color.white, Color.black };
58
59    private static final int TYPE_INT_GRB = 0x100;
60    private static final int TYPE_INT_GBR = 0x101;
61    private static final int TYPE_INT_RBG = 0x102;
62    private static final int TYPE_INT_BRG = 0x103;
63    private static final int TYPE_INT_555_GRB = 0x104;
64    private static final int TYPE_3BYTE_RGB = 0x105;
65    private static final int TYPE_3BYTE_GRB = 0x106;
66
67    private static final int w = 300;
68    private static final int h = 200;
69    private static final int dx = w / usedColors.length;
70
71    public static void main(String[] args) throws IOException {
72        initTests();
73
74        for (Integer type : tests.keySet()) {
75            new NoExtraBytesTest(type.intValue(), tests.get(type).intValue()).doTest();
76        }
77        System.out.println("Test passed.");
78    }
79
80    private static void initTests() {
81        tests = new Hashtable<Integer, Integer>();
82
83        tests.put(new Integer(BufferedImage.TYPE_INT_RGB), new Integer(24));
84        tests.put(new Integer(BufferedImage.TYPE_INT_BGR), new Integer(24));
85        tests.put(new Integer(BufferedImage.TYPE_3BYTE_BGR), new Integer(24));
86        tests.put(new Integer(TYPE_INT_GRB), new Integer(24));
87        tests.put(new Integer(TYPE_INT_GBR), new Integer(24));
88        tests.put(new Integer(TYPE_INT_RBG), new Integer(24));
89        tests.put(new Integer(TYPE_INT_BRG), new Integer(24));
90        tests.put(new Integer(BufferedImage.TYPE_USHORT_555_RGB), new Integer(16));
91        tests.put(new Integer(BufferedImage.TYPE_USHORT_565_RGB), new Integer(16));
92        tests.put(new Integer(TYPE_INT_555_GRB), new Integer(16));
93        tests.put(new Integer(TYPE_3BYTE_RGB), new Integer(24));
94        tests.put(new Integer(TYPE_3BYTE_GRB), new Integer(24));
95    }
96
97    private static String getImageTypeName(int t) {
98        switch(t) {
99            case BufferedImage.TYPE_INT_RGB:
100                return "TYPE_INT_RGB";
101            case BufferedImage.TYPE_INT_BGR:
102                return "TYPE_INT_BGR";
103            case BufferedImage.TYPE_3BYTE_BGR:
104                return "TYPE_3BYTE_BGR";
105            case BufferedImage.TYPE_USHORT_555_RGB:
106                return "TYPE_USHORT_555_RGB";
107            case BufferedImage.TYPE_USHORT_565_RGB:
108                return "TYPE_USHORT_565_RGB";
109            case TYPE_INT_GRB:
110                return "TYPE_INT_GRB";
111            case TYPE_INT_GBR:
112                return "TYPE_INT_GBR";
113            case TYPE_INT_RBG:
114                return "TYPE_INT_RBG";
115            case TYPE_INT_BRG:
116                return "TYPE_INT_BRG";
117            case TYPE_INT_555_GRB:
118                return "TYPE_INT_555_GRB";
119            case TYPE_3BYTE_RGB:
120                return "TYPE_3BYTE_RGB";
121            case TYPE_3BYTE_GRB:
122                return "TYPE_3BYTE_GRB";
123            default:
124                throw new IllegalArgumentException("Unknown image type: " + t);
125        }
126    }
127    private static BufferedImage createTestImage(int type) {
128        BufferedImage dst = null;
129        ColorModel colorModel = null;
130        WritableRaster raster = null;
131        ColorSpace cs = null;
132        System.out.println("Create image for " + getImageTypeName(type));
133        switch(type) {
134            case TYPE_INT_GRB:
135                colorModel = new DirectColorModel(24,
136                    0x0000ff00,
137                    0x00ff0000,
138                    0x000000ff);
139                break;
140            case TYPE_INT_GBR:
141                colorModel = new DirectColorModel(24,
142                    0x000000ff,
143                    0x00ff0000,
144                    0x0000ff00);
145                break;
146            case TYPE_INT_RBG:
147                colorModel = new DirectColorModel(24,
148                    0x00ff0000,
149                    0x000000ff,
150                    0x0000ff00);
151                break;
152            case TYPE_INT_BRG:
153                colorModel = new DirectColorModel(24,
154                    0x0000ff00,
155                    0x000000ff,
156                    0x00ff0000);
157                break;
158            case TYPE_INT_555_GRB:
159                colorModel = new DirectColorModel(24,
160                        0x0000001F,
161                        0x000003e0,
162                        0x00007c00);
163                break;
164            case TYPE_3BYTE_RGB:
165                cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
166                int[] nBits = {8, 8, 8};
167                int[] bOffs = {0, 1, 2};
168                colorModel = new ComponentColorModel(cs, nBits, false, false,
169                                                     Transparency.OPAQUE,
170                                                     DataBuffer.TYPE_BYTE);
171                raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
172                                                        w, h,
173                                                        w*3, 3,
174                                                        bOffs, null);
175                break;
176            case TYPE_3BYTE_GRB:
177                cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
178                //nBits = {8, 8, 8};
179                //bOffs = {0, 1, 2};
180                colorModel = new ComponentColorModel(cs, new int[] { 8, 8, 8 }, false, false,
181                                                     Transparency.OPAQUE,
182                                                     DataBuffer.TYPE_BYTE);
183                raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
184                        w, h,
185                        w*3, 3,
186                        new int[] { 1, 0, 2}, null);
187                break;
188            default:
189                dst = new BufferedImage(w, h, type);
190                //colorModel = ImageTypeSpecifier.createFromBufferedImageType(type).getColorModel();
191        }
192
193        if (dst == null) {
194            if (raster == null) {
195                raster = colorModel.createCompatibleWritableRaster(w, h);
196            }
197
198            dst = new BufferedImage(colorModel, raster, false, null);
199        }
200        Graphics g = dst.createGraphics();
201        for (int i = 0; i < usedColors.length; i ++) {
202            g.setColor(usedColors[i]);
203            g.fillRect(i * dx, 0, dx, h);
204        }
205        g.dispose();
206
207        return dst;
208    }
209
210    private BufferedImage src;
211    private int expectedColorDepth;
212    private int type;
213
214    private IIOImage iio_dst;
215
216    public NoExtraBytesTest(int type, int expectedColorDepth) {
217        this.type = type;
218        this.src = createTestImage(type);
219        this.expectedColorDepth = expectedColorDepth;
220    }
221
222    public void doTest() throws IOException {
223        // write src as BMP
224        System.out.println("Test for image: " + getImageTypeName(type));
225        System.out.println("image is " + src);
226
227        File f = File.createTempFile("sizeTest_", ".bmp", new File("."));
228        System.out.println("Use file " + f.getCanonicalPath());
229        ImageIO.write(src, "BMP", f);
230
231        //read it again
232        read(f);
233
234        checkColorDepth();
235
236        checkImageContent();
237    }
238
239    private void read(File f) throws IOException {
240        ImageReader reader = ImageIO.getImageReadersByFormatName("BMP").next();
241
242        ImageInputStream iis =
243                ImageIO.createImageInputStream(new FileInputStream(f));
244
245        reader.setInput(iis);
246
247        iio_dst = reader.readAll(0, reader.getDefaultReadParam());
248    }
249
250    private void checkColorDepth() {
251        IIOMetadata dst = iio_dst.getMetadata();
252
253        Node data = dst.getAsTree("javax_imageio_bmp_1.0");
254
255        Node n = data.getFirstChild();
256
257        while (n != null && !("BitsPerPixel".equals(n.getNodeName()))) {
258            System.out.println("Node " + n.getNodeName());
259            n = n.getNextSibling();
260        }
261        if (n == null) {
262            throw new RuntimeException("No BitsPerSample node!");
263        }
264
265        int bpp = 0;
266        String value = n.getNodeValue();
267        System.out.println("value = " + value);
268        try {
269            bpp = Integer.parseInt(value);
270        } catch (NumberFormatException e) {
271            throw new RuntimeException("Wrong bpp value: " + value, e);
272        }
273
274        if (bpp != this.expectedColorDepth) {
275            throw new RuntimeException("Wrong color depth: " + bpp +
276                    " (should be " + this.expectedColorDepth + ")");
277        }
278    }
279
280    private void checkImageContent() {
281        BufferedImage dst =
282                (BufferedImage)iio_dst.getRenderedImage();
283        int y = h / 2;
284        int x = dx / 2;
285
286        for (int i = 0; i < usedColors.length; i++, x += dx) {
287            int srcRgb = src.getRGB(x, y);
288            int dstRgb = dst.getRGB(x, y);
289            int rgb = usedColors[i].getRGB();
290
291            if (dstRgb != srcRgb || dstRgb != rgb) {
292                throw new RuntimeException("Wrong color at [" + x + ", " + y +
293                        "] " + Integer.toHexString(dstRgb) +
294                        " (srcRgb=" + Integer.toHexString(srcRgb) +
295                        ", original color is " + Integer.toHexString(rgb) + ")");
296            }
297
298        }
299        System.out.println("Image colors are OK.");
300    }
301}
302