about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2016-05-09 03:19:16 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2016-05-09 03:19:16 +0000
commitc2f873dc0560e36d181b12f450f659bfdb621b08 (patch)
tree8763f5667dd24555ec6d64e8786ae68d37cca920
parent7a0dbb463c55783da12c0d25aaebf6f234a8e457 (diff)
downloadnetpbm-mirror-c2f873dc0560e36d181b12f450f659bfdb621b08.tar.gz
netpbm-mirror-c2f873dc0560e36d181b12f450f659bfdb621b08.tar.xz
netpbm-mirror-c2f873dc0560e36d181b12f450f659bfdb621b08.zip
Fix handling of V4 and V5 Windows BMP - didn't work at all
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@2770 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--converter/bmp.h89
-rw-r--r--converter/other/bmptopnm.c219
-rw-r--r--doc/HISTORY2
3 files changed, 226 insertions, 84 deletions
diff --git a/converter/bmp.h b/converter/bmp.h
index f2355929..78c469d1 100644
--- a/converter/bmp.h
+++ b/converter/bmp.h
@@ -83,7 +83,17 @@
 
 #include "pm.h"  /* For pm_error() */
 
-enum bmpClass {C_WIN, C_OS2};
+enum bmpClass {
+    BMP_C_OS2_1x,
+    BMP_C_OS2_2x,
+    BMP_C_WIN_V1,
+    BMP_C_WIN_V2,
+    BMP_C_WIN_V3,
+    BMP_C_WIN_V4,
+    BMP_C_WIN_V5
+};
+
+
 
 static __inline__ const char *
 BMPClassName(enum bmpClass const class) {
@@ -91,8 +101,13 @@ BMPClassName(enum bmpClass const class) {
     const char * name;
 
     switch (class) {
-    case C_OS2: name = "OS/2 (v1)";    break;
-    case C_WIN: name = "Windows (v1)"; break;
+    case BMP_C_OS2_1x: name = "OS/2 (v1)";    break;
+    case BMP_C_OS2_2x: name = "OS/2 (v2)";    break;
+    case BMP_C_WIN_V1: name = "Windows (v1)"; break;
+    case BMP_C_WIN_V2: name = "Windows (v2)"; break;
+    case BMP_C_WIN_V3: name = "Windows (v3)"; break;
+    case BMP_C_WIN_V4: name = "Windows (v4)"; break;
+    case BMP_C_WIN_V5: name = "Windows (v5)"; break;
     }
 
   return name;
@@ -167,14 +182,66 @@ BMPleninfoheader(enum bmpClass const class) {
     unsigned int retval;
 
     switch (class) {
-    case C_WIN: retval = BMP_HDRLEN_WIN_V1; break;
-    case C_OS2: retval = BMP_HDRLEN_OS2_1x; break;
+    case BMP_C_WIN_V1: retval = BMP_HDRLEN_WIN_V1; break;
+    case BMP_C_WIN_V2: retval = BMP_HDRLEN_WIN_V2; break;
+    case BMP_C_WIN_V3: retval = BMP_HDRLEN_WIN_V3; break;
+    case BMP_C_WIN_V4: retval = BMP_HDRLEN_WIN_V4; break;
+    case BMP_C_WIN_V5: retval = BMP_HDRLEN_WIN_V5; break;
+    case BMP_C_OS2_1x: retval = BMP_HDRLEN_OS2_1x; break;
+    case BMP_C_OS2_2x: retval = BMP_HDRLEN_OS2_2x; break;
     }
     return retval;
 }
 
 
 
+static __inline__ void
+BMPdetermineclass(unsigned int    const infoHdrLen,
+                  enum bmpClass * const classP,
+                  const char **   const errorP) {
+/*----------------------------------------------------------------------------
+   Determine the class of BMP, based on the fact that the info header is
+   'infoHdrLen' bytes long.
+-----------------------------------------------------------------------------*/
+    switch (infoHdrLen) {
+    case BMP_HDRLEN_OS2_1x: *errorP = NULL; *classP = BMP_C_OS2_1x; break;
+    case BMP_HDRLEN_OS2_2x: *errorP = NULL; *classP = BMP_C_OS2_2x; break;
+    case BMP_HDRLEN_WIN_V1: *errorP = NULL; *classP = BMP_C_WIN_V1; break;
+    case BMP_HDRLEN_WIN_V2: *errorP = NULL; *classP = BMP_C_WIN_V2; break;
+    case BMP_HDRLEN_WIN_V3: *errorP = NULL; *classP = BMP_C_WIN_V3; break;
+    case BMP_HDRLEN_WIN_V4: *errorP = NULL; *classP = BMP_C_WIN_V4; break;
+    case BMP_HDRLEN_WIN_V5: *errorP = NULL; *classP = BMP_C_WIN_V5; break;
+
+    default:
+        pm_asprintf(errorP, "Not one of the 7 lengths we recognize");
+    }
+}
+
+
+
+static __inline__ unsigned int
+BMPlenrgb(enum bmpClass const class) {
+
+    unsigned int lenrgb;
+
+    switch (class) {
+    case BMP_C_OS2_1x:
+    case BMP_C_OS2_2x:
+        lenrgb = 3;
+        break;
+    case BMP_C_WIN_V1:
+    case BMP_C_WIN_V2:
+    case BMP_C_WIN_V3:
+    case BMP_C_WIN_V4:
+    case BMP_C_WIN_V5:
+        lenrgb = 4;
+        break;
+    }
+    return lenrgb;
+}
+
+
+
 static __inline__ unsigned int
 BMPlencolormap(enum bmpClass const class,
                unsigned int  const bitcount, 
@@ -186,7 +253,6 @@ BMPlencolormap(enum bmpClass const class,
 
    'cmapsize' == 0 means there is no palette.
 -----------------------------------------------------------------------------*/
-    unsigned int lenrgb;
     unsigned int lencolormap;
 
     if (bitcount < 1)
@@ -194,15 +260,10 @@ BMPlencolormap(enum bmpClass const class,
     else if (bitcount > 8) 
         lencolormap = 0;
     else {
-        switch (class) {
-        case C_WIN: lenrgb = 4; break;
-        case C_OS2: lenrgb = 3; break;
-        }
-
-        if (!cmapsize) 
-            lencolormap = (1 << bitcount) * lenrgb;
+        if (cmapsize) 
+            lencolormap = cmapsize * BMPlenrgb(class);
         else 
-            lencolormap = cmapsize * lenrgb;
+            lencolormap = (1 << bitcount) * BMPlenrgb(class);
     }
     return lencolormap;
 }
diff --git a/converter/other/bmptopnm.c b/converter/other/bmptopnm.c
index d57bfba9..8c2b657e 100644
--- a/converter/other/bmptopnm.c
+++ b/converter/other/bmptopnm.c
@@ -80,6 +80,19 @@ struct pixelformat {
         */
 };
 
+typedef struct {
+    /* These are all encodings of floating point */
+    unsigned long x;
+    unsigned long y; 
+    unsigned long z;
+} cieXyz;
+
+typedef struct {
+    cieXyz red;
+    cieXyz grn;
+    cieXyz blu;
+} cieXyzTriple;
+
 struct bmpInfoHeader {
     enum rowOrder rowOrder;
     unsigned int cols;
@@ -103,6 +116,7 @@ struct bmpInfoHeader {
     unsigned short cPlanes;
     BMPCompType compression;
     struct pixelformat pixelformat;
+    cieXyzTriple endPoints;
 };
 
 
@@ -208,21 +222,30 @@ GetLong(FILE * const fp) {
 
 
 
-typedef struct {
-    long dummy[12];
-} cieXyzTriple;
+static cieXyz
+GetCieXyz(FILE * const ifP) {
+
+    cieXyz retval;
+
+    retval.x = GetLong(ifP);
+    retval.y = GetLong(ifP);
+    retval.z = GetLong(ifP);
+
+    return retval;
+}
+
+
 
 static cieXyzTriple
-GetCieXyzTriple(FILE * const fp) {
+GetCieXyzTriple(FILE *         const ifP) {
 
-    cieXyzTriple v;
-    unsigned int i;
+    cieXyzTriple retval;
 
-    for (i = 0; i < 12; ++i) 
-        if (pm_readlittlelong(fp, &v.dummy[i]) == -1)
-            pm_error(er_read, ifname);
+    retval.red = GetCieXyz(ifP);
+    retval.grn = GetCieXyz(ifP);
+    retval.blu = GetCieXyz(ifP);
 
-    return v;
+    return retval;
 }
 
 
@@ -274,14 +297,12 @@ bmpReadfileheader(FILE *         const ifP,
 
 
 static void
-readOs2InfoHeader(FILE *                 const ifP,
-                  struct bmpInfoHeader * const headerP) {
+readOs2InfoHeaderRest(FILE *                 const ifP,
+                      struct bmpInfoHeader * const headerP) {
 
     unsigned short colsField, rowsField;
     unsigned short planesField, bitCountField;
 
-    headerP->class = C_OS2;
-
     pm_readlittleshortu(ifP, &colsField);
     if (colsField == 0)
         pm_error("Invalid BMP file: says width is zero");
@@ -317,9 +338,6 @@ readOs2InfoHeader(FILE *                 const ifP,
                  headerP->cBitCount);
                  
     headerP->compression = BMPCOMP_RGB;
-    
-    pm_message("OS/2 BMP, %dx%dx%d",
-               headerP->cols, headerP->rows, headerP->cBitCount);
 }
 
 
@@ -367,8 +385,6 @@ readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
     int colorsused;        /* ColorsUsed value from header */
     unsigned short planesField, bitCountField;
 
-    headerP->class = C_WIN;
-
     headerP->cols = GetLong(ifP);
     if (headerP->cols == 0)
         pm_error("Invalid BMP file: says width is zero");
@@ -541,7 +557,15 @@ defaultPixelformat(unsigned int const bitCount) {
 
 static void
 readV4InfoHeaderExtension(FILE *                 const ifP, 
-                          struct bmpInfoHeader * const headerP) {
+                          struct bmpInfoHeader * const headerP,
+                          unsigned int *         const bytesReadP) {
+
+    unsigned long redMsk, grnMsk, bluMsk, trnMsk;
+
+    redMsk = GetLong(ifP);
+    grnMsk = GetLong(ifP);
+    bluMsk = GetLong(ifP);
+    trnMsk = GetLong(ifP);
 
     if (headerP->bitFields) {
         /* A document from Microsoft says on Windows 95 there is no
@@ -549,25 +573,44 @@ readV4InfoHeaderExtension(FILE *                 const ifP,
            (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));
-        headerP->pixelformat.trn = bitPositionFromMask(GetLong(ifP));
+        headerP->pixelformat.red = bitPositionFromMask(redMsk);
+        headerP->pixelformat.grn = bitPositionFromMask(grnMsk);
+        headerP->pixelformat.blu = bitPositionFromMask(bluMsk);
+        headerP->pixelformat.trn = bitPositionFromMask(trnMsk);
 
         computeConventionalBgr(&headerP->pixelformat, headerP->cBitCount);
     } else
         headerP->pixelformat = defaultPixelformat(headerP->cBitCount);
 
     GetLong(ifP);  /* Color space */
-    GetCieXyzTriple(ifP);  /* Endpoints */
+
+    headerP->endPoints = GetCieXyzTriple(ifP);  /* 36 bytes */
+
     GetLong(ifP);  /* GammaRed */
     GetLong(ifP);  /* GammaGreen */
     GetLong(ifP);  /* GammaBlue */
+
+    *bytesReadP = 68;
 } 
 
 
 
 static void
+readV5InfoHeaderExtension(FILE *                 const ifP, 
+                          struct bmpInfoHeader * const headerP,
+                          unsigned int *         const bytesReadP) {
+
+    GetLong(ifP);  /* Intent */
+    GetLong(ifP);  /* ProfileData */
+    GetLong(ifP);  /* ProfileSize */
+    GetLong(ifP);  /* Reserved */
+
+    *bytesReadP = 16;
+}
+
+
+
+static void
 defaultV4InfoHeaderExtension(struct bmpInfoHeader * const headerP) {
 
     headerP->pixelformat = defaultPixelformat(headerP->cBitCount);
@@ -577,9 +620,9 @@ defaultV4InfoHeaderExtension(struct bmpInfoHeader * const headerP) {
 
 
 static void
-readWindowsInfoHeader(FILE *                 const ifP, 
-                      unsigned int           const cInfoHeaderSize,
-                      struct bmpInfoHeader * const headerP) {
+readWindowsInfoHeaderRest(FILE *                 const ifP, 
+                          unsigned int           const cInfoHeaderSize,
+                          struct bmpInfoHeader * const headerP) {
 
     /* There are 3 major formats of Windows
        BMP, identified by the 3 info header lengths.  The original
@@ -587,23 +630,34 @@ readWindowsInfoHeader(FILE *                 const ifP,
        new with Windows 95 and NT 4.0.  The "V5 header" is 124 bytes
        and was new with Windows 98 and Windows 2000.
     */
+    unsigned int bytesRead;
+
     readWindowsBasic40ByteInfoHeader(ifP, headerP);
 
-    if (cInfoHeaderSize >= 108) 
-        readV4InfoHeaderExtension(ifP, headerP);
-    else 
+    bytesRead = 40;
+
+    if (cInfoHeaderSize >= BMP_HDRLEN_WIN_V4) {
+        unsigned int v4BytesRead;
+        readV4InfoHeaderExtension(ifP, headerP, &v4BytesRead);
+        bytesRead += v4BytesRead;
+
+        assert(bytesRead == BMP_HDRLEN_WIN_V4);
+    } else 
         defaultV4InfoHeaderExtension(headerP);
 
-    if (cInfoHeaderSize >= 124) {
-        /* Read off the V5 info header extension. */
-        GetLong(ifP);  /* Intent */
-        GetLong(ifP);  /* ProfileData */
-        GetLong(ifP);  /* ProfileSize */
-        GetLong(ifP);  /* Reserved */
+    if (cInfoHeaderSize >= BMP_HDRLEN_WIN_V5) {
+        unsigned int v5BytesRead;
+        readV5InfoHeaderExtension(ifP, headerP, &v5BytesRead);
+        bytesRead += v5BytesRead;
+        assert(bytesRead == BMP_HDRLEN_WIN_V5);
     }
 
-    pm_message("Windows BMP, %dx%dx%d",
-               headerP->cols, headerP->rows, headerP->cBitCount);
+    for (; bytesRead < cInfoHeaderSize;) {
+        GetByte(ifP);
+        ++bytesRead;
+    }
+    
+    assert(bytesRead == cInfoHeaderSize);
 }
 
 
@@ -611,25 +665,36 @@ readWindowsInfoHeader(FILE *                 const ifP,
 static void
 bmpReadinfoheader(FILE *                 const ifP, 
                   unsigned int *         const bytesReadP,
-                  struct bmpInfoHeader * const headerP) {
+                  struct bmpInfoHeader * const headerP,
+                  const char **          const errorP) {
 
     unsigned int const cInfoHeaderSize = GetLong(ifP);
 
-    switch (cInfoHeaderSize) {
-    case 12:
-        readOs2InfoHeader(ifP, headerP);
-        break;
-    case 40: 
-    case 108:
-    case 124:
-        readWindowsInfoHeader(ifP, cInfoHeaderSize, headerP);
-        break;
-    default:
-        pm_error("%s: unknown Info Header size: %u bytes", 
-                 ifname, cInfoHeaderSize);
-        break;
+    const char * error;
+
+    BMPdetermineclass(cInfoHeaderSize, &headerP->class, &error);
+
+    if (error) {
+        pm_asprintf(errorP, "Cannot determine the class of BMP from the "
+                    "info header size %u.  %s", cInfoHeaderSize, error);
+        pm_strfree(error);
+    } else {
+        switch (headerP->class) {
+        case BMP_C_WIN_V1: 
+        case BMP_C_WIN_V2: 
+        case BMP_C_WIN_V3: 
+        case BMP_C_WIN_V4: 
+        case BMP_C_WIN_V5: 
+            readWindowsInfoHeaderRest(ifP, cInfoHeaderSize, headerP);
+            break;
+        case BMP_C_OS2_1x:
+        case BMP_C_OS2_2x:
+            readOs2InfoHeaderRest(ifP, headerP);
+            break;
+        }
+        *errorP = NULL;
+        *bytesReadP = cInfoHeaderSize;
     }
-    *bytesReadP = cInfoHeaderSize;
 }
 
 
@@ -676,11 +741,12 @@ bmpReadColormap(FILE *         const ifP,
 
         bytesRead += 3;
 
-        if (class == C_WIN) {
+        for (i = 3; i < BMPlenrgb(class); ++i) {
             GetByte(ifP);
             bytesRead += 1;
         }
     }
+
     *colormapP = colormap;
     *bytesReadP = bytesRead;
 }
@@ -1190,19 +1256,28 @@ bmpReadraster(FILE *            const ifP,
 
 static void
 reportHeader(struct bmpInfoHeader const header,
-             unsigned int         const offBits) {
+             unsigned int         const offBits,
+             bool                 const verbose) {
              
-    pm_message("BMP image header says:");
-    pm_message("  Class of BMP: %s", BMPClassName(header.class));
-    pm_message("  Width: %d pixels", header.cols);
-    pm_message("  Height: %d pixels", header.rows);
-    pm_message("  Depth: %d planes", header.cPlanes);
-    pm_message("  Row order: %s", 
-               header.rowOrder == BOTTOMUP ? "bottom up" : "top down");
-    pm_message("  Byte offset of raster within file: %u", offBits);
-    pm_message("  Bits per pixel in raster: %u", header.cBitCount);
-    pm_message("  Compression: %s", BMPCompTypeName(header.compression));
-    pm_message("  Colors in color map: %u", header.cmapsize);
+    if (verbose) {
+        pm_message("BMP image header says:");
+        pm_message("  Class of BMP: %s", BMPClassName(header.class));
+        pm_message("  Width: %d pixels", header.cols);
+        pm_message("  Height: %d pixels", header.rows);
+        pm_message("  Depth: %d planes", header.cPlanes);
+        pm_message("  Row order: %s", 
+                   header.rowOrder == BOTTOMUP ? "bottom up" : "top down");
+        pm_message("  Byte offset of raster within file: %u", offBits);
+        pm_message("  Bits per pixel in raster: %u", header.cBitCount);
+        pm_message("  Compression: %s", BMPCompTypeName(header.compression));
+        pm_message("  Colors in color map: %u", header.cmapsize);
+    } else {
+        pm_message("%s BMP, %ux%ux%u",
+                   BMPClassName(header.class),
+                   header.cols,
+                   header.rows,
+                   header.cBitCount);
+    }
 }        
 
 
@@ -1340,14 +1415,18 @@ readBmp(FILE *               const ifP,
     }
     {
         unsigned int bytesRead;
-        bmpReadinfoheader(ifP, &bytesRead, &bmpHeader);
+        const char * error;
+        bmpReadinfoheader(ifP, &bytesRead, &bmpHeader, &error);
+        if (error)
+            pm_error("Failed to read the BMP info header.  Image may "
+                     "not be a valid BMP.  %s", error);
+
         if (verbose)
             pm_message("Read %u bytes of header", bytesRead);
         pos += bytesRead;
     }
 
-    if (verbose) 
-        reportHeader(bmpHeader, offBits);
+    reportHeader(bmpHeader, offBits, verbose);
 
     warnIfOffBitsWrong(bmpHeader, offBits);
 
diff --git a/doc/HISTORY b/doc/HISTORY
index 4a23ffb6..03f676ae 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -6,6 +6,8 @@ CHANGE HISTORY
 
 not yet  BJH  Release 10.75.00
 
+              bmptopnm: Add ability to convert Version 4 and 5 Windows BMP.
+
               pbmtext: remove undocumented -dump option; add 'genfontc'
               development tool (buildtools/ directory) to replace it.