1/*
2 * Copyright (c) 2003, 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 4641872
27 * @summary Tests writing compression modes of BMP plugin
28 * @modules java.desktop/com.sun.imageio.plugins.bmp
29 */
30
31import java.awt.Color;
32import java.awt.Dimension;
33import java.awt.Graphics;
34import java.awt.Graphics2D;
35import java.awt.Transparency;
36import java.awt.color.ColorSpace;
37import java.awt.image.BufferedImage;
38import java.awt.image.ColorModel;
39import java.awt.image.ComponentColorModel;
40import java.awt.image.DataBuffer;
41import java.awt.image.DirectColorModel;
42import java.awt.image.IndexColorModel;
43import java.awt.image.PixelInterleavedSampleModel;
44import java.awt.image.Raster;
45import java.awt.image.SampleModel;
46import java.awt.image.SinglePixelPackedSampleModel;
47import java.awt.image.WritableRaster;
48import java.io.ByteArrayInputStream;
49import java.io.ByteArrayOutputStream;
50import java.io.File;
51import java.io.FileOutputStream;
52import java.io.IOException;
53import java.util.Arrays;
54import java.util.Iterator;
55import java.util.LinkedList;
56import java.util.List;
57
58import javax.imageio.IIOImage;
59import javax.imageio.ImageIO;
60import javax.imageio.ImageReader;
61import javax.imageio.ImageTypeSpecifier;
62import javax.imageio.ImageWriteParam;
63import javax.imageio.ImageWriter;
64import javax.imageio.metadata.IIOMetadata;
65import javax.imageio.plugins.bmp.BMPImageWriteParam;
66import javax.imageio.stream.ImageOutputStream;
67import javax.swing.JComponent;
68import javax.swing.JFrame;
69
70import com.sun.imageio.plugins.bmp.BMPMetadata;
71
72public class BMPCompressionTest {
73
74    static final String format = "BMP";
75
76    public static void main(String[] args) {
77
78        ImageWriter iw = null;
79        Iterator writers = ImageIO.getImageWritersByFormatName(format);
80        if (!writers.hasNext()) {
81            throw new RuntimeException("No available Image writer for "+format);
82        }
83        iw = (ImageWriter)writers.next();
84
85
86        Iterator tests = Test.createTestSet(iw);
87
88        while(tests.hasNext()) {
89
90            Test t = (Test)tests.next();
91            System.out.println(t.getDescription());
92            t.doTest();
93        }
94
95    }
96
97
98    static class Test {
99        static ImageWriter iw;
100        private BufferedImage img;
101        private String description;
102        private BMPImageWriteParam param;
103        private IIOMetadata meta;
104
105
106        public static Iterator createTestSet(ImageWriter w) {
107            List l = new LinkedList();
108
109            Test.iw = w;
110
111            // variate compression types
112            BMPImageWriteParam param = (BMPImageWriteParam)iw.getDefaultWriteParam();
113            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
114            param.setCompressionType("BI_RGB");
115            if (param.canWriteCompressed()) {
116                String[] cTypes = param.getCompressionTypes();
117                String[] cDescr = param.getCompressionQualityDescriptions();
118                float[] cValues = param.getCompressionQualityValues();
119
120                if (cDescr == null) {
121                    System.out.println("There are no compression quality description!");
122                } else {
123                    for(int i=0; i<cDescr.length; i++) {
124                        System.out.println("Quality[" + i + "]=\""+cDescr[i]+"\"");
125                    }
126                }
127                if (cValues == null) {
128                    System.out.println("There are no compression quality values!");
129                } else {
130                    for(int i=0; i<cValues.length; i++) {
131                        System.out.println("Value["+i+"]=\""+cValues[i]+"\"");
132                    }
133                }
134
135                for(int i=0; i<cTypes.length; i++) {
136                    String compressionType = cTypes[i];
137                    BufferedImage img = null;
138
139                    int type = BufferedImage.TYPE_INT_BGR;
140                    try {
141                        img = createTestImage(type);
142                        if (compressionType.equals("BI_RLE8")) {
143                            img = createTestImage2(8, DataBuffer.TYPE_BYTE);
144                        } else if (compressionType.equals("BI_RLE4")) {
145                            img = createTestImage3(4, DataBuffer.TYPE_BYTE);
146                        } else if (compressionType.equals("BI_BITFIELDS")) {
147                            img = createTestImage4(32);
148                        }
149
150                    } catch (IOException ex) {
151                        throw new RuntimeException("Unable to create test image");
152                    }
153                    BMPImageWriteParam p = (BMPImageWriteParam)iw.getDefaultWriteParam();
154                    System.out.println("Current compression type is \""+cTypes[i]+"\"");
155                    p.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
156                    p.setCompressionType(compressionType);
157
158                    IIOMetadata md = iw.getDefaultImageMetadata(new ImageTypeSpecifier(img), p);
159
160                    l.add( new Test(p, md, img));
161                }
162            }
163            //     }
164            return l.iterator();
165
166        }
167
168        private Test(BMPImageWriteParam p, IIOMetadata md, BufferedImage i) {
169            param = p;
170            meta = md;
171            img = i;
172
173
174            description = "Compression type is " + p.getCompressionType();
175        }
176
177        public String getDescription() {
178            return description;
179        }
180
181        public void doTest() {
182            try {
183                System.out.println(this.getDescription());
184                if (param.getCompressionMode() != ImageWriteParam.MODE_EXPLICIT) {
185                    System.out.println("Warning: compression mode is not MODE_EXPLICIT");
186                }
187                // change metadata according to ImageWriteParam
188                IIOMetadata new_meta = iw.convertImageMetadata(meta, new ImageTypeSpecifier(img), param);
189
190                IIOImage iio_img = new IIOImage(img, null, new_meta);
191
192                ByteArrayOutputStream baos = new ByteArrayOutputStream();
193                ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
194                iw.setOutput(ios);
195                System.out.print("write image...");
196                System.out.println("Current compression Type is \""+param.getCompressionType()+"\"");
197                iw.write(new_meta, iio_img, param);
198                //iw.write(iio_img);
199                System.out.println("OK");
200                System.out.print("read image ... ");
201                ios.flush();
202
203                byte[] ba_image = baos.toByteArray();
204
205                System.out.println("Array length=" + ba_image.length);
206                FileOutputStream fos = new FileOutputStream(new File(param.getCompressionType()+".bmp"));
207                fos.write(ba_image);
208                fos.flush();
209                fos = null;
210                ByteArrayInputStream bais = new ByteArrayInputStream(ba_image);
211
212                ImageReader ir = ImageIO.getImageReader(iw);
213                ir.setInput(ImageIO.createImageInputStream(bais));
214
215                BufferedImage res = ir.read(0);
216                System.out.println("OK");
217
218                if (!param.getCompressionType().equals("BI_JPEG")) {
219                    System.out.print("compare images ... ");
220                    boolean r = compare(img,res);
221                    System.out.println(r?"OK":"FAILED");
222                    if (!r) {
223                        throw new RuntimeException("Compared images are not equals. Test failed.");
224                    }
225                }
226
227
228                BMPMetadata mdata = (BMPMetadata)ir.getImageMetadata(0);
229
230                if (!param.getCompressionType().equals(param.getCompressionTypes()[mdata.compression])) {
231                    throw new RuntimeException("Different compression value");
232                }
233
234            } catch (Exception ex) {
235                ex.printStackTrace();
236                throw new RuntimeException("Unexpected exception: " + ex);
237            }
238
239        }
240
241        private boolean compare(final BufferedImage in, final BufferedImage out) {
242
243            final int width = in.getWidth();
244            int height = in.getHeight();
245            if (out.getWidth() != width || out.getHeight() != height) {
246                throw new RuntimeException("Dimensions changed!");
247            }
248
249            Raster oldras = in.getRaster();
250            ColorModel oldcm = in.getColorModel();
251            Raster newras = out.getRaster();
252            ColorModel newcm = out.getColorModel();
253
254            for (int j = 0; j < height; j++) {
255                for (int i = 0; i < width; i++) {
256                    Object oldpixel = oldras.getDataElements(i, j, null);
257                    int oldrgb = oldcm.getRGB(oldpixel);
258                    int oldalpha = oldcm.getAlpha(oldpixel);
259
260                    Object newpixel = newras.getDataElements(i, j, null);
261                    int newrgb = newcm.getRGB(newpixel);
262                    int newalpha = newcm.getAlpha(newpixel);
263
264                    if (newrgb != oldrgb ||
265                        newalpha != oldalpha) {
266                        // showDiff(in, out);
267                        throw new RuntimeException("Pixels differ at " + i +
268                                                   ", " + j + " new = " + Integer.toHexString(newrgb) + " old = " + Integer.toHexString(oldrgb));
269                    }
270                }
271            }
272            return true;
273        }
274
275        private static BufferedImage createTestImage2(int nbits, int transfertype) {
276            final int colorShift = 2;
277            int SIZE = 256;
278            BufferedImage image = null;
279
280            ColorSpace colorSpace =
281                ColorSpace.getInstance(ColorSpace.CS_GRAY);
282            ColorModel colorModel =
283                new ComponentColorModel(colorSpace,
284                                        new int[] {nbits},
285                                        false,
286                                        false,
287                                        Transparency.OPAQUE,
288                                        transfertype);
289
290            SampleModel sampleModel =
291                new PixelInterleavedSampleModel(transfertype,
292                                                SIZE,
293                                                SIZE,
294                                                1,
295                                                SIZE,
296                                                new int[] {0});
297
298            image =
299                new BufferedImage(colorModel,
300                                  Raster.createWritableRaster(sampleModel, null),
301                                  false, null);
302            WritableRaster raster = image.getWritableTile(0, 0);
303            int[] samples = raster.getSamples(0, 0, SIZE, SIZE, 0, (int[])null);
304            int off = 0;
305            int[] row = new int[SIZE];
306            for(int i = 0; i < SIZE; i++) {
307                Arrays.fill(row, i << colorShift);
308                System.arraycopy(row, 0, samples, off, SIZE);
309                off += SIZE;
310            }
311            raster.setSamples(0, 0, SIZE, SIZE, 0, samples);
312
313            return image;
314        }
315
316
317        private static BufferedImage createTestImage3(int nbits, int transfertype) {
318            final int colorShift = 2;
319            int SIZE = 256;
320            BufferedImage image = null;
321
322            ColorSpace colorSpace =
323                ColorSpace.getInstance(ColorSpace.CS_sRGB);
324            ColorModel colorModel =
325                new IndexColorModel(nbits,
326                                    4,
327                                    new byte[] { (byte)255,   0,   0, (byte)255},
328                                    new byte[] {   0, (byte)255,   0, (byte)255},
329                                    new byte[] {   0,   0, (byte)255, (byte)255});
330
331            SampleModel sampleModel =
332                new PixelInterleavedSampleModel(transfertype,
333                                                SIZE,
334                                                SIZE,
335                                                1,
336                                                SIZE,
337                                                new int[] {0});
338
339            image =
340                new BufferedImage(colorModel,
341                                  Raster.createWritableRaster(sampleModel, null),
342
343                                  false, null);
344
345            Graphics2D g = image.createGraphics();
346            g.setColor(Color.white);
347            g.fillRect(0,0, SIZE, SIZE);
348            g.setColor(Color.red);
349            g.fillOval(10, 10, SIZE -20, SIZE-20);
350
351            return image;
352        }
353
354        private static BufferedImage createTestImage4(int nbits) {
355            int SIZE = 10;
356
357
358            BufferedImage image = null;
359
360            ColorSpace colorSpace =
361                ColorSpace.getInstance(ColorSpace.CS_sRGB);
362            ColorModel colorModel =
363                new DirectColorModel(colorSpace,
364                                     nbits, 0xff0000, 0x00ff00, 0x0000ff, 0x000000, false, DataBuffer.TYPE_INT);
365
366            SampleModel sampleModel =
367                new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT,
368                                                SIZE,
369                                                SIZE,
370                                      new int[] { 0xff0000, 0x00ff00, 0x0000ff} );
371
372
373            image =
374                new BufferedImage(colorModel,
375                                  Raster.createWritableRaster(sampleModel, null),
376
377                                  false, null);
378
379            Graphics2D g = image.createGraphics();
380            g.setColor(Color.red);
381            g.fillRect(0,0, SIZE, SIZE);
382            g.setColor(Color.green);
383            //g.fillOval(10, 10, SIZE -20, SIZE-20);
384            g.drawLine(7, 0, 7, SIZE);
385            g.setColor(Color.blue);
386            g.drawLine(1, 0, 1, SIZE);
387            g.setColor(Color.white);
388            g.drawLine(3, 0, 3, SIZE);
389            g.setColor(Color.yellow);
390            g.drawLine(5, 0, 5, SIZE);
391            return image;
392        }
393
394        private static BufferedImage createTestImage(int type)
395          throws IOException {
396
397            int w = 200;
398            int h = 200;
399            BufferedImage b = new BufferedImage(w, h, type);
400            Graphics2D g = b.createGraphics();
401            g.setColor(Color.white);
402            g.fillRect(0,0, w, h);
403            g.setColor(Color.black);
404            g.fillOval(10, 10, w -20, h-20);
405
406            return b;
407        }
408
409
410    }
411
412    private static void showDiff(final BufferedImage in,
413                                 final BufferedImage out) {
414        final int width = in.getWidth();
415        final int height = in.getHeight();
416
417        JFrame f = new JFrame("");
418        f.getContentPane().add( new JComponent() {
419                public Dimension getPreferredSize() {
420                    return new Dimension(2*width+2, height);
421                }
422                public void paintComponent(Graphics g) {
423                    g.setColor(Color.black);
424                    g.drawImage(in, 0,0, null);
425
426                    g.drawImage(out, width+2, 0, null);
427                }
428            });
429        f.pack();
430        f.setVisible(true);
431    }
432
433}
434