You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by da...@apache.org on 2011/06/21 20:51:28 UTC

svn commit: r1138134 - in /commons/proper/sanselan/trunk/src: main/java/org/apache/sanselan/formats/bmp/ main/java/org/apache/sanselan/formats/bmp/pixelparsers/ test/data/images/bmp/3/

Author: damjan
Date: Tue Jun 21 18:51:28 2011
New Revision: 1138134

URL: http://svn.apache.org/viewvc?rev=1138134&view=rev
Log:
Parse the BMP version 2, 3, 4 and 5 headers.
Decouple reading BI_BITFIELDS masks from reading the palette,
and add a comment explaining why the palette can be present
even for images that don't need it. In the case of
version >= 3 BMP files with BI_BITFIELDS compression,
populate the alpha channel, and add a test case for this.


Added:
    commons/proper/sanselan/trunk/src/test/data/images/bmp/3/
    commons/proper/sanselan/trunk/src/test/data/images/bmp/3/V4-bitfields.bmp   (with props)
    commons/proper/sanselan/trunk/src/test/data/images/bmp/3/info.txt
Modified:
    commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpHeaderInfo.java
    commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpImageParser.java
    commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/pixelparsers/PixelParserBitFields.java

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpHeaderInfo.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpHeaderInfo.java?rev=1138134&r1=1138133&r2=1138134&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpHeaderInfo.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpHeaderInfo.java Tue Jun 21 18:51:28 2011
@@ -43,12 +43,43 @@ public class BmpHeaderInfo
     public final int colorsUsed;
     public final int colorsImportant;
 
+    public final int redMask;
+    public final int greenMask;
+    public final int blueMask;
+    public final int alphaMask;
+
+    public final int colorSpaceType;
+    public final ColorSpace colorSpace;
+    public final int gammaRed;
+    public final int gammaGreen;
+    public final int gammaBlue;
+    public final int intent;
+    public final int profileData;
+    public final int profileSize;
+    public final int reservedV5;
+
+    public static class ColorSpaceCoordinate
+    {
+        public int x, y, z;
+    }
+
+    public static class ColorSpace
+    {
+        public ColorSpaceCoordinate red;
+        public ColorSpaceCoordinate green;
+        public ColorSpaceCoordinate blue;
+    }
+
     public BmpHeaderInfo(byte identifier1, byte identifier2, int fileSize,
             int reserved, int bitmapDataOffset,
             int bitmapHeaderSize, int width, int height, int planes,
             int bitsPerPixel, int compression, int bitmapDataSize,
             int hResolution, int vResolution, int colorsUsed,
-            int colorsImportant)
+            int colorsImportant, int redMask, int greenMask,
+            int blueMask, int alphaMask, int colorSpaceType,
+            ColorSpace colorSpace, int gammaRed, int gammaGreen,
+            int gammaBlue, int intent, int profileData,
+            int profileSize, int reservedV5)
     {
         this.identifier1 = identifier1;
         this.identifier2 = identifier2;
@@ -67,6 +98,20 @@ public class BmpHeaderInfo
         this.vResolution = vResolution;
         this.colorsUsed = colorsUsed;
         this.colorsImportant = colorsImportant;
+
+        this.redMask = redMask;
+        this.greenMask = greenMask;
+        this.blueMask = blueMask;
+        this.alphaMask = alphaMask;
+        this.colorSpaceType = colorSpaceType;
+        this.colorSpace = colorSpace;
+        this.gammaRed = gammaRed;
+        this.gammaGreen = gammaGreen;
+        this.gammaBlue = gammaBlue;
+        this.intent = intent;
+        this.profileData = profileData;
+        this.profileSize = profileSize;
+        this.reservedV5 = reservedV5;
     }
 
 }
\ No newline at end of file

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpImageParser.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpImageParser.java?rev=1138134&r1=1138133&r2=1138134&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpImageParser.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/BmpImageParser.java Tue Jun 21 18:51:28 2011
@@ -104,19 +104,93 @@ public class BmpImageParser extends Imag
 
         int bitmapHeaderSize = read4Bytes("Bitmap Header Size", is,
                 "Not a Valid BMP File");
-        int width = read4Bytes("Width", is, "Not a Valid BMP File");
-        int height = read4Bytes("Height", is, "Not a Valid BMP File");
-        int planes = read2Bytes("Planes", is, "Not a Valid BMP File");
-        int bitsPerPixel = read2Bytes("Bits Per Pixel", is,
-                "Not a Valid BMP File");
-        int compression = read4Bytes("Compression", is, "Not a Valid BMP File");
-        int bitmapDataSize = read4Bytes("Bitmap Data Size", is,
-                "Not a Valid BMP File");
-        int hResolution = read4Bytes("HResolution", is, "Not a Valid BMP File");
-        int vResolution = read4Bytes("VResolution", is, "Not a Valid BMP File");
-        int colorsUsed = read4Bytes("ColorsUsed", is, "Not a Valid BMP File");
-        int colorsImportant = read4Bytes("ColorsImportant", is,
-                "Not a Valid BMP File");
+        int width = 0;
+        int height = 0;
+        int planes = 0;
+        int bitsPerPixel = 0;
+        int compression = 0;
+        int bitmapDataSize = 0;
+        int hResolution = 0;
+        int vResolution = 0;
+        int colorsUsed = 0;
+        int colorsImportant = 0;
+        int redMask = 0;
+        int greenMask = 0;
+        int blueMask = 0;
+        int alphaMask = 0;
+        int colorSpaceType = 0;
+        BmpHeaderInfo.ColorSpace colorSpace = new BmpHeaderInfo.ColorSpace();
+        colorSpace.red = new BmpHeaderInfo.ColorSpaceCoordinate();
+        colorSpace.green = new BmpHeaderInfo.ColorSpaceCoordinate();
+        colorSpace.blue = new BmpHeaderInfo.ColorSpaceCoordinate();
+        int gammaRed = 0;
+        int gammaGreen = 0;
+        int gammaBlue = 0;
+        int intent = 0;
+        int profileData = 0;
+        int profileSize = 0;
+        int reservedV5 = 0;
+        
+        if (bitmapHeaderSize >= 40)
+        {
+            // BITMAPINFOHEADER
+            width = read4Bytes("Width", is, "Not a Valid BMP File");
+            height = read4Bytes("Height", is, "Not a Valid BMP File");
+            planes = read2Bytes("Planes", is, "Not a Valid BMP File");
+            bitsPerPixel = read2Bytes("Bits Per Pixel", is,
+                    "Not a Valid BMP File");
+            compression = read4Bytes("Compression", is, "Not a Valid BMP File");
+            bitmapDataSize = read4Bytes("Bitmap Data Size", is,
+                    "Not a Valid BMP File");
+            hResolution = read4Bytes("HResolution", is, "Not a Valid BMP File");
+            vResolution = read4Bytes("VResolution", is, "Not a Valid BMP File");
+            colorsUsed = read4Bytes("ColorsUsed", is, "Not a Valid BMP File");
+            colorsImportant = read4Bytes("ColorsImportant", is,
+                    "Not a Valid BMP File");
+            if (bitmapHeaderSize >= 52 || compression == BI_BITFIELDS)
+            {
+                // 52 = BITMAPV2INFOHEADER, now undocumented
+                // see http://en.wikipedia.org/wiki/BMP_file_format
+                redMask = read4Bytes("RedMask", is, "Not a Valid BMP File");
+                greenMask = read4Bytes("GreenMask", is, "Not a Valid BMP File");
+                blueMask = read4Bytes("BlueMask", is, "Not a Valid BMP File");
+            }
+            if (bitmapHeaderSize >= 56)
+            {
+                // 56 = the now undocumented BITMAPV3HEADER sometimes used by Photoshop
+                // see http://forums.adobe.com/thread/751592?tstart=1
+                alphaMask = read4Bytes("AlphaMask", is, "Not a Valid BMP File");
+            }
+            if (bitmapHeaderSize >= 108)
+            {
+                // BITMAPV4HEADER
+                colorSpaceType = read4Bytes("ColorSpaceType", is, "Not a Valid BMP File");
+                colorSpace.red.x = read4Bytes("ColorSpaceRedX", is, "Not a Valid BMP File");
+                colorSpace.red.y = read4Bytes("ColorSpaceRedY", is, "Not a Valid BMP File");
+                colorSpace.red.z = read4Bytes("ColorSpaceRedZ", is, "Not a Valid BMP File");
+                colorSpace.green.x = read4Bytes("ColorSpaceGreenX", is, "Not a Valid BMP File");
+                colorSpace.green.y = read4Bytes("ColorSpaceGreenY", is, "Not a Valid BMP File");
+                colorSpace.green.z = read4Bytes("ColorSpaceGreenZ", is, "Not a Valid BMP File");
+                colorSpace.blue.x = read4Bytes("ColorSpaceBlueX", is, "Not a Valid BMP File");
+                colorSpace.blue.y = read4Bytes("ColorSpaceBlueY", is, "Not a Valid BMP File");
+                colorSpace.blue.z = read4Bytes("ColorSpaceBlueZ", is, "Not a Valid BMP File");
+                gammaRed = read4Bytes("GammaRed", is, "Not a Valid BMP File");
+                gammaGreen = read4Bytes("GammaGreen", is, "Not a Valid BMP File");
+                gammaBlue = read4Bytes("GammaBlue", is, "Not a Valid BMP File");
+            }
+            if (bitmapHeaderSize >= 124)
+            {
+                // BITMAPV5HEADER
+                intent = read4Bytes("Intent", is, "Not a Valid BMP File");
+                profileData = read4Bytes("ProfileData", is, "Not a Valid BMP File");
+                profileSize = read4Bytes("ProfileSize", is, "Not a Valid BMP File");
+                reservedV5 = read4Bytes("Reserved", is, "Not a Valid BMP File");
+            }
+        }
+        else
+        {
+            throw new ImageReadException("Invalid/unsupported BMP file");
+        }
 
         if (verbose)
         {
@@ -136,12 +210,39 @@ public class BmpImageParser extends Imag
             this.debugNumber("vResolution", vResolution, 4);
             this.debugNumber("colorsUsed", colorsUsed, 4);
             this.debugNumber("colorsImportant", colorsImportant, 4);
+            if (bitmapHeaderSize >= 52 || compression == BI_BITFIELDS)
+            {
+                this.debugNumber("redMask", redMask, 4);
+                this.debugNumber("greenMask", greenMask, 4);
+                this.debugNumber("blueMask", blueMask, 4);
+            }
+            if (bitmapHeaderSize >= 56)
+            {
+                this.debugNumber("alphaMask", alphaMask, 4);
+            }
+            if (bitmapHeaderSize >= 108)
+            {
+                this.debugNumber("colorSpaceType", colorSpaceType, 4);
+                this.debugNumber("gammaRed", gammaRed, 4);
+                this.debugNumber("gammaGreen", gammaGreen, 4);
+                this.debugNumber("gammaBlue", gammaBlue, 4);
+            }
+            if (bitmapHeaderSize >= 124)
+            {
+                this.debugNumber("intent", intent, 4);
+                this.debugNumber("profileData", profileData, 4);
+                this.debugNumber("profileSize", profileSize, 4);
+                this.debugNumber("reservedV5", reservedV5, 4);
+            }
         }
 
         BmpHeaderInfo result = new BmpHeaderInfo(identifier1, identifier2,
                 fileSize, reserved, bitmapDataOffset, bitmapHeaderSize, width,
                 height, planes, bitsPerPixel, compression, bitmapDataSize,
-                hResolution, vResolution, colorsUsed, colorsImportant);
+                hResolution, vResolution, colorsUsed, colorsImportant,
+                redMask, greenMask, blueMask, alphaMask, colorSpaceType,
+                colorSpace, gammaRed, gammaGreen, gammaBlue, intent,
+                profileData, profileSize, reservedV5);
         return result;
     }
 
@@ -230,6 +331,9 @@ public class BmpImageParser extends Imag
             this.debugNumber("Compression", bhi.compression, 4);
         }
 
+        // A palette is always valid, even for images that don't need it
+        // (like 32 bpp), it specifies the "optimal color palette" for
+        // when the image is displayed on a <= 256 color graphics card.
         int paletteLength;
         int rleSamplesPerByte = 0;
         boolean rle = false;
@@ -274,8 +378,10 @@ public class BmpImageParser extends Imag
         case BI_BITFIELDS:
             if (verbose)
                 System.out.println("Compression: BI_BITFIELDS");
-            paletteLength = 3 * 4; // TODO: is this right? are the masks always
-            // LONGs?
+            if (bhi.bitsPerPixel <= 8)
+                paletteLength = 4 * colorTableSize;
+            else
+                paletteLength = 0;
             // BytesPerPixel = 2;
             // BytesPerPaletteEntry = 4;
             break;
@@ -319,7 +425,8 @@ public class BmpImageParser extends Imag
             imageLineLength++;
 
         final int headerSize = BITMAP_FILE_HEADER_SIZE
-                + BITMAP_INFO_HEADER_SIZE;
+                + bhi.bitmapHeaderSize
+                + (bhi.bitmapHeaderSize == 40 &&  bhi.compression == BI_BITFIELDS ? 3*4 : 0);
         int expectedDataOffset = headerSize + paletteLength;
 
         if (verbose)
@@ -605,9 +712,8 @@ public class BmpImageParser extends Imag
         int width = bhi.width;
         int height = bhi.height;
 
-        boolean hasAlpha = false;
         BufferedImage result = getBufferedImageFactory(params)
-                .getColorBufferedImage(width, height, hasAlpha);
+                .getColorBufferedImage(width, height, true);
 
         if (verbose)
         {

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/pixelparsers/PixelParserBitFields.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/pixelparsers/PixelParserBitFields.java?rev=1138134&r1=1138133&r2=1138134&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/pixelparsers/PixelParserBitFields.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/bmp/pixelparsers/PixelParserBitFields.java Tue Jun 21 18:51:28 2011
@@ -16,9 +16,7 @@
  */
 package org.apache.sanselan.formats.bmp.pixelparsers;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
-import java.io.InputStream;
 
 import org.apache.sanselan.ImageReadException;
 import org.apache.sanselan.formats.bmp.BmpHeaderInfo;
@@ -29,28 +27,27 @@ public class PixelParserBitFields extend
     private final int redShift;
     private final int greenShift;
     private final int blueShift;
+    private final int alphaShift;
 
     private final int redMask;
     private final int greenMask;
     private final int blueMask;
+    private final int alphaMask;
 
     public PixelParserBitFields(BmpHeaderInfo bhi, byte ColorTable[],
             byte ImageData[]) throws ImageReadException, IOException
     {
         super(bhi, ColorTable, ImageData);
 
-        InputStream bais = new ByteArrayInputStream(ColorTable);
-
-        redMask = bfp.read4Bytes("redMask", bais,
-                "BMP BI_BITFIELDS Bad Color Table");
-        greenMask = bfp.read4Bytes("greenMask", bais,
-                "BMP BI_BITFIELDS Bad Color Table");
-        blueMask = bfp.read4Bytes("blueMask", bais,
-                "BMP BI_BITFIELDS Bad Color Table");
+        redMask = bhi.redMask;
+        greenMask = bhi.greenMask;
+        blueMask = bhi.blueMask;
+        alphaMask = bhi.alphaMask;
 
         redShift = getMaskShift(redMask);
         greenShift = getMaskShift(greenMask);
         blueShift = getMaskShift(blueMask);
+        alphaShift = (alphaMask != 0 ? getMaskShift(alphaMask) : 0);
     }
 
     private int getMaskShift(int mask)
@@ -106,12 +103,12 @@ public class PixelParserBitFields extend
         int red = (redMask & data);
         int green = (greenMask & data);
         int blue = (blueMask & data);
+        int alpha = (alphaMask != 0 ? alphaMask & data : 0xff);
 
         red = (redShift >= 0) ? red >> redShift : red << -redShift;
         green = (greenShift >= 0) ? green >> greenShift : green << -greenShift;
         blue = (blueShift >= 0) ? blue >> blueShift : blue << -blueShift;
-
-        int alpha = 0xff;
+        alpha = (alphaShift >= 0) ? alpha >> alphaShift : alpha << -alphaShift;
 
         int rgb = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
 

Added: commons/proper/sanselan/trunk/src/test/data/images/bmp/3/V4-bitfields.bmp
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/bmp/3/V4-bitfields.bmp?rev=1138134&view=auto
==============================================================================
Binary file - no diff available.

Propchange: commons/proper/sanselan/trunk/src/test/data/images/bmp/3/V4-bitfields.bmp
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: commons/proper/sanselan/trunk/src/test/data/images/bmp/3/info.txt
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/bmp/3/info.txt?rev=1138134&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/test/data/images/bmp/3/info.txt (added)
+++ commons/proper/sanselan/trunk/src/test/data/images/bmp/3/info.txt Tue Jun 21 18:51:28 2011
@@ -0,0 +1 @@
+V4-bitfields.bmp: V4 BMP with bitfields and alpha, blue "16" on transparent background. Contributed by Damjan Jovanovic.