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     @Test
 191     public void test1Bit() {
 192         testImageType(BufferedImage.TYPE_BYTE_BINARY, "out1bit.bmp");
 193     }
 194 
 195     @Test
 196     public void test4Bit() {
 197         testImage(create4BitImage(), "out4bit.bmp", null);
 198     }
 199 
 200     //@Test
 201     public void test4BitRLE() {
 202         testImage(create4BitImage(), "out4bitRLE.bmp", "BI_RLE4");
 203     }
 204 
 205     @Test
 206     public void test8Bit() {
 207         testImageType(BufferedImage.TYPE_BYTE_INDEXED, "out8bit.bmp");
 208     }
 209 
 210     @Test
 211     public void test8BitRLE() {
 212         testImageType(BufferedImage.TYPE_BYTE_INDEXED, "out8bitRLE.bmp", "BI_RLE8");
 213     }
 214 
 215     @Test
 216     public void test16Bit() {
 217         testImageType(BufferedImage.TYPE_USHORT_555_RGB, "out16bit.bmp");
 218     }
 219 
 220     @Test
 221     public void test24Bit() {
 222         testImageType(BufferedImage.TYPE_INT_RGB, "out24bit.bmp");
 223     }
 224 
 225     void testFile(String fileName, String outFileName, String compression) {
 226         try {
 227             Image image = loadImage(new FileInputStream(fileName));
 228             BufferedImage bImg = ImageIO.read(new File(fileName));
 229             if (writeFiles) {
 230                 writeBMPImage(bImg, outFileName, compression);
 231             }
 232             compare(image, bImg);
 233         } catch (IOException e) {
 234             fail("unexpected IOException: " + e);
 235         }
 236     }
 237 
 238     //@Test
 239     public void testFiles() {
 240         testFile("pal4rle.bmp", "pal4rleOut.bmp", "BI_RLE4");
 241         testFile("out4bitRLE.bmp", "out4bitRLEOut.bmp", "BI_RLE4");
 242         testFile("pal8rletrns.bmp", "pal8rletrnsOut.bmp", null);
 243     }
 244 }