changeset 1279:0d3f5f44c0c4

Somewhat improve PCX read support in libgfx.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 18 Aug 2017 02:03:15 +0300
parents 5206e3d4e6b7
children 300a51e98fc3
files src/libgfx.c
diffstat 1 files changed, 69 insertions(+), 66 deletions(-) [+]
line wrap: on
line diff
--- a/src/libgfx.c	Fri Aug 18 00:43:48 2017 +0300
+++ b/src/libgfx.c	Fri Aug 18 02:03:15 2017 +0300
@@ -681,6 +681,8 @@
 #endif
 
 
+#define DMPCX_PAL_COLORS  16 // Number of internal palette colors
+
 typedef struct
 {
     Uint8 r,g,b;
@@ -689,18 +691,21 @@
 
 typedef struct
 {
-    Uint8 manufacturer,
-            version,
-            encoding,
-            bpp;
+    Uint8 manufacturer,     // always 0x0a
+            version,        // Z-Soft PCX Paintbrush version:
+                            // 0 = v2.5, 2 = v2.8 with palette, 3 = v2.8 without palette, 5 = v3.0 or better
+            encoding,       // usually 0x01 = RLE, 0x00 = uncompressed
+            bitsPerPlane;   // bits per pixel per plane
+
     Uint16 xmin, ymin, xmax, ymax;
-    Uint16 hres, vres;
-    DMPCXColor colormap[16];
-    Uint8 reserved;
-    Uint8 nplanes;
-    Uint16 bpl;
-    Uint16 palinfo;
-    Uint8 filler[58];
+    Uint16 hres, vres;      // resolution in DPI, can be image dimensions as well.
+    DMPCXColor colorMap[DMPCX_PAL_COLORS];
+    Uint8 reserved;         // should be set to 0
+    Uint8 nplanes;          // number of planes
+    Uint16 bpl;             // bytes per plane LINE
+    Uint16 palInfo;         // 1 = color/BW, 2 = grayscale
+    Uint16 hScreenSize, vScreenSize;
+    Uint8 filler[54];
 } DMPCXHeader;
 
 
@@ -742,14 +747,13 @@
 static int dmWritePCXRow(void *cbdata, Uint8 *row, size_t len)
 {
     DMPCXData *pcx = (DMPCXData *) cbdata;
-    int plane;
     size_t soffs = 0;
 
 //    fprintf(stderr, "%d, %d * %d = %d\n", len, pcx->header->bpl, pcx->header->nplanes, pcx->header->nplanes * pcx->header->bpl);
 
     pcx->bufOffs = 0;
 
-    for (plane = 0; plane < pcx->header->nplanes; plane++)
+    for (int plane = 0; plane < pcx->header->nplanes; plane++)
     {
         Uint8 data = dmPCXGetByte(row, len, soffs++),
               count = 1;
@@ -814,25 +818,24 @@
     dmMemset(&hdr, 0, sizeof(hdr));
     if (spec->paletted)
     {
-        int i;
-        for (i = 0; i < (img->ncolors > 16 ? 16 : img->ncolors); i++)
+        for (int i = 0; i < (img->ncolors > DMPCX_PAL_COLORS ? DMPCX_PAL_COLORS : img->ncolors); i++)
         {
-            hdr.colormap[i].r = img->pal[i].r;
-            hdr.colormap[i].g = img->pal[i].g;
-            hdr.colormap[i].b = img->pal[i].b;
+            hdr.colorMap[i].r = img->pal[i].r;
+            hdr.colorMap[i].g = img->pal[i].g;
+            hdr.colorMap[i].b = img->pal[i].b;
         }
     }
     hdr.manufacturer = 10;
     hdr.version      = 5;
     hdr.encoding     = 1;
-    hdr.bpp          = 8;
+    hdr.bitsPerPlane = 8;
     hdr.hres         = img->width * spec->scaleX;
     hdr.vres         = img->height * spec->scaleY;
     hdr.xmin         = hdr.ymin = 0;
     hdr.xmax         = hdr.hres - 1;
     hdr.ymax         = hdr.vres - 1;
     hdr.nplanes      = dmImageGetBytesPerPixel(pcx.format);
-    hdr.palinfo      = 1;
+    hdr.palInfo      = 1;
 
     res = (img->width * spec->scaleX);
     hdr.bpl = res / 2;
@@ -840,7 +843,7 @@
     hdr.bpl *= 2;
 
     dmMsg(3, "PCX: paletted=%d, nplanes=%d, bpp=%d, bpl=%d\n",
-        spec->paletted, hdr.nplanes, hdr.bpp, hdr.bpl);
+        spec->paletted, hdr.nplanes, hdr.bitsPerPlane, hdr.bpl);
 
     pcx.bufLen       = hdr.bpl * 4;
     if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL)
@@ -855,7 +858,7 @@
     if (!dm_fwrite_byte(pcx.fp, hdr.manufacturer) ||
         !dm_fwrite_byte(pcx.fp, hdr.version) ||
         !dm_fwrite_byte(pcx.fp, hdr.encoding) ||
-        !dm_fwrite_byte(pcx.fp, hdr.bpp))
+        !dm_fwrite_byte(pcx.fp, hdr.bitsPerPlane))
     {
         res = dmError(DMERR_FWRITE,
             "PCX: Could not write basic header data.\n");
@@ -874,7 +877,7 @@
         goto error;
     }
 
-    if (!dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.colormap, sizeof(hdr.colormap)))
+    if (!dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.colorMap, sizeof(hdr.colorMap)))
     {
         res = dmError(DMERR_FWRITE,
             "PCX: Could not write colormap.\n");
@@ -884,7 +887,7 @@
     if (!dm_fwrite_byte(pcx.fp, hdr.reserved) ||
         !dm_fwrite_byte(pcx.fp, hdr.nplanes) ||
         !dm_fwrite_le16(pcx.fp, hdr.bpl) ||
-        !dm_fwrite_le16(pcx.fp, hdr.palinfo) ||
+        !dm_fwrite_le16(pcx.fp, hdr.palInfo) ||
         !dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.filler, sizeof(hdr.filler)))
     {
         res = dmError(DMERR_FWRITE,
@@ -977,9 +980,7 @@
     DMImage *img;
     DMPCXData pcx;
     DMPCXHeader hdr;
-    BOOL paletted;
-    int res = 0, yc, xc;
-    Uint8 *dp;
+    int res = 0;
 
     pcx.buf = NULL;
 
@@ -987,7 +988,7 @@
     if (!dm_fread_byte(fp, &hdr.manufacturer) ||
         !dm_fread_byte(fp, &hdr.version) ||
         !dm_fread_byte(fp, &hdr.encoding) ||
-        !dm_fread_byte(fp, &hdr.bpp))
+        !dm_fread_byte(fp, &hdr.bitsPerPlane))
     {
         res = dmError(DMERR_FREAD,
             "PCX: Could not read basic header data.\n");
@@ -996,8 +997,7 @@
 
     if (hdr.manufacturer != 10 ||
         hdr.version != 5 ||
-        hdr.encoding != 1 ||
-        hdr.bpp != 8)
+        hdr.encoding != 1)
     {
         res = dmError(DMERR_NOT_SUPPORTED,
             "PCX: Not a PCX file, or unsupported variant.\n");
@@ -1016,7 +1016,7 @@
         goto error;
     }
 
-    if (!dm_fread_str(fp, (Uint8 *) &hdr.colormap, sizeof(hdr.colormap)))
+    if (!dm_fread_str(fp, (Uint8 *) &hdr.colorMap, sizeof(hdr.colorMap)))
     {
         res = dmError(DMERR_FREAD,
             "PCX: Could not read colormap.\n");
@@ -1026,7 +1026,7 @@
     if (!dm_fread_byte(fp, &hdr.reserved) ||
         !dm_fread_byte(fp, &hdr.nplanes) ||
         !dm_fread_le16(fp, &hdr.bpl) ||
-        !dm_fread_le16(fp, &hdr.palinfo) ||
+        !dm_fread_le16(fp, &hdr.palInfo) ||
         !dm_fread_str(fp, (Uint8 *) &hdr.filler, sizeof(hdr.filler)))
     {
         res = dmError(DMERR_FREAD,
@@ -1034,9 +1034,9 @@
         goto error;
     }
 
-    if (hdr.nplanes != 3 && hdr.nplanes != 1)
+    if (hdr.nplanes < 1 || hdr.nplanes > 8)
     {
-        res = dmError(DMERR_FREAD,
+        res = dmError(DMERR_NOT_SUPPORTED,
             "PCX: Unsupported number of bitplanes %d.\n",
             hdr.nplanes);
         goto error;
@@ -1050,7 +1050,14 @@
         goto error;
     }
 
-    paletted = hdr.nplanes == 1;
+    if (hdr.bpl < (img->width * hdr.bitsPerPlane) / 8)
+    {
+        res = dmError(DMERR_MALLOC,
+            "PCX: The bytes per plane line value %d is smaller than width*bpp/8 = %d!\n",
+            hdr.bpl, (img->width * hdr.bitsPerPlane) / 8);
+        goto error;
+    }
+
     pcx.bufLen = hdr.nplanes * hdr.bpl;
     if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL)
     {
@@ -1060,48 +1067,44 @@
     }
 
     // Read image data
-    dp = img->data;
-    for (yc = 0; yc < img->height; yc++)
+    Uint8 *dp = img->data;
+    for (int yc = 0; yc < img->height; yc++)
     {
         // Decode row of RLE'd data
         if (!dmPCXDecodeRLERow(fp, pcx.buf, pcx.bufLen))
         {
             res = dmError(DMERR_INVALID_DATA,
-                "PCX: Error decoding RLE data.\n");
+                "PCX: Error decoding RLE compressed data.\n");
             goto error;
         }
 
         // Decode bitplanes
-        switch (hdr.nplanes)
+        switch (hdr.bitsPerPlane)
         {
-            case 1:
-                memcpy(dp, pcx.buf, img->width);
+            case 8:
+                for (int nplane = 0; nplane < hdr.nplanes; nplane++)
+                {
+                    Uint8 *dptr = dp + nplane,
+                          *sptr = pcx.buf + (hdr.bpl * nplane);
+
+                    for (int xc = 0; xc < img->width; xc += hdr.nplanes, dptr += hdr.nplanes, sptr++)
+                        *dptr = *sptr;
+                }
                 break;
 
-            case 3:
-                {
-                    Uint8 *dptr = dp,
-                            *sptr1 = pcx.buf,
-                            *sptr2 = sptr1 + hdr.bpl,
-                            *sptr3 = sptr2 + hdr.bpl;
-
-                    for (xc = 0; xc < img->width; xc++)
-                    {
-                        *dptr++ = *sptr1++;
-                        *dptr++ = *sptr2++;
-                        *dptr++ = *sptr3++;
-                    }
-                }
-                break;
+            default:
+                res = dmError(DMERR_INVALID_DATA,
+                    "PCX: Unsupported number of bits per plane %d.\n",
+                    hdr.bitsPerPlane);
+                goto error;
         }
 
         dp += img->pitch;
     }
 
-    // Read VGA palette
-    if (paletted)
+    // Read additional VGA palette, if available
     {
-        int i, ncolors;
+        int ncolors;
         Uint8 tmpb;
         BOOL read;
 
@@ -1125,6 +1128,7 @@
 
         if (read)
         {
+            // Okay, attempt to read the palette data
             if (!dmReadPaletteData(fp, img->pal, ncolors))
             {
                 res = dmError(DMERR_FREAD,
@@ -1134,14 +1138,13 @@
         }
         else
         {
-            for (i = 0; i < img->ncolors; i++)
+            // If the extra palette is not available, copy the colors from
+            // the header palette to our internal palette structure.
+            for (int i = 0; i < (img->ncolors > DMPCX_PAL_COLORS ? DMPCX_PAL_COLORS : img->ncolors); i++)
             {
-                if (i < 16)
-                {
-                    img->pal[i].r = hdr.colormap[i].r;
-                    img->pal[i].g = hdr.colormap[i].g;
-                    img->pal[i].b = hdr.colormap[i].b;
-                }
+                img->pal[i].r = hdr.colorMap[i].r;
+                img->pal[i].g = hdr.colorMap[i].g;
+                img->pal[i].b = hdr.colorMap[i].b;
             }
         }
     }