aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/lowagie/text/pdf/codec/BmpImage.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/lowagie/text/pdf/codec/BmpImage.java')
-rw-r--r--src/main/java/com/lowagie/text/pdf/codec/BmpImage.java1282
1 files changed, 1282 insertions, 0 deletions
diff --git a/src/main/java/com/lowagie/text/pdf/codec/BmpImage.java b/src/main/java/com/lowagie/text/pdf/codec/BmpImage.java
new file mode 100644
index 0000000..f9f80be
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/codec/BmpImage.java
@@ -0,0 +1,1282 @@
+/*
+ * Copyright 2003 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ *
+ *
+ * The original JAI codecs have the following license
+ *
+ * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * -Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * -Redistribution in binary form must reproduct the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
+ * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
+ * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
+ * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
+ * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
+ * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that Software is not designed,licensed or intended for use in
+ * the design, construction, operation or maintenance of any nuclear facility.
+ */
+package com.lowagie.text.pdf.codec;
+
+import com.lowagie.text.pdf.*;
+import com.lowagie.text.*;
+import java.io.*;
+import java.util.HashMap;
+import java.net.URL;
+
+/** Reads a BMP image. All types of BMP can be read.
+ * <p>
+ * It is based in the JAI codec.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class BmpImage {
+
+ // BMP variables
+ private InputStream inputStream;
+ private long bitmapFileSize;
+ private long bitmapOffset;
+ private long compression;
+ private long imageSize;
+ private byte palette[];
+ private int imageType;
+ private int numBands;
+ private boolean isBottomUp;
+ private int bitsPerPixel;
+ private int redMask, greenMask, blueMask, alphaMask;
+ public HashMap properties = new HashMap();
+ private long xPelsPerMeter;
+ private long yPelsPerMeter;
+ // BMP Image types
+ private static final int VERSION_2_1_BIT = 0;
+ private static final int VERSION_2_4_BIT = 1;
+ private static final int VERSION_2_8_BIT = 2;
+ private static final int VERSION_2_24_BIT = 3;
+
+ private static final int VERSION_3_1_BIT = 4;
+ private static final int VERSION_3_4_BIT = 5;
+ private static final int VERSION_3_8_BIT = 6;
+ private static final int VERSION_3_24_BIT = 7;
+
+ private static final int VERSION_3_NT_16_BIT = 8;
+ private static final int VERSION_3_NT_32_BIT = 9;
+
+ private static final int VERSION_4_1_BIT = 10;
+ private static final int VERSION_4_4_BIT = 11;
+ private static final int VERSION_4_8_BIT = 12;
+ private static final int VERSION_4_16_BIT = 13;
+ private static final int VERSION_4_24_BIT = 14;
+ private static final int VERSION_4_32_BIT = 15;
+
+ // Color space types
+ private static final int LCS_CALIBRATED_RGB = 0;
+ private static final int LCS_sRGB = 1;
+ private static final int LCS_CMYK = 2;
+
+ // Compression Types
+ private static final int BI_RGB = 0;
+ private static final int BI_RLE8 = 1;
+ private static final int BI_RLE4 = 2;
+ private static final int BI_BITFIELDS = 3;
+
+ int width;
+ int height;
+
+ BmpImage(InputStream is, boolean noHeader, int size) throws IOException {
+ bitmapFileSize = size;
+ bitmapOffset = 0;
+ process(is, noHeader);
+ }
+
+ /** Reads a BMP from an url.
+ * @param url the url
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(URL url) throws IOException {
+ InputStream is = null;
+ try {
+ is = url.openStream();
+ Image img = getImage(is);
+ img.setUrl(url);
+ return img;
+ }
+ finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ /** Reads a BMP from a stream. The stream is not closed.
+ * @param is the stream
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(InputStream is) throws IOException {
+ return getImage(is, false, 0);
+ }
+
+ /** Reads a BMP from a stream. The stream is not closed.
+ * The BMP may not have a header and be considered as a plain DIB.
+ * @param is the stream
+ * @param noHeader true to process a plain DIB
+ * @param size the size of the DIB. Not used for a BMP
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(InputStream is, boolean noHeader, int size) throws IOException {
+ BmpImage bmp = new BmpImage(is, noHeader, size);
+ try {
+ Image img = bmp.getImage();
+ img.setDpi((int)((double)bmp.xPelsPerMeter * 0.0254), (int)((double)bmp.yPelsPerMeter * 0.0254));
+ img.setOriginalType(Image.ORIGINAL_BMP);
+ return img;
+ }
+ catch (BadElementException be) {
+ throw new ExceptionConverter(be);
+ }
+ }
+
+ /** Reads a BMP from a file.
+ * @param file the file
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(String file) throws IOException {
+ return getImage(Image.toURL(file));
+ }
+
+ /** Reads a BMP from a byte array.
+ * @param data the byte array
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(byte data[]) throws IOException {
+ InputStream is = null;
+ try {
+ is = new ByteArrayInputStream(data);
+ Image img = getImage(is);
+ img.setOriginalData(data);
+ return img;
+ }
+ finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+
+ protected void process(InputStream stream, boolean noHeader) throws IOException {
+ if (noHeader || stream instanceof BufferedInputStream) {
+ inputStream = stream;
+ } else {
+ inputStream = new BufferedInputStream(stream);
+ }
+ if (!noHeader) {
+ // Start File Header
+ if (!(readUnsignedByte(inputStream) == 'B' &&
+ readUnsignedByte(inputStream) == 'M')) {
+ throw new
+ RuntimeException("Invalid magic value for BMP file.");
+ }
+
+ // Read file size
+ bitmapFileSize = readDWord(inputStream);
+
+ // Read the two reserved fields
+ readWord(inputStream);
+ readWord(inputStream);
+
+ // Offset to the bitmap from the beginning
+ bitmapOffset = readDWord(inputStream);
+
+ // End File Header
+ }
+ // Start BitmapCoreHeader
+ long size = readDWord(inputStream);
+
+ if (size == 12) {
+ width = readWord(inputStream);
+ height = readWord(inputStream);
+ } else {
+ width = readLong(inputStream);
+ height = readLong(inputStream);
+ }
+
+ int planes = readWord(inputStream);
+ bitsPerPixel = readWord(inputStream);
+
+ properties.put("color_planes", new Integer(planes));
+ properties.put("bits_per_pixel", new Integer(bitsPerPixel));
+
+ // As BMP always has 3 rgb bands, except for Version 5,
+ // which is bgra
+ numBands = 3;
+ if (bitmapOffset == 0)
+ bitmapOffset = size;
+ if (size == 12) {
+ // Windows 2.x and OS/2 1.x
+ properties.put("bmp_version", "BMP v. 2.x");
+
+ // Classify the image type
+ if (bitsPerPixel == 1) {
+ imageType = VERSION_2_1_BIT;
+ } else if (bitsPerPixel == 4) {
+ imageType = VERSION_2_4_BIT;
+ } else if (bitsPerPixel == 8) {
+ imageType = VERSION_2_8_BIT;
+ } else if (bitsPerPixel == 24) {
+ imageType = VERSION_2_24_BIT;
+ }
+
+ // Read in the palette
+ int numberOfEntries = (int)((bitmapOffset-14-size) / 3);
+ int sizeOfPalette = numberOfEntries*3;
+ if (bitmapOffset == size) {
+ switch (imageType) {
+ case VERSION_2_1_BIT:
+ sizeOfPalette = 2 * 3;
+ break;
+ case VERSION_2_4_BIT:
+ sizeOfPalette = 16 * 3;
+ break;
+ case VERSION_2_8_BIT:
+ sizeOfPalette = 256 * 3;
+ break;
+ case VERSION_2_24_BIT:
+ sizeOfPalette = 0;
+ break;
+ }
+ bitmapOffset = size + sizeOfPalette;
+ }
+ palette = new byte[sizeOfPalette];
+ inputStream.read(palette, 0, sizeOfPalette);
+ properties.put("palette", palette);
+ } else {
+
+ compression = readDWord(inputStream);
+ imageSize = readDWord(inputStream);
+ xPelsPerMeter = readLong(inputStream);
+ yPelsPerMeter = readLong(inputStream);
+ long colorsUsed = readDWord(inputStream);
+ long colorsImportant = readDWord(inputStream);
+
+ switch((int)compression) {
+ case BI_RGB:
+ properties.put("compression", "BI_RGB");
+ break;
+
+ case BI_RLE8:
+ properties.put("compression", "BI_RLE8");
+ break;
+
+ case BI_RLE4:
+ properties.put("compression", "BI_RLE4");
+ break;
+
+ case BI_BITFIELDS:
+ properties.put("compression", "BI_BITFIELDS");
+ break;
+ }
+
+ properties.put("x_pixels_per_meter", new Long(xPelsPerMeter));
+ properties.put("y_pixels_per_meter", new Long(yPelsPerMeter));
+ properties.put("colors_used", new Long(colorsUsed));
+ properties.put("colors_important", new Long(colorsImportant));
+
+ if (size == 40) {
+ // Windows 3.x and Windows NT
+ switch((int)compression) {
+
+ case BI_RGB: // No compression
+ case BI_RLE8: // 8-bit RLE compression
+ case BI_RLE4: // 4-bit RLE compression
+
+ if (bitsPerPixel == 1) {
+ imageType = VERSION_3_1_BIT;
+ } else if (bitsPerPixel == 4) {
+ imageType = VERSION_3_4_BIT;
+ } else if (bitsPerPixel == 8) {
+ imageType = VERSION_3_8_BIT;
+ } else if (bitsPerPixel == 24) {
+ imageType = VERSION_3_24_BIT;
+ } else if (bitsPerPixel == 16) {
+ imageType = VERSION_3_NT_16_BIT;
+ redMask = 0x7C00;
+ greenMask = 0x3E0;
+ blueMask = 0x1F;
+ properties.put("red_mask", new Integer(redMask));
+ properties.put("green_mask", new Integer(greenMask));
+ properties.put("blue_mask", new Integer(blueMask));
+ } else if (bitsPerPixel == 32) {
+ imageType = VERSION_3_NT_32_BIT;
+ redMask = 0x00FF0000;
+ greenMask = 0x0000FF00;
+ blueMask = 0x000000FF;
+ properties.put("red_mask", new Integer(redMask));
+ properties.put("green_mask", new Integer(greenMask));
+ properties.put("blue_mask", new Integer(blueMask));
+ }
+
+ // Read in the palette
+ int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
+ int sizeOfPalette = numberOfEntries*4;
+ if (bitmapOffset == size) {
+ switch (imageType) {
+ case VERSION_3_1_BIT:
+ sizeOfPalette = (int)(colorsUsed == 0 ? 2 : colorsUsed) * 4;
+ break;
+ case VERSION_3_4_BIT:
+ sizeOfPalette = (int)(colorsUsed == 0 ? 16 : colorsUsed) * 4;
+ break;
+ case VERSION_3_8_BIT:
+ sizeOfPalette = (int)(colorsUsed == 0 ? 256 : colorsUsed) * 4;
+ break;
+ default:
+ sizeOfPalette = 0;
+ break;
+ }
+ bitmapOffset = size + sizeOfPalette;
+ }
+ palette = new byte[sizeOfPalette];
+ inputStream.read(palette, 0, sizeOfPalette);
+ properties.put("palette", palette);
+
+ properties.put("bmp_version", "BMP v. 3.x");
+ break;
+
+ case BI_BITFIELDS:
+
+ if (bitsPerPixel == 16) {
+ imageType = VERSION_3_NT_16_BIT;
+ } else if (bitsPerPixel == 32) {
+ imageType = VERSION_3_NT_32_BIT;
+ }
+
+ // BitsField encoding
+ redMask = (int)readDWord(inputStream);
+ greenMask = (int)readDWord(inputStream);
+ blueMask = (int)readDWord(inputStream);
+
+ properties.put("red_mask", new Integer(redMask));
+ properties.put("green_mask", new Integer(greenMask));
+ properties.put("blue_mask", new Integer(blueMask));
+
+ if (colorsUsed != 0) {
+ // there is a palette
+ sizeOfPalette = (int)colorsUsed*4;
+ palette = new byte[sizeOfPalette];
+ inputStream.read(palette, 0, sizeOfPalette);
+ properties.put("palette", palette);
+ }
+
+ properties.put("bmp_version", "BMP v. 3.x NT");
+ break;
+
+ default:
+ throw new
+ RuntimeException("Invalid compression specified in BMP file.");
+ }
+ } else if (size == 108) {
+ // Windows 4.x BMP
+
+ properties.put("bmp_version", "BMP v. 4.x");
+
+ // rgb masks, valid only if comp is BI_BITFIELDS
+ redMask = (int)readDWord(inputStream);
+ greenMask = (int)readDWord(inputStream);
+ blueMask = (int)readDWord(inputStream);
+ // Only supported for 32bpp BI_RGB argb
+ alphaMask = (int)readDWord(inputStream);
+ long csType = readDWord(inputStream);
+ int redX = readLong(inputStream);
+ int redY = readLong(inputStream);
+ int redZ = readLong(inputStream);
+ int greenX = readLong(inputStream);
+ int greenY = readLong(inputStream);
+ int greenZ = readLong(inputStream);
+ int blueX = readLong(inputStream);
+ int blueY = readLong(inputStream);
+ int blueZ = readLong(inputStream);
+ long gammaRed = readDWord(inputStream);
+ long gammaGreen = readDWord(inputStream);
+ long gammaBlue = readDWord(inputStream);
+
+ if (bitsPerPixel == 1) {
+ imageType = VERSION_4_1_BIT;
+ } else if (bitsPerPixel == 4) {
+ imageType = VERSION_4_4_BIT;
+ } else if (bitsPerPixel == 8) {
+ imageType = VERSION_4_8_BIT;
+ } else if (bitsPerPixel == 16) {
+ imageType = VERSION_4_16_BIT;
+ if ((int)compression == BI_RGB) {
+ redMask = 0x7C00;
+ greenMask = 0x3E0;
+ blueMask = 0x1F;
+ }
+ } else if (bitsPerPixel == 24) {
+ imageType = VERSION_4_24_BIT;
+ } else if (bitsPerPixel == 32) {
+ imageType = VERSION_4_32_BIT;
+ if ((int)compression == BI_RGB) {
+ redMask = 0x00FF0000;
+ greenMask = 0x0000FF00;
+ blueMask = 0x000000FF;
+ }
+ }
+
+ properties.put("red_mask", new Integer(redMask));
+ properties.put("green_mask", new Integer(greenMask));
+ properties.put("blue_mask", new Integer(blueMask));
+ properties.put("alpha_mask", new Integer(alphaMask));
+
+ // Read in the palette
+ int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
+ int sizeOfPalette = numberOfEntries*4;
+ if (bitmapOffset == size) {
+ switch (imageType) {
+ case VERSION_4_1_BIT:
+ sizeOfPalette = (int)(colorsUsed == 0 ? 2 : colorsUsed) * 4;
+ break;
+ case VERSION_4_4_BIT:
+ sizeOfPalette = (int)(colorsUsed == 0 ? 16 : colorsUsed) * 4;
+ break;
+ case VERSION_4_8_BIT:
+ sizeOfPalette = (int)(colorsUsed == 0 ? 256 : colorsUsed) * 4;
+ break;
+ default:
+ sizeOfPalette = 0;
+ break;
+ }
+ bitmapOffset = size + sizeOfPalette;
+ }
+ palette = new byte[sizeOfPalette];
+ inputStream.read(palette, 0, sizeOfPalette);
+
+ if (palette != null || palette.length != 0) {
+ properties.put("palette", palette);
+ }
+
+ switch((int)csType) {
+ case LCS_CALIBRATED_RGB:
+ // All the new fields are valid only for this case
+ properties.put("color_space", "LCS_CALIBRATED_RGB");
+ properties.put("redX", new Integer(redX));
+ properties.put("redY", new Integer(redY));
+ properties.put("redZ", new Integer(redZ));
+ properties.put("greenX", new Integer(greenX));
+ properties.put("greenY", new Integer(greenY));
+ properties.put("greenZ", new Integer(greenZ));
+ properties.put("blueX", new Integer(blueX));
+ properties.put("blueY", new Integer(blueY));
+ properties.put("blueZ", new Integer(blueZ));
+ properties.put("gamma_red", new Long(gammaRed));
+ properties.put("gamma_green", new Long(gammaGreen));
+ properties.put("gamma_blue", new Long(gammaBlue));
+
+ // break;
+ throw new
+ RuntimeException("Not implemented yet.");
+
+ case LCS_sRGB:
+ // Default Windows color space
+ properties.put("color_space", "LCS_sRGB");
+ break;
+
+ case LCS_CMYK:
+ properties.put("color_space", "LCS_CMYK");
+ // break;
+ throw new
+ RuntimeException("Not implemented yet.");
+ }
+
+ } else {
+ properties.put("bmp_version", "BMP v. 5.x");
+ throw new
+ RuntimeException("BMP version 5 not implemented yet.");
+ }
+ }
+
+ if (height > 0) {
+ // bottom up image
+ isBottomUp = true;
+ } else {
+ // top down image
+ isBottomUp = false;
+ height = Math.abs(height);
+ }
+ // When number of bitsPerPixel is <= 8, we use IndexColorModel.
+ if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {
+
+ numBands = 1;
+
+
+ // Create IndexColorModel from the palette.
+ byte r[], g[], b[];
+ int sizep;
+ if (imageType == VERSION_2_1_BIT ||
+ imageType == VERSION_2_4_BIT ||
+ imageType == VERSION_2_8_BIT) {
+
+ sizep = palette.length/3;
+
+ if (sizep > 256) {
+ sizep = 256;
+ }
+
+ int off;
+ r = new byte[sizep];
+ g = new byte[sizep];
+ b = new byte[sizep];
+ for (int i=0; i<sizep; i++) {
+ off = 3 * i;
+ b[i] = palette[off];
+ g[i] = palette[off+1];
+ r[i] = palette[off+2];
+ }
+ } else {
+ sizep = palette.length/4;
+
+ if (sizep > 256) {
+ sizep = 256;
+ }
+
+ int off;
+ r = new byte[sizep];
+ g = new byte[sizep];
+ b = new byte[sizep];
+ for (int i=0; i<sizep; i++) {
+ off = 4 * i;
+ b[i] = palette[off];
+ g[i] = palette[off+1];
+ r[i] = palette[off+2];
+ }
+ }
+
+ } else if (bitsPerPixel == 16) {
+ numBands = 3;
+ } else if (bitsPerPixel == 32) {
+ numBands = alphaMask == 0 ? 3 : 4;
+
+ // The number of bands in the SampleModel is determined by
+ // the length of the mask array passed in.
+ } else {
+ numBands = 3;
+ }
+ }
+
+ private byte[] getPalette(int group) {
+ if (palette == null)
+ return null;
+ byte np[] = new byte[palette.length / group * 3];
+ int e = palette.length / group;
+ for (int k = 0; k < e; ++k) {
+ int src = k * group;
+ int dest = k * 3;
+ np[dest + 2] = palette[src++];
+ np[dest + 1] = palette[src++];
+ np[dest] = palette[src];
+ }
+ return np;
+ }
+
+ private Image getImage() throws IOException, BadElementException {
+ byte bdata[] = null; // buffer for byte data
+
+ // if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
+ // bdata = (byte[])((DataBufferByte)tile.getDataBuffer()).getData();
+ // else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
+ // sdata = (short[])((DataBufferUShort)tile.getDataBuffer()).getData();
+ // else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
+ // idata = (int[])((DataBufferInt)tile.getDataBuffer()).getData();
+
+ // There should only be one tile.
+ switch(imageType) {
+
+ case VERSION_2_1_BIT:
+ // no compression
+ return read1Bit(3);
+
+ case VERSION_2_4_BIT:
+ // no compression
+ return read4Bit(3);
+
+ case VERSION_2_8_BIT:
+ // no compression
+ return read8Bit(3);
+
+ case VERSION_2_24_BIT:
+ // no compression
+ bdata = new byte[width * height * 3];
+ read24Bit(bdata);
+ return new ImgRaw(width, height, 3, 8, bdata);
+
+ case VERSION_3_1_BIT:
+ // 1-bit images cannot be compressed.
+ return read1Bit(4);
+
+ case VERSION_3_4_BIT:
+ switch((int)compression) {
+ case BI_RGB:
+ return read4Bit(4);
+
+ case BI_RLE4:
+ return readRLE4();
+
+ default:
+ throw new
+ RuntimeException("Invalid compression specified for BMP file.");
+ }
+
+ case VERSION_3_8_BIT:
+ switch((int)compression) {
+ case BI_RGB:
+ return read8Bit(4);
+
+ case BI_RLE8:
+ return readRLE8();
+
+ default:
+ throw new
+ RuntimeException("Invalid compression specified for BMP file.");
+ }
+
+ case VERSION_3_24_BIT:
+ // 24-bit images are not compressed
+ bdata = new byte[width * height * 3];
+ read24Bit(bdata);
+ return new ImgRaw(width, height, 3, 8, bdata);
+
+ case VERSION_3_NT_16_BIT:
+ return read1632Bit(false);
+
+ case VERSION_3_NT_32_BIT:
+ return read1632Bit(true);
+
+ case VERSION_4_1_BIT:
+ return read1Bit(4);
+
+ case VERSION_4_4_BIT:
+ switch((int)compression) {
+
+ case BI_RGB:
+ return read4Bit(4);
+
+ case BI_RLE4:
+ return readRLE4();
+
+ default:
+ throw new
+ RuntimeException("Invalid compression specified for BMP file.");
+ }
+
+ case VERSION_4_8_BIT:
+ switch((int)compression) {
+
+ case BI_RGB:
+ return read8Bit(4);
+
+ case BI_RLE8:
+ return readRLE8();
+
+ default:
+ throw new
+ RuntimeException("Invalid compression specified for BMP file.");
+ }
+
+ case VERSION_4_16_BIT:
+ return read1632Bit(false);
+
+ case VERSION_4_24_BIT:
+ bdata = new byte[width * height * 3];
+ read24Bit(bdata);
+ return new ImgRaw(width, height, 3, 8, bdata);
+
+ case VERSION_4_32_BIT:
+ return read1632Bit(true);
+ }
+ return null;
+ }
+
+ private Image indexedModel(byte bdata[], int bpc, int paletteEntries) throws BadElementException {
+ Image img = new ImgRaw(width, height, 1, bpc, bdata);
+ PdfArray colorspace = new PdfArray();
+ colorspace.add(PdfName.INDEXED);
+ colorspace.add(PdfName.DEVICERGB);
+ byte np[] = getPalette(paletteEntries);
+ int len = np.length;
+ colorspace.add(new PdfNumber(len / 3 - 1));
+ colorspace.add(new PdfString(np));
+ PdfDictionary ad = new PdfDictionary();
+ ad.put(PdfName.COLORSPACE, colorspace);
+ img.setAdditional(ad);
+ return img;
+ }
+
+ // Deal with 1 Bit images using IndexColorModels
+ private Image read1Bit(int paletteEntries) throws IOException, BadElementException {
+ byte bdata[] = new byte[((width + 7) / 8) * height];
+ int padding = 0;
+ int bytesPerScanline = (int)Math.ceil((double)width/8.0);
+
+ int remainder = bytesPerScanline % 4;
+ if (remainder != 0) {
+ padding = 4 - remainder;
+ }
+
+ int imSize = (bytesPerScanline + padding) * height;
+
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ int bytesRead = 0;
+ while (bytesRead < imSize) {
+ bytesRead += inputStream.read(values, bytesRead,
+ imSize - bytesRead);
+ }
+
+ if (isBottomUp) {
+
+ // Convert the bottom up image to a top down format by copying
+ // one scanline from the bottom to the top at a time.
+
+ for (int i=0; i<height; i++) {
+ System.arraycopy(values,
+ imSize - (i+1)*(bytesPerScanline + padding),
+ bdata,
+ i*bytesPerScanline, bytesPerScanline);
+ }
+ } else {
+
+ for (int i=0; i<height; i++) {
+ System.arraycopy(values,
+ i * (bytesPerScanline + padding),
+ bdata,
+ i * bytesPerScanline,
+ bytesPerScanline);
+ }
+ }
+ return indexedModel(bdata, 1, paletteEntries);
+ }
+
+ // Method to read a 4 bit BMP image data
+ private Image read4Bit(int paletteEntries) throws IOException, BadElementException {
+ byte bdata[] = new byte[((width + 1) / 2) * height];
+
+ // Padding bytes at the end of each scanline
+ int padding = 0;
+
+ int bytesPerScanline = (int)Math.ceil((double)width/2.0);
+ int remainder = bytesPerScanline % 4;
+ if (remainder != 0) {
+ padding = 4 - remainder;
+ }
+
+ int imSize = (bytesPerScanline + padding) * height;
+
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ int bytesRead = 0;
+ while (bytesRead < imSize) {
+ bytesRead += inputStream.read(values, bytesRead,
+ imSize - bytesRead);
+ }
+
+ if (isBottomUp) {
+
+ // Convert the bottom up image to a top down format by copying
+ // one scanline from the bottom to the top at a time.
+ for (int i=0; i<height; i++) {
+ System.arraycopy(values,
+ imSize - (i+1)*(bytesPerScanline + padding),
+ bdata,
+ i*bytesPerScanline,
+ bytesPerScanline);
+ }
+ } else {
+ for (int i=0; i<height; i++) {
+ System.arraycopy(values,
+ i * (bytesPerScanline + padding),
+ bdata,
+ i * bytesPerScanline,
+ bytesPerScanline);
+ }
+ }
+ return indexedModel(bdata, 4, paletteEntries);
+ }
+
+ // Method to read 8 bit BMP image data
+ private Image read8Bit(int paletteEntries) throws IOException, BadElementException {
+ byte bdata[] = new byte[width * height];
+ // Padding bytes at the end of each scanline
+ int padding = 0;
+
+ // width * bitsPerPixel should be divisible by 32
+ int bitsPerScanline = width * 8;
+ if ( bitsPerScanline%32 != 0) {
+ padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline;
+ padding = (int)Math.ceil(padding/8.0);
+ }
+
+ int imSize = (width + padding) * height;
+
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ int bytesRead = 0;
+ while (bytesRead < imSize) {
+ bytesRead += inputStream.read(values, bytesRead, imSize - bytesRead);
+ }
+
+ if (isBottomUp) {
+
+ // Convert the bottom up image to a top down format by copying
+ // one scanline from the bottom to the top at a time.
+ for (int i=0; i<height; i++) {
+ System.arraycopy(values,
+ imSize - (i+1) * (width + padding),
+ bdata,
+ i * width,
+ width);
+ }
+ } else {
+ for (int i=0; i<height; i++) {
+ System.arraycopy(values,
+ i * (width + padding),
+ bdata,
+ i * width,
+ width);
+ }
+ }
+ return indexedModel(bdata, 8, paletteEntries);
+ }
+
+ // Method to read 24 bit BMP image data
+ private void read24Bit(byte[] bdata) {
+ // Padding bytes at the end of each scanline
+ int padding = 0;
+
+ // width * bitsPerPixel should be divisible by 32
+ int bitsPerScanline = width * 24;
+ if ( bitsPerScanline%32 != 0) {
+ padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline;
+ padding = (int)Math.ceil(padding/8.0);
+ }
+
+
+ int imSize = ((width * 3 + 3) / 4 * 4) * height;
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ try {
+ int bytesRead = 0;
+ while (bytesRead < imSize) {
+ int r = inputStream.read(values, bytesRead,
+ imSize - bytesRead);
+ if (r < 0)
+ break;
+ bytesRead += r;
+ }
+ } catch (IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+
+ int l=0, count;
+
+ if (isBottomUp) {
+ int max = width*height*3-1;
+
+ count = -padding;
+ for (int i=0; i<height; i++) {
+ l = max - (i+1)*width*3 + 1;
+ count += padding;
+ for (int j=0; j<width; j++) {
+ bdata[l + 2] = values[count++];
+ bdata[l + 1] = values[count++];
+ bdata[l] = values[count++];
+ l += 3;
+ }
+ }
+ } else {
+ count = -padding;
+ for (int i=0; i<height; i++) {
+ count += padding;
+ for (int j=0; j<width; j++) {
+ bdata[l + 2] = values[count++];
+ bdata[l + 1] = values[count++];
+ bdata[l] = values[count++];
+ l += 3;
+ }
+ }
+ }
+ }
+
+ private int findMask(int mask) {
+ int k = 0;
+ for (; k < 32; ++k) {
+ if ((mask & 1) == 1)
+ break;
+ mask >>>= 1;
+ }
+ return mask;
+ }
+
+ private int findShift(int mask) {
+ int k = 0;
+ for (; k < 32; ++k) {
+ if ((mask & 1) == 1)
+ break;
+ mask >>>= 1;
+ }
+ return k;
+ }
+
+ private Image read1632Bit(boolean is32) throws IOException, BadElementException {
+
+ int red_mask = findMask(redMask);
+ int red_shift = findShift(redMask);
+ int red_factor = red_mask + 1;
+ int green_mask = findMask(greenMask);
+ int green_shift = findShift(greenMask);
+ int green_factor = green_mask + 1;
+ int blue_mask = findMask(blueMask);
+ int blue_shift = findShift(blueMask);
+ int blue_factor = blue_mask + 1;
+ byte bdata[] = new byte[width * height * 3];
+ // Padding bytes at the end of each scanline
+ int padding = 0;
+
+ if (!is32) {
+ // width * bitsPerPixel should be divisible by 32
+ int bitsPerScanline = width * 16;
+ if ( bitsPerScanline%32 != 0) {
+ padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline;
+ padding = (int)Math.ceil(padding/8.0);
+ }
+ }
+
+ int imSize = (int)imageSize;
+ if (imSize == 0) {
+ imSize = (int)(bitmapFileSize - bitmapOffset);
+ }
+
+ int l=0;
+ int v;
+ if (isBottomUp) {
+ for (int i=height - 1; i >= 0; --i) {
+ l = width * 3 * i;
+ for (int j=0; j<width; j++) {
+ if (is32)
+ v = (int)readDWord(inputStream);
+ else
+ v = readWord(inputStream);
+ bdata[l++] = (byte)(((v >>> red_shift) & red_mask) * 256 / red_factor);
+ bdata[l++] = (byte)(((v >>> green_shift) & green_mask) * 256 / green_factor);
+ bdata[l++] = (byte)(((v >>> blue_shift) & blue_mask) * 256 / blue_factor);
+ }
+ for (int m=0; m<padding; m++) {
+ inputStream.read();
+ }
+ }
+ } else {
+ for (int i=0; i<height; i++) {
+ for (int j=0; j<width; j++) {
+ if (is32)
+ v = (int)readDWord(inputStream);
+ else
+ v = readWord(inputStream);
+ bdata[l++] = (byte)(((v >>> red_shift) & red_mask) * 256 / red_factor);
+ bdata[l++] = (byte)(((v >>> green_shift) & green_mask) * 256 / green_factor);
+ bdata[l++] = (byte)(((v >>> blue_shift) & blue_mask) * 256 / blue_factor);
+ }
+ for (int m=0; m<padding; m++) {
+ inputStream.read();
+ }
+ }
+ }
+ return new ImgRaw(width, height, 3, 8, bdata);
+ }
+
+ private Image readRLE8() throws IOException, BadElementException {
+
+ // If imageSize field is not provided, calculate it.
+ int imSize = (int)imageSize;
+ if (imSize == 0) {
+ imSize = (int)(bitmapFileSize - bitmapOffset);
+ }
+
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ int bytesRead = 0;
+ while (bytesRead < imSize) {
+ bytesRead += inputStream.read(values, bytesRead,
+ imSize - bytesRead);
+ }
+
+ // Since data is compressed, decompress it
+ byte val[] = decodeRLE(true, values);
+
+ // Uncompressed data does not have any padding
+ imSize = width * height;
+
+ if (isBottomUp) {
+
+ // Convert the bottom up image to a top down format by copying
+ // one scanline from the bottom to the top at a time.
+ // int bytesPerScanline = (int)Math.ceil((double)width/8.0);
+ byte temp[] = new byte[val.length];
+ int bytesPerScanline = width;
+ for (int i=0; i<height; i++) {
+ System.arraycopy(val,
+ imSize - (i+1)*(bytesPerScanline),
+ temp,
+ i*bytesPerScanline, bytesPerScanline);
+ }
+ val = temp;
+ }
+ return indexedModel(val, 8, 4);
+ }
+
+ private Image readRLE4() throws IOException, BadElementException {
+
+ // If imageSize field is not specified, calculate it.
+ int imSize = (int)imageSize;
+ if (imSize == 0) {
+ imSize = (int)(bitmapFileSize - bitmapOffset);
+ }
+
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ int bytesRead = 0;
+ while (bytesRead < imSize) {
+ bytesRead += inputStream.read(values, bytesRead,
+ imSize - bytesRead);
+ }
+
+ // Decompress the RLE4 compressed data.
+ byte val[] = decodeRLE(false, values);
+
+ // Invert it as it is bottom up format.
+ if (isBottomUp) {
+
+ byte inverted[] = val;
+ val = new byte[width * height];
+ int l = 0, index, lineEnd;
+
+ for (int i = height-1; i >= 0; i--) {
+ index = i * width;
+ lineEnd = l + width;
+ while(l != lineEnd) {
+ val[l++] = inverted[index++];
+ }
+ }
+ }
+ int stride = ((width + 1) / 2);
+ byte bdata[] = new byte[stride * height];
+ int ptr = 0;
+ int sh = 0;
+ for (int h = 0; h < height; ++h) {
+ for (int w = 0; w < width; ++w) {
+ if ((w & 1) == 0)
+ bdata[sh + w / 2] = (byte)(val[ptr++] << 4);
+ else
+ bdata[sh + w / 2] |= (byte)(val[ptr++] & 0x0f);
+ }
+ sh += stride;
+ }
+ return indexedModel(bdata, 4, 4);
+ }
+
+ private byte[] decodeRLE(boolean is8, byte values[]) {
+ byte val[] = new byte[width * height];
+ try {
+ int ptr = 0;
+ int x = 0;
+ int q = 0;
+ for (int y = 0; y < height && ptr < values.length;) {
+ int count = values[ptr++] & 0xff;
+ if (count != 0) {
+ // encoded mode
+ int bt = values[ptr++] & 0xff;
+ if (is8) {
+ for (int i = count; i != 0; --i) {
+ val[q++] = (byte)bt;
+ }
+ }
+ else {
+ for (int i = 0; i < count; ++i) {
+ val[q++] = (byte)((i & 1) == 1 ? (bt & 0x0f) : ((bt >>> 4) & 0x0f));
+ }
+ }
+ x += count;
+ }
+ else {
+ // escape mode
+ count = values[ptr++] & 0xff;
+ if (count == 1)
+ break;
+ switch (count) {
+ case 0:
+ x = 0;
+ ++y;
+ q = y * width;
+ break;
+ case 2:
+ // delta mode
+ x += values[ptr++] & 0xff;
+ y += values[ptr++] & 0xff;
+ q = y * width + x;
+ break;
+ default:
+ // absolute mode
+ if (is8) {
+ for (int i = count; i != 0; --i)
+ val[q++] = (byte)(values[ptr++] & 0xff);
+ }
+ else {
+ int bt = 0;
+ for (int i = 0; i < count; ++i) {
+ if ((i & 1) == 0)
+ bt = values[ptr++] & 0xff;
+ val[q++] = (byte)((i & 1) == 1 ? (bt & 0x0f) : ((bt >>> 4) & 0x0f));
+ }
+ }
+ x += count;
+ // read pad byte
+ if (is8) {
+ if ((count & 1) == 1)
+ ++ptr;
+ }
+ else {
+ if ((count & 3) == 1 || (count & 3) == 2)
+ ++ptr;
+ }
+ break;
+ }
+ }
+ }
+ }
+ catch (Exception e) {
+ //empty on purpose
+ }
+
+ return val;
+ }
+
+ // Windows defined data type reading methods - everything is little endian
+
+ // Unsigned 8 bits
+ private int readUnsignedByte(InputStream stream) throws IOException {
+ return (stream.read() & 0xff);
+ }
+
+ // Unsigned 2 bytes
+ private int readUnsignedShort(InputStream stream) throws IOException {
+ int b1 = readUnsignedByte(stream);
+ int b2 = readUnsignedByte(stream);
+ return ((b2 << 8) | b1) & 0xffff;
+ }
+
+ // Signed 16 bits
+ private int readShort(InputStream stream) throws IOException {
+ int b1 = readUnsignedByte(stream);
+ int b2 = readUnsignedByte(stream);
+ return (b2 << 8) | b1;
+ }
+
+ // Unsigned 16 bits
+ private int readWord(InputStream stream) throws IOException {
+ return readUnsignedShort(stream);
+ }
+
+ // Unsigned 4 bytes
+ private long readUnsignedInt(InputStream stream) throws IOException {
+ int b1 = readUnsignedByte(stream);
+ int b2 = readUnsignedByte(stream);
+ int b3 = readUnsignedByte(stream);
+ int b4 = readUnsignedByte(stream);
+ long l = (long)((b4 << 24) | (b3 << 16) | (b2 << 8) | b1);
+ return l & 0xffffffff;
+ }
+
+ // Signed 4 bytes
+ private int readInt(InputStream stream) throws IOException {
+ int b1 = readUnsignedByte(stream);
+ int b2 = readUnsignedByte(stream);
+ int b3 = readUnsignedByte(stream);
+ int b4 = readUnsignedByte(stream);
+ return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
+ }
+
+ // Unsigned 4 bytes
+ private long readDWord(InputStream stream) throws IOException {
+ return readUnsignedInt(stream);
+ }
+
+ // 32 bit signed value
+ private int readLong(InputStream stream) throws IOException {
+ return readInt(stream);
+ }
+}