about summary refs log tree commit diff
path: root/converter
diff options
context:
space:
mode:
Diffstat (limited to 'converter')
-rw-r--r--converter/other/pamtoqoi.c6
-rw-r--r--converter/other/qoi.c315
-rw-r--r--converter/other/qoi.h18
-rw-r--r--converter/other/qoitopam.c10
4 files changed, 191 insertions, 158 deletions
diff --git a/converter/other/pamtoqoi.c b/converter/other/pamtoqoi.c
index 13448f19..6bad6a90 100644
--- a/converter/other/pamtoqoi.c
+++ b/converter/other/pamtoqoi.c
@@ -139,7 +139,7 @@ main(int argc, char **argv) {
     qoi_Desc qoiDesc;
     tuplen * tuplerown;
     unsigned char * qoiRaster;
-    unsigned char * qoiImage;
+    const unsigned char * qoiImage;
     size_t qoiSz;
     unsigned int row;
 
@@ -169,11 +169,11 @@ main(int argc, char **argv) {
              inpam.width,
              &qoiRaster[row * inpam.width * qoiDesc.channelCt]);
     }
-    qoiImage = qoi_encode(qoiRaster, &qoiDesc, &qoiSz);
+    qoi_encode(qoiRaster, &qoiDesc, &qoiImage, &qoiSz);
 
     pm_writefile(stdout, qoiImage, qoiSz);
 
-    free(qoiImage);
+    free((void*)qoiImage);
     free(qoiRaster);
     pnm_freepamrown(tuplerown);
 
diff --git a/converter/other/qoi.c b/converter/other/qoi.c
index abc59564..8919bb03 100644
--- a/converter/other/qoi.c
+++ b/converter/other/qoi.c
@@ -1,15 +1,12 @@
+#include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include "pm.h"
+#include "mallocvar.h"
 #include "qoi.h"
 
-#ifndef QOI_MALLOC
-    #define QOI_MALLOC(sz) malloc(sz)
-    #define QOI_FREE(p)    free(p)
-#endif
-#ifndef QOI_ZEROARR
-    #define QOI_ZEROARR(a) memset((a),0,sizeof(a))
-#endif
+#define ZEROARRAY(a) memset((a),0,sizeof(a))
 
 #define QOI_OP_INDEX  0x00 /* 00xxxxxx */
 #define QOI_OP_DIFF   0x40 /* 01xxxxxx */
@@ -42,69 +39,128 @@ static unsigned char const padding[8] = {0,0,0,0,0,0,0,1};
 
 static void
 write32(unsigned char * const bytes,
-        int *           const p,
+        size_t *        const cursorP,
         unsigned int    const v) {
 
-    bytes[(*p)++] = (0xff000000 & v) >> 24;
-    bytes[(*p)++] = (0x00ff0000 & v) >> 16;
-    bytes[(*p)++] = (0x0000ff00 & v) >> 8;
-    bytes[(*p)++] = (0x000000ff & v);
+    bytes[(*cursorP)++] = (0xff000000 & v) >> 24;
+    bytes[(*cursorP)++] = (0x00ff0000 & v) >> 16;
+    bytes[(*cursorP)++] = (0x0000ff00 & v) >> 8;
+    bytes[(*cursorP)++] = (0x000000ff & v);
 }
 
 
 
 static unsigned int
-read32(const unsigned char *bytes, int *p) {
+read32(const unsigned char * const bytes,
+       size_t *              const cursorP) {
+
+    unsigned int a = bytes[(*cursorP)++];
+    unsigned int b = bytes[(*cursorP)++];
+    unsigned int c = bytes[(*cursorP)++];
+    unsigned int d = bytes[(*cursorP)++];
 
-    unsigned int a = bytes[(*p)++];
-    unsigned int b = bytes[(*p)++];
-    unsigned int c = bytes[(*p)++];
-    unsigned int d = bytes[(*p)++];
     return a << 24 | b << 16 | c << 8 | d;
 }
 
 
 
-void *
-qoi_encode(const void *     const data,
-           const qoi_Desc * const descP,
-           size_t *         const outLenP) {
+static void
+encodeQoiHeader(unsigned char * const bytes,
+                qoi_Desc        const qoiDesc,
+                size_t *        const cursorP) {
+
+    write32(bytes, cursorP, QOI_MAGIC);
+    write32(bytes, cursorP, qoiDesc.width);
+    write32(bytes, cursorP, qoiDesc.height);
+    bytes[(*cursorP)++] = qoiDesc.channelCt;
+    bytes[(*cursorP)++] = qoiDesc.colorspace;
+}
+
+
+
+static void
+encodeNewPixel(Rgba            const px,
+               Rgba            const pxPrev,
+               unsigned char * const bytes,
+               size_t *        const cursorP) {
+
+    if (px.rgba.a == pxPrev.rgba.a) {
+        signed char const vr = px.rgba.r - pxPrev.rgba.r;
+        signed char const vg = px.rgba.g - pxPrev.rgba.g;
+        signed char const vb = px.rgba.b - pxPrev.rgba.b;
+
+        signed char const vgR = vr - vg;
+        signed char const vgB = vb - vg;
+
+        if (
+            vr > -3 && vr < 2 &&
+            vg > -3 && vg < 2 &&
+            vb > -3 && vb < 2
+            ) {
+            bytes[(*cursorP)++] = QOI_OP_DIFF |
+                (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
+        } else if (
+            vgR >  -9 && vgR <  8 &&
+            vg  > -33 && vg  < 32 &&
+            vgB >  -9 && vgB <  8
+            ) {
+            bytes[(*cursorP)++] = QOI_OP_LUMA     | (vg   + 32);
+            bytes[(*cursorP)++] = (vgR + 8) << 4 | (vgB +  8);
+        } else {
+            bytes[(*cursorP)++] = QOI_OP_RGB;
+            bytes[(*cursorP)++] = px.rgba.r;
+            bytes[(*cursorP)++] = px.rgba.g;
+            bytes[(*cursorP)++] = px.rgba.b;
+        }
+    } else {
+        bytes[(*cursorP)++] = QOI_OP_RGBA;
+        bytes[(*cursorP)++] = px.rgba.r;
+        bytes[(*cursorP)++] = px.rgba.g;
+        bytes[(*cursorP)++] = px.rgba.b;
+        bytes[(*cursorP)++] = px.rgba.a;
+    }
+}
+
+
+
+void
+qoi_encode(const unsigned char *  const pixels,
+           const qoi_Desc *       const descP,
+           const unsigned char ** const qoiImageP,
+           size_t *               const outLenP) {
 
-    int p;
+    size_t cursor;
     unsigned int i, maxSize, run;
-    unsigned int pxLen, pxEnd, pxPos, channelCt;
+    unsigned int pxLen, pxEnd, pxPos;
     unsigned char * bytes;
-    const unsigned char * pixels;
     Rgba index[64];
     Rgba px, pxPrev;
 
-    if (data == NULL || outLenP == NULL || descP == NULL ||
-        descP->width == 0 || descP->height == 0 ||
-        descP->channelCt < 3 || descP->channelCt > 4 ||
-        descP->colorspace > 1 ||
-        descP->height >= QOI_PIXELS_MAX / descP->width) {
+    assert(pixels);
+    assert(descP);
+    assert(outLenP);
+    assert(descP->width > 0);
+    assert(descP->height > 0);
+    assert(descP->channelCt >= 3 && descP->channelCt <= 4);
+    assert(descP->colorspace == QOI_SRGB || descP->colorspace == QOI_LINEAR);
 
-        return NULL;
-    }
+    if (descP->height >= QOI_PIXELS_MAX / descP->width)
+        pm_error("Too many pixles for OQI: %u x %u (max is %u",
+                 descP->height, descP->width, QOI_PIXELS_MAX);
 
     maxSize =
         descP->width * descP->height * (descP->channelCt + 1) +
         QOI_HEADER_SIZE + sizeof(padding);
 
-    p = 0;
-    bytes = (unsigned char *) QOI_MALLOC(maxSize);
+    MALLOCARRAY(bytes, maxSize);
     if (!bytes)
-        return NULL;
+        pm_error("Cannot allocate %u bytes", maxSize);
 
-    write32(bytes, &p, QOI_MAGIC);
-    write32(bytes, &p, descP->width);
-    write32(bytes, &p, descP->height);
-    bytes[p++] = descP->channelCt;
-    bytes[p++] = descP->colorspace;
+    cursor = 0;
 
-    pixels = (const unsigned char *)data;
+    encodeQoiHeader(bytes, *descP, &cursor);
 
-    QOI_ZEROARR(index);
+    ZEROARRAY(index);
 
     run = 0;
     pxPrev.rgba.r = 0;
@@ -115,153 +171,136 @@ qoi_encode(const void *     const data,
 
     pxLen = descP->width * descP->height * descP->channelCt;
     pxEnd = pxLen - descP->channelCt;
-    channelCt = descP->channelCt;
 
-    for (pxPos = 0; pxPos < pxLen; pxPos += channelCt) {
+    for (pxPos = 0; pxPos < pxLen; pxPos += descP->channelCt) {
         px.rgba.r = pixels[pxPos + 0];
         px.rgba.g = pixels[pxPos + 1];
         px.rgba.b = pixels[pxPos + 2];
 
-        if (channelCt == 4) {
+        if (descP->channelCt == 4) {
             px.rgba.a = pixels[pxPos + 3];
         }
 
         if (px.v == pxPrev.v) {
             ++run;
             if (run == 62 || pxPos == pxEnd) {
-                bytes[p++] = QOI_OP_RUN | (run - 1);
+                bytes[cursor++] = QOI_OP_RUN | (run - 1);
                 run = 0;
             }
         } else {
-            unsigned int indexPos;
+            unsigned int const indexPos = QOI_COLOR_HASH(px) % 64;
 
             if (run > 0) {
-                bytes[p++] = QOI_OP_RUN | (run - 1);
+                bytes[cursor++] = QOI_OP_RUN | (run - 1);
                 run = 0;
             }
 
-            indexPos = QOI_COLOR_HASH(px) % 64;
-
             if (index[indexPos].v == px.v) {
-                bytes[p++] = QOI_OP_INDEX | indexPos;
+                bytes[cursor++] = QOI_OP_INDEX | indexPos;
             } else {
                 index[indexPos] = px;
 
-                if (px.rgba.a == pxPrev.rgba.a) {
-                    signed char vr = px.rgba.r - pxPrev.rgba.r;
-                    signed char vg = px.rgba.g - pxPrev.rgba.g;
-                    signed char vb = px.rgba.b - pxPrev.rgba.b;
-
-                    signed char vgR = vr - vg;
-                    signed char vgB = vb - vg;
-
-                    if (
-                        vr > -3 && vr < 2 &&
-                        vg > -3 && vg < 2 &&
-                        vb > -3 && vb < 2
-                    ) {
-                        bytes[p++] = QOI_OP_DIFF |
-                            (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
-                    } else if (
-                        vgR >  -9 && vgR <  8 &&
-                        vg  > -33 && vg  < 32 &&
-                        vgB >  -9 && vgB <  8
-                    ) {
-                        bytes[p++] = QOI_OP_LUMA     | (vg   + 32);
-                        bytes[p++] = (vgR + 8) << 4 | (vgB +  8);
-                    } else {
-                        bytes[p++] = QOI_OP_RGB;
-                        bytes[p++] = px.rgba.r;
-                        bytes[p++] = px.rgba.g;
-                        bytes[p++] = px.rgba.b;
-                    }
-                } else {
-                    bytes[p++] = QOI_OP_RGBA;
-                    bytes[p++] = px.rgba.r;
-                    bytes[p++] = px.rgba.g;
-                    bytes[p++] = px.rgba.b;
-                    bytes[p++] = px.rgba.a;
-                }
+                encodeNewPixel(px, pxPrev, bytes, &cursor);
             }
         }
         pxPrev = px;
     }
 
-    for (i = 0; i < (int)sizeof(padding); ++i)
-        bytes[p++] = padding[i];
+    for (i = 0; i < sizeof(padding); ++i)
+        bytes[cursor++] = padding[i];
 
-    *outLenP = p;
+    *qoiImageP = bytes;
+    *outLenP   = cursor;
+}
 
-    return bytes;
+
+
+static void
+decodeQoiHeader(const unsigned char * const qoiImage,
+                qoi_Desc *            const qoiDescP,
+                size_t *              const cursorP) {
+
+    unsigned int headerMagic;
+
+    headerMagic          = read32(qoiImage, cursorP);
+    qoiDescP->width      = read32(qoiImage, cursorP);
+    qoiDescP->height     = read32(qoiImage, cursorP);
+    qoiDescP->channelCt  = qoiImage[(*cursorP)++];
+    qoiDescP->colorspace = qoiImage[(*cursorP)++];
+
+    if (qoiDescP->width == 0)
+        pm_error("Invalid QOI image: width is zero");
+    if (qoiDescP->height == 0)
+        pm_error("Invalid QOI image: height is zero");
+    if (qoiDescP->channelCt != 3 && qoiDescP->channelCt != 4)
+        pm_error("Invalid QOI image: channel count is %u.  "
+                 "Only 3 and 4 are valid", qoiDescP->channelCt);
+    if (qoiDescP->colorspace != QOI_SRGB && qoiDescP->colorspace != QOI_LINEAR)
+        pm_error("Invalid QOI image: colorspace code is %u.  "
+                 "Only %u (SRGB) and %u (LINEAR) are valid",
+                 qoiDescP->colorspace, QOI_SRGB, QOI_LINEAR);
+    if (headerMagic != QOI_MAGIC)
+        pm_error("Invalid QOI image: Where the magic number 0x%04x "
+                 "should be, there is 0x%04x",
+                 QOI_MAGIC, headerMagic);
+    if (qoiDescP->height >= QOI_PIXELS_MAX / qoiDescP->width)
+        pm_error ("Invalid QOI image: %u x %u is More than %u pixels",
+                  qoiDescP->width, qoiDescP->height, QOI_PIXELS_MAX);
 }
 
 
 
-void *
-qoi_decode(const void * const data,
-           size_t       const size,
-           qoi_Desc *   const descP) {
+void
+qoi_decode(const unsigned char *  const qoiImage,
+           size_t                 const size,
+           qoi_Desc *             const qoiDescP,
+           const unsigned char ** const qoiRasterP) {
+
+    unsigned int const chunksLen = size - sizeof(padding);
 
-    const unsigned char * bytes;
-    unsigned int header_magic;
     unsigned char * pixels;
     Rgba index[64];
     Rgba px;
-    unsigned int pxLen, chunksLen, pxPos;
-    int p;
+    unsigned int pxLen, pxPos;
+    size_t cursor;
     unsigned int run;
 
-    if (data == NULL || descP == NULL ||
-        size < QOI_HEADER_SIZE + (int)sizeof(padding)) {
-        return NULL;
-    }
+    assert(qoiImage);
+    assert(qoiDescP);
+    assert(size >= QOI_HEADER_SIZE + sizeof(padding));
 
-    bytes = (const unsigned char *)data;
-
-    header_magic      = read32(bytes, &p);
-    descP->width      = read32(bytes, &p);
-    descP->height     = read32(bytes, &p);
-    descP->channelCt  = bytes[p++];
-    descP->colorspace = bytes[p++];
-
-    if (
-        descP->width == 0 || descP->height == 0 ||
-        descP->channelCt < 3 || descP->channelCt > 4 ||
-        descP->colorspace > 1 ||
-        header_magic != QOI_MAGIC ||
-        descP->height >= QOI_PIXELS_MAX / descP->width
-    ) {
-        return NULL;
-    }
+    cursor = 0;
 
-    pxLen = descP->width * descP->height * descP->channelCt;
-    pixels = (unsigned char *) QOI_MALLOC(pxLen);
-    if (!pixels) {
-        return NULL;
-    }
+    decodeQoiHeader(qoiImage, qoiDescP, &cursor);
+
+    pxLen = qoiDescP->width * qoiDescP->height * qoiDescP->channelCt;
+    MALLOCARRAY(pixels, pxLen);
+    if (!pixels)
+        pm_error("Failed to allocate %u bytes for %u x %u x %u QOI raster",
+                 pxLen,
+                 qoiDescP->width, qoiDescP->height, qoiDescP->channelCt);
 
-    QOI_ZEROARR(index);
+    ZEROARRAY(index);
     px.rgba.r = 0;
     px.rgba.g = 0;
     px.rgba.b = 0;
     px.rgba.a = 255;
 
-    chunksLen = size - sizeof(padding);
-    for (pxPos = 0, run = 0; pxPos < pxLen; pxPos += descP->channelCt) {
+    for (pxPos = 0, run = 0; pxPos < pxLen; pxPos += qoiDescP->channelCt) {
         if (run > 0) {
             --run;
-        } else if (p < chunksLen) {
-            unsigned char const b1 = bytes[p++];
+        } else if (cursor < chunksLen) {
+            unsigned char const b1 = qoiImage[cursor++];
 
             if (b1 == QOI_OP_RGB) {
-                px.rgba.r = bytes[p++];
-                px.rgba.g = bytes[p++];
-                px.rgba.b = bytes[p++];
+                px.rgba.r = qoiImage[cursor++];
+                px.rgba.g = qoiImage[cursor++];
+                px.rgba.b = qoiImage[cursor++];
             } else if (b1 == QOI_OP_RGBA) {
-                px.rgba.r = bytes[p++];
-                px.rgba.g = bytes[p++];
-                px.rgba.b = bytes[p++];
-                px.rgba.a = bytes[p++];
+                px.rgba.r = qoiImage[cursor++];
+                px.rgba.g = qoiImage[cursor++];
+                px.rgba.b = qoiImage[cursor++];
+                px.rgba.a = qoiImage[cursor++];
             } else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) {
                 px = index[b1];
             } else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) {
@@ -269,7 +308,7 @@ qoi_decode(const void * const data,
                 px.rgba.g += ((b1 >> 2) & 0x03) - 2;
                 px.rgba.b += ( b1       & 0x03) - 2;
             } else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) {
-                unsigned char const b2 = bytes[p++];
+                unsigned char const b2 = qoiImage[cursor++];
                 unsigned char const vg = (b1 & 0x3f) - 32;
                 px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
                 px.rgba.g += vg;
@@ -285,11 +324,11 @@ qoi_decode(const void * const data,
         pixels[pxPos + 1] = px.rgba.g;
         pixels[pxPos + 2] = px.rgba.b;
 
-        if (descP->channelCt == 4)
+        if (qoiDescP->channelCt == 4)
             pixels[pxPos + 3] = px.rgba.a;
     }
 
-    return pixels;
+    *qoiRasterP = pixels;
 }
 
 
diff --git a/converter/other/qoi.h b/converter/other/qoi.h
index 4fee4530..2886a777 100644
--- a/converter/other/qoi.h
+++ b/converter/other/qoi.h
@@ -256,10 +256,11 @@ typedef struct {
    The returned qoi data should be free()d after use.
 */
 
-void *
-qoi_encode(const void *     const data,
-           const qoi_Desc * const descP,
-           size_t *         const outLenP);
+void
+qoi_encode(const unsigned char *  const data,
+           const qoi_Desc *       const descP,
+           const unsigned char ** const qoiImageP,
+           size_t *               const outLenP);
 
 /* Decode a QOI image from memory.
 
@@ -270,9 +271,10 @@ qoi_encode(const void *     const data,
    The returned pixel data should be free()d after use.
 */
 
-void *
-qoi_decode(const void * const data,
-           size_t       const size,
-           qoi_Desc *   const descP);
+void
+qoi_decode(const unsigned char *  const qoiImage,
+           size_t                 const size,
+           qoi_Desc *             const descP,
+           const unsigned char ** const qoiRasterP);
 
 #endif
diff --git a/converter/other/qoitopam.c b/converter/other/qoitopam.c
index 1998e507..cb9fd925 100644
--- a/converter/other/qoitopam.c
+++ b/converter/other/qoitopam.c
@@ -36,8 +36,6 @@ readQoi(FILE *                 const ifP,
 
     size_t qoiSz;
     const unsigned char * qoiImg;
-    unsigned char * qoiRaster;
-    qoi_Desc qoiDesc;
 
     /* Unfortunately, qoi.h does not implement a streaming decoder,
        we need to read the whole stream into memory -- expensive.
@@ -46,13 +44,7 @@ readQoi(FILE *                 const ifP,
     */
     pm_readfile(stdin, &qoiImg, &qoiSz);
 
-    qoiRaster = qoi_decode(qoiImg, qoiSz, &qoiDesc);
-
-    if (!qoiRaster)
-        pm_error("Decoding qoi failed.");
-
-    *qoiDescP = qoiDesc;
-    *qoiRasterP = qoiRaster;
+    qoi_decode(qoiImg, qoiSz, qoiDescP, qoiRasterP);
 
     free((void*)qoiImg);
 }