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 test.com.sun.javafx.iio.bmp;
  27 
  28 import com.sun.javafx.iio.ImageFrame;
  29 import com.sun.javafx.iio.ImageLoader;
  30 import com.sun.javafx.iio.ImageLoaderFactory;
  31 import com.sun.javafx.iio.bmp.BMPImageLoaderFactory;
  32 import com.sun.javafx.iio.bmp.BMPImageLoaderShim;
  33 import test.com.sun.javafx.iio.ImageTestHelper;
  34 import com.sun.prism.Image;
  35 import java.awt.image.BufferedImage;
  36 import java.awt.image.DataBuffer;
  37 import java.awt.image.IndexColorModel;
  38 import java.io.ByteArrayInputStream;
  39 import java.io.IOException;
  40 import java.io.InputStream;
  41 import javax.imageio.ImageIO;
  42 import javax.imageio.stream.MemoryCacheImageInputStream;
  43 import static org.junit.Assert.*;
  44 import org.junit.Test;
  45 
  46 public class BMPImageLoaderTest {
  47     // if true, the test will write BMP files generated by JDK to the current directory
  48     static final boolean writeFiles = false;
  49     static final int testWidth = 509, testHeight = 157;
  50 
  51     int getByte(int dword, int shift) {
  52         return (dword >> shift) & 0xff;
  53     }
  54 
  55     boolean compareByte(int p1, int p2, int shift, int tolerance) {
  56         return Math.abs(getByte(p1, shift) - getByte(p2, shift)) <= tolerance;
  57     }
  58 
  59     boolean compareRGB(int p1, int p2, int tolerance) {
  60         return compareByte(p1, p2, 24, tolerance) &&
  61                compareByte(p1, p2, 16, tolerance) &&
  62                compareByte(p1, p2, 8,  tolerance);
  63     }
  64 
  65     void compare(Image img, BufferedImage bImg) {
  66         assertNotNull(img);
  67         assertNotNull(bImg);
  68         int w = bImg.getWidth(), h = bImg.getHeight();
  69         assertEquals("Unmatched width", w, img.getWidth());
  70         assertEquals("Unmatched height", h, img.getHeight());
  71 
  72         for (int y = 0; y < h; y++) {
  73             for (int x = 0; x < w; x++) {
  74                 int p1 = bImg.getRGB(x, y);
  75                 int p2 = img.getArgb(x, y);
  76                 if (!compareRGB(p1, p2, 1)) {
  77                     throw new org.junit.ComparisonFailure(
  78                         "pixel " + x + ", " + y + " does not match", 
  79                         String.format("0x%08X", p1), String.format("0x%08X", p2)
  80                     );
  81                 }
  82             }
  83         }
  84     }
  85 
  86     Image loadImage(InputStream stream) throws IOException {
  87         ImageLoaderFactory loaderFactory = BMPImageLoaderFactory.getInstance();
  88         ImageLoader loader = loaderFactory.createImageLoader(stream);
  89         assertNotNull(loader);
  90 
  91         ImageFrame frame = loader.load(0, 0, 0, true, true);
  92         return Image.convertImageFrame(frame);
  93     }
  94 
  95     BufferedImage create4BitImage() {
  96         int[] cmap = new int[16];
  97         int i = 0;
  98         for (int r = 0; r < 2; r++) {
  99             for (int g = 0; g < 2; g++) {
 100                 for (int b = 0; b < 2; b++) {
 101                     cmap[i++] = 0xff << 24 | r * 255 << 16 | g * 255 << 8 | b * 255;
 102                     if ((r | g | b) == 0) {
 103                         cmap[i++] = 0xffc0c0c0;
 104                     } else {
 105                         cmap[i++] = 0xff << 24 | r * 128 << 16 | g * 128 << 8 | b * 128;
 106                     }
 107                 }
 108             }
 109         }
 110         IndexColorModel cm = new IndexColorModel(4, 16, cmap, 0, false, -1, DataBuffer.TYPE_BYTE);
 111         return new BufferedImage(testWidth, testHeight, BufferedImage.TYPE_BYTE_BINARY, cm);
 112     }
 113 
 114     BufferedImage createImage(int type) {
 115         return new BufferedImage(testWidth, testHeight, type);
 116     }
 117 
 118     void writeBMPFile(BufferedImage bImg, String fileName, String compression) {
 119         try {
 120             ImageTestHelper.writeImage(bImg, fileName, "bmp", compression);
 121         } catch (IOException e) {
 122             System.out.println("writeBMPFile " + fileName + " failed: " + e);
 123         }
 124     }
 125 
 126     Image getImage(BufferedImage bImg, String compression) throws IOException {
 127         ByteArrayInputStream stream =
 128                 ImageTestHelper.writeImageToStream(bImg, "bmp", compression);
 129         return loadImage(stream);
 130     }
 131 
 132     void testImageType(int type, String fileName, String compression) throws IOException {
 133         BufferedImage bImg = createImage(type);
 134         testImage(bImg, fileName, compression);
 135     }
 136 
 137     void testImageType(int type, String fileName) throws IOException {
 138         BufferedImage bImg = createImage(type);
 139         testImage(bImg, fileName, null);
 140     }
 141 
 142     void testImage(BufferedImage bImg, String fileName, String compression) throws IOException {
 143         //ImageTestHelper.drawImageHue(bImg);
 144         //ImageTestHelper.drawImageAll(bImg);
 145         ImageTestHelper.drawImageRandom(bImg);
 146         if (writeFiles) {
 147             writeBMPFile(bImg, fileName, compression);
 148         }
 149         Image image = getImage(bImg, compression);
 150         compare(image, bImg);
 151     }
 152 
 153     @Test
 154     public void testRT32213() throws IOException  {
 155         final int[] bytes = {
 156             0x42, 0x4d, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
 157             0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x28, 0x00,
 158             0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
 159             0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
 160             0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
 161             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 162             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 163             0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x80, 0x00,
 164             0x00, 0x00
 165         };
 166 
 167         ByteArrayInputStream stream = ImageTestHelper.constructStreamFromInts(bytes);
 168         Image image = loadImage(stream);
 169         stream.reset();
 170         BufferedImage bImg = ImageIO.read(new MemoryCacheImageInputStream(stream));
 171         compare(image, bImg);
 172     }
 173 
 174     @Test
 175     public void testRT15619() throws IOException {
 176         InputStream stream = ImageTestHelper.createTestImageStream("bmp");
 177         InputStream testStream = ImageTestHelper.createStutteringInputStream(stream);
 178         loadImage(testStream);
 179     }
 180 
 181     @Test
 182     public void test1Bit() throws IOException {
 183         testImageType(BufferedImage.TYPE_BYTE_BINARY, "out1bit.bmp");
 184     }
 185 
 186     @Test
 187     public void test4Bit() throws IOException {
 188         testImage(create4BitImage(), "out4bit.bmp", null);
 189     }
 190 
 191     //@Test
 192     public void test4BitRLE() throws IOException {
 193         testImage(create4BitImage(), "out4bitRLE.bmp", "BI_RLE4");
 194     }
 195 
 196     @Test
 197     public void test8Bit() throws IOException {
 198         testImageType(BufferedImage.TYPE_BYTE_INDEXED, "out8bit.bmp");
 199     }
 200 
 201     @Test
 202     public void test8BitRLE() throws IOException {
 203         testImageType(BufferedImage.TYPE_BYTE_INDEXED, "out8bitRLE.bmp", "BI_RLE8");
 204     }
 205 
 206     @Test
 207     public void test16Bit() throws IOException {
 208         testImageType(BufferedImage.TYPE_USHORT_555_RGB, "out16bit.bmp");
 209     }
 210 
 211     @Test
 212     public void test24Bit() throws IOException {
 213         testImageType(BufferedImage.TYPE_INT_RGB, "out24bit.bmp");
 214     }
 215 
 216     @Test
 217     public void testBitfields() throws IOException {
 218         testImageType(BufferedImage.TYPE_USHORT_555_RGB, "out16bit555.bmp", "BI_BITFIELDS");
 219         testImageType(BufferedImage.TYPE_USHORT_565_RGB, "out16bit565.bmp", "BI_BITFIELDS");
 220     }
 221 
 222     @Test
 223     public void testMasks() {
 224         assertTrue(BMPImageLoaderShim.checkDisjointMasks(1, 2, 4));
 225         assertTrue(BMPImageLoaderShim.checkDisjointMasks(0x00F, 0x0F0, 0xF00));
 226         assertFalse(BMPImageLoaderShim.checkDisjointMasks(1, 2, 5));
 227         assertFalse(BMPImageLoaderShim.checkDisjointMasks(2, 1, 6));
 228 
 229         assertTrue(BMPImageLoaderShim.isPow2Minus1(1));
 230         assertTrue(BMPImageLoaderShim.isPow2Minus1(3));
 231         assertTrue(BMPImageLoaderShim.isPow2Minus1(7));
 232         assertFalse(BMPImageLoaderShim.isPow2Minus1(2));
 233         assertFalse(BMPImageLoaderShim.isPow2Minus1(11));
 234     }
 235 }