1 /*
   2  * Copyright (c) 2014, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.javafx.iio;
  27 
  28 import com.sun.javafx.iio.bmp.BMPImageLoaderFactory;
  29 import com.sun.prism.Image;
  30 import java.awt.image.BufferedImage;
  31 import java.awt.image.DataBuffer;
  32 import java.awt.image.IndexColorModel;
  33 import java.io.ByteArrayInputStream;
  34 import java.io.ByteArrayOutputStream;
  35 import java.io.File;
  36 import java.io.FileInputStream;
  37 import java.io.IOException;
  38 import java.io.InputStream;
  39 import javax.imageio.ImageIO;
  40 import javax.imageio.stream.MemoryCacheImageInputStream;
  41 import static org.junit.Assert.*;
  42 import org.junit.Test;
  43 
  44 public class BMPImageLoaderTest {
  45     // if true, the test will write BMP files generated by JDK to the current directory
  46     static final boolean writeFiles = false;
  47     static final int testWidth = 509, testHeight = 157;
  48 
  49     ByteArrayInputStream constructStream(int[] bytes) {
  50         ByteArrayOutputStream baos = new ByteArrayOutputStream();
  51         for (int b : bytes) {
  52             baos.write(b);
  53         }
  54         return new ByteArrayInputStream(baos.toByteArray());
  55     }
  56 
  57     int getByte(int dword, int shift) {
  58         return (dword >> shift) & 0xff;
  59     }
  60 
  61     boolean compareByte(int p1, int p2, int shift, int tolerance) {
  62         return Math.abs(getByte(p1, shift) - getByte(p2, shift)) <= tolerance;
  63     }
  64 
  65     boolean compareRGB(int p1, int p2, int tolerance) {
  66         return compareByte(p1, p2, 24, tolerance) &&
  67                compareByte(p1, p2, 16, tolerance) &&
  68                compareByte(p1, p2, 8,  tolerance);
  69     }
  70 
  71     void compare(Image img, BufferedImage bImg) {
  72         assertNotNull(img);
  73         assertNotNull(bImg);
  74         int w = bImg.getWidth(), h = bImg.getHeight();
  75         assertEquals("Unmatched width", w, img.getWidth());
  76         assertEquals("Unmatched height", h, img.getHeight());
  77 
  78         for (int y = 0; y < h; y++) {
  79             for (int x = 0; x < w; x++) {
  80                 int p1 = bImg.getRGB(x, y);
  81                 int p2 = img.getArgb(x, y);
  82                 if (!compareRGB(p1, p2, 1)) {
  83                     throw new org.junit.ComparisonFailure(
  84                         "pixel " + x + ", " + y + " does not match", 
  85                         String.format("0x%08X", p1), String.format("0x%08X", p2)
  86                     );
  87                 }
  88             }
  89         }
  90     }
  91 
  92     Image loadImage(InputStream stream) {
  93         ImageLoaderFactory loaderFactory = BMPImageLoaderFactory.getInstance();
  94         ImageLoader loader = null;
  95         try {
  96             loader = loaderFactory.createImageLoader(stream);
  97         } catch (IOException ioEx) {
  98             fail("unexpected IOException: " + ioEx);
  99         }
 100         assertNotNull(loader);
 101 
 102         try {
 103             ImageFrame frame = loader.load(0, 0, 0, true, true);
 104             return Image.convertImageFrame(frame);
 105         } catch (IOException e) {
 106             fail("unexpected IOException: " + e);
 107         }
 108         return null;
 109     }
 110 
 111     BufferedImage create4BitImage() {
 112         int[] cmap = new int[16];
 113         int i = 0;
 114         for (int r = 0; r < 2; r++) {
 115             for (int g = 0; g < 2; g++) {
 116                 for (int b = 0; b < 2; b++) {
 117                     cmap[i++] = 0xff << 24 | r * 255 << 16 | g * 255 << 8 | b * 255;
 118                     if ((r | g | b) == 0) {
 119                         cmap[i++] = 0xffc0c0c0;
 120                     } else {
 121                         cmap[i++] = 0xff << 24 | r * 128 << 16 | g * 128 << 8 | b * 128;
 122                     }
 123                 }
 124             }
 125         }
 126         IndexColorModel cm = new IndexColorModel(4, 16, cmap, 0, false, -1, DataBuffer.TYPE_BYTE);
 127         return new BufferedImage(testWidth, testHeight, BufferedImage.TYPE_BYTE_BINARY, cm);
 128     }
 129 
 130     BufferedImage createImage(int type) {
 131         return new BufferedImage(testWidth, testHeight, type);
 132     }
 133 
 134     void writeBMPImage(BufferedImage bImg, String fileName, String compression) {
 135         ImageTestHelper.writeImage(bImg, fileName, "bmp", compression); 
 136    }
 137 
 138     Image getImage(BufferedImage bImg, String compression) {
 139         ByteArrayInputStream stream =
 140                 ImageTestHelper.writeImageToStream(bImg, "bmp", compression, null);
 141         return loadImage(stream);
 142     }
 143 
 144     void testImageType(int type, String fileName, String compression) {
 145         BufferedImage bImg = createImage(type);
 146         testImage(bImg, fileName, compression);
 147     }
 148 
 149     void testImageType(int type, String fileName) {
 150         BufferedImage bImg = createImage(type);
 151         testImage(bImg, fileName, null);
 152     }
 153 
 154     void testImage(BufferedImage bImg, String fileName, String compression) {
 155         //ImageTestHelper.drawImageHue(bImg);
 156         //ImageTestHelper.drawImageAll(bImg);
 157         ImageTestHelper.drawImageRandom(bImg);
 158         if (writeFiles) {
 159             writeBMPImage(bImg, fileName, compression);
 160         }
 161         Image image = getImage(bImg, compression);
 162         compare(image, bImg);
 163     }
 164 
 165     @Test
 166     public void testRT32213()  {
 167         final int[] bytes = {
 168             0x42, 0x4d, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
 169             0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x28, 0x00,
 170             0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
 171             0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
 172             0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
 173             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 174             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 175             0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x80, 0x00,
 176             0x00, 0x00
 177         };
 178 
 179         ByteArrayInputStream stream = constructStream(bytes);
 180         Image image = loadImage(stream);
 181         stream.reset();
 182         try {
 183             BufferedImage bImg = ImageIO.read(new MemoryCacheImageInputStream(stream));
 184             compare(image, bImg);
 185         } catch (IOException e) {
 186             fail("unexpected IOException: " + e);
 187         }
 188     }
 189 
 190     private static class RT15619InputStream extends InputStream {
 191 
 192         private final InputStream delegate;
 193 
 194         public RT15619InputStream(InputStream delegate) {
 195             this.delegate = delegate;
 196         }
 197 
 198         @Override
 199         public int read() throws IOException {
 200             return delegate.read();
 201         }
 202 
 203         // read by 128 bytes chunks
 204         @Override
 205         public int read(byte[] b, int off, int len) throws IOException {
 206             return delegate.read(b, off, Math.min(len, 128));
 207         }
 208     }
 209 
 210     @Test
 211     public void testRT15619() {
 212         BufferedImage bImg = createImage(BufferedImage.TYPE_INT_RGB);
 213         ImageTestHelper.drawImageRandom(bImg);
 214         ByteArrayInputStream stream =
 215                 ImageTestHelper.writeImageToStream(bImg, "bmp", null, null);
 216         RT15619InputStream testStream = new RT15619InputStream(stream);
 217         Image image = loadImage(testStream);
 218         compare(image, bImg);
 219     }
 220 
 221     @Test
 222     public void test1Bit() {
 223         testImageType(BufferedImage.TYPE_BYTE_BINARY, "out1bit.bmp");
 224     }
 225 
 226     @Test
 227     public void test4Bit() {
 228         testImage(create4BitImage(), "out4bit.bmp", null);
 229     }
 230 
 231     //@Test
 232     public void test4BitRLE() {
 233         testImage(create4BitImage(), "out4bitRLE.bmp", "BI_RLE4");
 234     }
 235 
 236     @Test
 237     public void test8Bit() {
 238         testImageType(BufferedImage.TYPE_BYTE_INDEXED, "out8bit.bmp");
 239     }
 240 
 241     @Test
 242     public void test8BitRLE() {
 243         testImageType(BufferedImage.TYPE_BYTE_INDEXED, "out8bitRLE.bmp", "BI_RLE8");
 244     }
 245 
 246     @Test
 247     public void test16Bit() {
 248         testImageType(BufferedImage.TYPE_USHORT_555_RGB, "out16bit.bmp");
 249     }
 250 
 251     @Test
 252     public void test24Bit() {
 253         testImageType(BufferedImage.TYPE_INT_RGB, "out24bit.bmp");
 254     }
 255 
 256     void testFile(String fileName, String outFileName, String compression) {
 257         try {
 258             Image image = loadImage(new FileInputStream(fileName));
 259             BufferedImage bImg = ImageIO.read(new File(fileName));
 260             if (writeFiles) {
 261                 writeBMPImage(bImg, outFileName, compression);
 262             }
 263             compare(image, bImg);
 264         } catch (IOException e) {
 265             fail("unexpected IOException: " + e);
 266         }
 267     }
 268 
 269     //@Test
 270     public void testFiles() {
 271         testFile("pal4rle.bmp", "pal4rleOut.bmp", "BI_RLE4");
 272         testFile("out4bitRLE.bmp", "out4bitRLEOut.bmp", "BI_RLE4");
 273         testFile("pal8rletrns.bmp", "pal8rletrnsOut.bmp", null);
 274     }
 275 }