about summary refs log tree commit diff
path: root/converter/other/bmptopnm.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/other/bmptopnm.c')
-rw-r--r--converter/other/bmptopnm.c114
1 files changed, 87 insertions, 27 deletions
diff --git a/converter/other/bmptopnm.c b/converter/other/bmptopnm.c
index e1bd1403..9a405d70 100644
--- a/converter/other/bmptopnm.c
+++ b/converter/other/bmptopnm.c
@@ -17,11 +17,17 @@
  in supporting documentation.  This software is provided "as is"
  without express or implied warranty.
 
+ Note: From mid-2003 to mid-2007, this program would crash on any 16
+ bit BMP without transparency and no one reported it.  Before that, it
+ refused to even try to read a 16 bit BMP.  I conclude that essentially
+ nobody is using 16 bit BMP.
+
 *****************************************************************************/
 #include <string.h>
 #include <limits.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -42,6 +48,10 @@ struct bitPosition {
 
        Example: if 16 bits are laid out as XRRRRRGGGGGBBBBB then the shift
        count for the R component is 10 and the mask is 0000000000011111.
+
+       A 'mask' of zero denotes absence of any bits; e.g. in the example
+       above, the mask for the transparency component is zero because there
+       is no transparency component .  'shift' is arbitrary in that case.
     */
     unsigned int shift;
         /* How many bits right you have to shift the value to get the subject
@@ -306,6 +316,36 @@ readOs2InfoHeader(FILE *                 const ifP,
 
 
 static void
+validateCompression(unsigned long const compression,
+                    enum rowOrder const rowOrder,
+                    unsigned int  const cBitCount) {
+    
+    if (compression != COMP_RGB && compression != COMP_BITFIELDS &&
+        compression != COMP_RLE4 && compression != COMP_RLE8 ) 
+        pm_error("Input has unknown encoding.  "
+                 "Compression type code = %ld.  The only ones we know "
+                 "are RGB (%u), BITFIELDS (%u), "
+                 "RLE4 (%u), and RLE8 (%u)",
+                 compression, COMP_RGB, COMP_BITFIELDS,
+                 COMP_RLE4, COMP_RLE8);
+                     
+    if ((compression == COMP_RLE4 || compression == COMP_RLE8) &&
+        rowOrder == TOPDOWN )                        
+        pm_error("Invalid BMP header.  Claims image is top-down and also "
+                 "compressed, which is an impossible combination.");
+
+    if ( (compression == COMP_RLE4 && cBitCount !=4) ||
+         (compression == COMP_RLE8 && cBitCount !=8) ) 
+        pm_error("Invalid BMP header.  " 
+                 "Compression type (%s) disagrees with "
+                 "number of bits per pixel (%u).",
+                 compression == COMP_RLE4 ? "RLE4" : "RLE8",
+                 cBitCount);
+}
+
+
+
+static void
 readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
                                  struct bmpInfoHeader * const headerP) {
 /*----------------------------------------------------------------------------
@@ -340,22 +380,9 @@ readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
         unsigned long int const compression = GetLong(ifP);
 
         headerP->bitFields = (compression == COMP_BITFIELDS);
-    
-        if (compression != COMP_RGB && compression != COMP_BITFIELDS &&
-            compression != COMP_RLE4 && compression != COMP_RLE8 ) 
-            pm_error("Input is compressed.  Unsupported encoding method.\n"
-                     "Compression type code = %ld", compression);
-                     
-        if ( (compression == COMP_RLE4 || compression == COMP_RLE8) &&
-              headerP->rowOrder == TOPDOWN )                        
-            pm_error("Invalid BMP header.  Top-down images cannot be compressed.");
 
-        if ( (compression == COMP_RLE4 && headerP->cBitCount !=4) ||
-             (compression == COMP_RLE8 && headerP->cBitCount !=8) )                        
-            pm_error("Invalid BMP header.\n" 
-                     "Compression type (%s) disagrees with number of bits per pixel (%u).",
-                      compression == COMP_RLE4 ? "RLE4" : "RLE8",
-                      headerP->cBitCount);
+        validateCompression(compression, headerP->rowOrder,
+                            headerP->cBitCount);
 
         headerP->compression = compression;             
     }
@@ -399,7 +426,7 @@ lsbZeroCount(unsigned int const mask)
    Use GCC built-in when available.
 -----------------------------------------------------------------------------*/
 
-#if ( defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 304) )
+#if HAVE_GCC_BITCOUNT 
 {
       return ( mask==0 ? sizeof(mask)*8 : __builtin_ctz(mask) );
 }
@@ -463,6 +490,10 @@ defaultPixelformat(unsigned int const bitCount) {
 
     switch (bitCount) {
     case 16:
+        /* This layout is sometimes called "RGB555".  A document from
+           Microsoft says this is the default (when the "compression"
+           field of the header says COMP_BITFIELDS).
+        */
         retval.conventionalBgr = FALSE;
         retval.red.shift = 10;
         retval.grn.shift = 5;
@@ -500,6 +531,11 @@ readV4InfoHeaderExtension(FILE *                 const ifP,
                           struct bmpInfoHeader * const headerP) {
 
     if (headerP->bitFields) {
+        /* A document from Microsoft says on Windows 95 there is no
+           transparency plane and (red, green, blue) must be either
+           (5,5,5) or (5,6,5) for 16 bit and (8,8,8) for 32 bit.
+           It calls these RGB555, RGB565, RGB888.
+        */
         headerP->pixelformat.red = bitPositionFromMask(GetLong(ifP));
         headerP->pixelformat.grn = bitPositionFromMask(GetLong(ifP));
         headerP->pixelformat.blu = bitPositionFromMask(GetLong(ifP));
@@ -655,16 +691,16 @@ extractBitFields(unsigned int       const rasterval,
         (rasterval >> pixelformat.blu.shift) & pixelformat.blu.mask;
     unsigned int const abits = 
         (rasterval >> pixelformat.trn.shift) & pixelformat.trn.mask;
-    
-    *rP = pixelformat.red.mask ?
-              (unsigned int) rbits * maxval / pixelformat.red.mask : 0;
-    *gP = pixelformat.grn.mask ?
-              (unsigned int) gbits * maxval / pixelformat.grn.mask : 0;
-    *bP = pixelformat.blu.mask ?
-              (unsigned int) bbits * maxval / pixelformat.blu.mask : 0;
-    *aP = pixelformat.trn.mask ?
-              (unsigned int) abits * maxval / pixelformat.trn.mask : 0;
-}
+
+    *rP = pixelformat.red.mask > 0 ?
+        (unsigned int) rbits * maxval / pixelformat.red.mask : 0;
+    *gP = pixelformat.grn.mask > 0 ?
+        (unsigned int) gbits * maxval / pixelformat.grn.mask : 0;
+    *bP = pixelformat.blu.mask > 0 ?
+        (unsigned int) bbits * maxval / pixelformat.blu.mask : 0;
+    *aP = pixelformat.trn.mask > 0 ?
+        (unsigned int) abits * maxval / pixelformat.trn.mask : 0;
+}        
 
 
 
@@ -1220,7 +1256,7 @@ readColorMap(FILE *               const ifP,
                    bytesRead,
                    BMPlencolormap(BMPheader.class, BMPheader.cBitCount, 
                                   BMPheader.cmapsize));
-}
+    }
 }
 
 
@@ -1259,6 +1295,25 @@ warnIfBadFileSize(struct bmpInfoHeader const BMPheader,
 
 
 
+static bool
+isValidBmpBpp(unsigned int const cBitCount) {
+
+    switch (cBitCount) {
+    case 1:
+    case 2:
+    case 4:
+    case 8:
+    case 16:
+    case 24:
+    case 32:
+        return true;
+    default:
+        return false;
+    }
+}
+
+
+
 static void
 readBmp(FILE *               const ifP, 
         unsigned char ***    const BMPrasterP, 
@@ -1316,6 +1371,11 @@ readBmp(FILE *               const ifP,
     if (fgetc(ifP) != EOF)
         pm_message("warning: some image data remains unread.");
     
+    if (!isValidBmpBpp(BMPheader.cBitCount))
+        pm_error("Invalid BMP image: 'cBitCount' field of header "
+                 "(number of bits for each pixel in raster) is %u",
+                 BMPheader.cBitCount);
+
     *colsP        = BMPheader.cols;
     *rowsP        = BMPheader.rows;
     *cBitCountP   = BMPheader.cBitCount;