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.Color;
  31 import java.awt.GradientPaint;
  32 import java.awt.Graphics2D;
  33 import java.awt.image.BufferedImage;
  34 import java.awt.image.DataBuffer;
  35 import java.awt.image.IndexColorModel;
  36 import java.io.ByteArrayInputStream;
  37 import java.io.ByteArrayOutputStream;
  38 import java.io.File;
  39 import java.io.FileInputStream;
  40 import java.io.IOException;
  41 import java.io.InputStream;
  42 import java.util.Iterator;
  43 import java.util.Random;
  44 import javax.imageio.IIOImage;
  45 import javax.imageio.ImageIO;
  46 import javax.imageio.ImageWriteParam;
  47 import javax.imageio.ImageWriter;
  48 import javax.imageio.stream.ImageOutputStream;
  49 import javax.imageio.stream.MemoryCacheImageInputStream;
  50 import static org.junit.Assert.*;
  51 import org.junit.Test;
  52 
  53 public class BMPImageLoaderTest {
  54     static final boolean writeFiles = false;
  55     static final int testWidth = 509, testHeight = 157;
  56 
  57     ByteArrayInputStream constructStream(int[] bytes) {
  58         ByteArrayOutputStream baos = new ByteArrayOutputStream();
  59         for (int b : bytes) {
  60             baos.write(b);
  61         }
  62         return new ByteArrayInputStream(baos.toByteArray());
  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 (p1 != p2) {
  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) {
  87         ImageLoaderFactory loaderFactory = BMPImageLoaderFactory.getInstance();
  88         ImageLoader loader = null;
  89         try {
  90             loader = loaderFactory.createImageLoader(stream);
  91         } catch (IOException ioEx) {
  92             fail("unexpected IOException: " + ioEx);
  93         }
  94         assertNotNull(loader);
  95 
  96         try {
  97             ImageFrame frame = loader.load(0, 0, 0, true, true);
  98             return Image.convertImageFrame(frame);
  99         } catch (IOException e) {
 100             fail("unexpected IOException: " + e);
 101         }
 102         return null;
 103     }
 104 
 105     BufferedImage create4BitImage() {
 106         int[] cmap = new int[16];
 107         int i = 0;
 108         for (int r = 0; r < 2; r++) {
 109             for (int g = 0; g < 2; g++) {
 110                 for (int b = 0; b < 2; b++) {
 111                     cmap[i++] = 0xff << 24 | r * 255 << 16 | g * 255 << 8 | b * 255;
 112                     if ((r | g | b) == 0) {
 113                         cmap[i++] = 0xffc0c0c0;
 114                     } else {
 115                         cmap[i++] = 0xff << 24 | r * 128 << 16 | g * 128 << 8 | b * 128;
 116                     }
 117                 }
 118             }
 119         }
 120         IndexColorModel cm = new IndexColorModel(4, 16, cmap, 0, false, -1, DataBuffer.TYPE_BYTE);
 121         return new BufferedImage(testWidth, testHeight, BufferedImage.TYPE_BYTE_BINARY, cm);
 122     }
 123 
 124     BufferedImage createImage(int type) {
 125         return new BufferedImage(testWidth, testHeight, type);
 126     }
 127 
 128     void writeImage(BufferedImage bImg, Object out, String compression) {
 129         try {
 130             ImageOutputStream ios = ImageIO.createImageOutputStream(out);
 131             Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("bmp");
 132             ImageWriter writer = iter.next();
 133             ImageWriteParam iwp = writer.getDefaultWriteParam();
 134             if (compression != null) {
 135                 iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
 136                 iwp.setCompressionType(compression);
 137             }
 138             writer.setOutput(ios);
 139             writer.write(null, new IIOImage(bImg, null, null), iwp);
 140         } catch (IOException e) {
 141             fail("unexpected IOException: " + e);
 142         }
 143     }
 144 
 145     Image getImage(BufferedImage bImg, String compression) {
 146         ByteArrayOutputStream out = new ByteArrayOutputStream();
 147         writeImage(bImg, out, compression);
 148         return loadImage(new ByteArrayInputStream(out.toByteArray()));
 149     }
 150 
 151     void testImageType(int type, String fileName, String compression) {
 152         BufferedImage bImg = createImage(type);
 153         testImage(bImg, fileName, compression);
 154     }
 155 
 156     void testImageType(int type, String fileName) {
 157         BufferedImage bImg = createImage(type);
 158         testImage(bImg, fileName, null);
 159     }
 160 
 161     void drawImageGradient(BufferedImage bImg) {
 162         Graphics2D graphics = bImg.createGraphics();
 163         GradientPaint g = new GradientPaint(0, 0, Color.RED, testWidth, testHeight, Color.GREEN);
 164         graphics.setPaint(g);
 165         graphics.fillRect(0, 0, testWidth, testHeight);
 166     }
 167 
 168     void drawImageRandom(BufferedImage bImg) {
 169         int h = bImg.getHeight(), w = bImg.getWidth();
 170         Random r = new Random(1);
 171         for (int y = 0; y < h; y++) {
 172             for (int x = 0; x < w; x++) {
 173                 bImg.setRGB(x, y, r.nextInt(1 << 24));
 174             }
 175         }
 176     }
 177 
 178     void drawImageHue(BufferedImage bImg) {
 179         int h = bImg.getHeight(), w = bImg.getWidth();
 180         for (int y = 0; y < h; y++) {
 181             float s = 2.0f * y / h;
 182             if (s > 1) {
 183                 s = 1;
 184             }
 185             float b = 2.0f * (h - y) / h;
 186             if (b > 1) {
 187                 b = 1;
 188             }
 189             for (int x = 0; x < w; x++) {
 190                 float hue = (float) x / w;
 191                 bImg.setRGB(x, y, Color.HSBtoRGB(hue, s, b));
 192             }
 193         }
 194     }
 195 
 196     void drawImageAll(BufferedImage bImg) {
 197         int h = bImg.getHeight(), w = bImg.getWidth();
 198         //if (h*w < (1<<24)) return;
 199         for (int y = 0; y < h; y++) {
 200             for (int x = 0; x < w; x++) {
 201                 bImg.setRGB(x, y, y * h + x);
 202             }
 203         }
 204     }
 205 
 206     void testImage(BufferedImage bImg, String fileName, String compression) {
 207         //drawImageHue(bImg);
 208         //drawImageAll(bImg);
 209         drawImageRandom(bImg);
 210         if (writeFiles && fileName != null) {
 211             File file = new File(fileName);
 212             file.delete();
 213             writeImage(bImg, file, compression);
 214         }
 215         Image image = getImage(bImg, compression);
 216         compare(image, bImg);
 217     }
 218 
 219     @Test
 220     public void testRT32213()  {
 221         final int[] bytes = {
 222             0x42, 0x4d, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
 223             0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x28, 0x00,
 224             0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
 225             0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
 226             0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
 227             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 228             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 229             0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x80, 0x00,
 230             0x00, 0x00
 231         };
 232 
 233         ByteArrayInputStream stream = constructStream(bytes);
 234         Image image = loadImage(stream);
 235         stream.reset();
 236         try {
 237             BufferedImage bImg = ImageIO.read(new MemoryCacheImageInputStream(stream));
 238             compare(image, bImg);
 239         } catch (IOException e) {
 240             fail("unexpected IOException: " + e);
 241         }
 242     }
 243 
 244     @Test
 245     public void test1Bit() {
 246         testImageType(BufferedImage.TYPE_BYTE_BINARY, "out1bit.bmp");
 247     }
 248 
 249     @Test
 250     public void test4Bit() {
 251         testImage(create4BitImage(), "out4bit.bmp", null);
 252     }
 253 
 254     //@Test
 255     public void test4BitRLE() {
 256         testImage(create4BitImage(), "out4bitRLE.bmp", "BI_RLE4");
 257     }
 258 
 259     @Test
 260     public void test8Bit() {
 261         testImageType(BufferedImage.TYPE_BYTE_INDEXED, "out8bit.bmp");
 262     }
 263 
 264     @Test
 265     public void test8BitRLE() {
 266         testImageType(BufferedImage.TYPE_BYTE_INDEXED, "out8bitRLE.bmp", "BI_RLE8");
 267     }
 268 
 269     @Test
 270     public void test16Bit() {
 271         testImageType(BufferedImage.TYPE_USHORT_555_RGB, "out16bit.bmp");
 272     }
 273 
 274     @Test
 275     public void test24Bit() {
 276         testImageType(BufferedImage.TYPE_INT_RGB, "out24bit.bmp");
 277     }
 278 
 279     void testFile(String fileName, String outFileName, String compression) {
 280         try {
 281             Image image = loadImage(new FileInputStream(fileName));
 282             BufferedImage bImg = ImageIO.read(new File(fileName));
 283             if (writeFiles && outFileName != null) {
 284                 File outFile = new File(outFileName);
 285                 outFile.delete();
 286                 writeImage(bImg, outFile, compression);
 287             }
 288             compare(image, bImg);
 289         } catch (IOException e) {
 290             fail("unexpected IOException: " + e);
 291         }
 292     }
 293 
 294     //@Test
 295     public void testFiles() {
 296         testFile("pal4rle.bmp", "pal4rleOut.bmp", "BI_RLE4");
 297         testFile("out4bitRLE.bmp", "out4bitRLEOut.bmp", "BI_RLE4");
 298         testFile("pal8rletrns.bmp", "pal8rletrnsOut.bmp", null);
 299     }
 300 }