summary refs log tree commit diff
path: root/bin/glitch.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--bin/glitch.c312
1 files changed, 10 insertions, 302 deletions
diff --git a/bin/glitch.c b/bin/glitch.c
index 34908f15..58a544d7 100644
--- a/bin/glitch.c
+++ b/bin/glitch.c
@@ -27,11 +27,9 @@
 #include <zlib.h>
 
 #define PACKED __attribute__((packed))
-#define PAIR(a, b) ((uint16_t)(a) << 8 | (uint16_t)(b))
 
 #define CRC_INIT (crc32(0, Z_NULL, 0))
 
-static bool verbose;
 static const char *path;
 static FILE *file;
 static uint32_t crc;
@@ -70,10 +68,6 @@ struct PACKED Chunk {
     char type[4];
 };
 
-static bool ancillary(struct Chunk chunk) {
-    return chunk.type[0] & 0x20;
-}
-
 static const char *typeStr(struct Chunk chunk) {
     static char buf[5];
     memcpy(buf, chunk.type, 4);
@@ -129,9 +123,9 @@ static struct PACKED {
         GRAYSCALE_ALPHA = 4,
         TRUECOLOR_ALPHA = 6,
     } color;
-    enum PACKED { DEFLATE } compression;
-    enum PACKED { ADAPTIVE } filter;
-    enum PACKED { PROGRESSIVE, ADAM7 } interlace;
+    uint8_t compression;
+    uint8_t filter;
+    uint8_t interlace;
 } header;
 
 static size_t lineSize(void) {
@@ -149,23 +143,6 @@ static size_t dataSize(void) {
     return (1 + lineSize()) * header.height;
 }
 
-static const char *COLOR_STR[] = {
-    [GRAYSCALE] = "grayscale",
-    [TRUECOLOR] = "truecolor",
-    [INDEXED] = "indexed",
-    [GRAYSCALE_ALPHA] = "grayscale alpha",
-    [TRUECOLOR_ALPHA] = "truecolor alpha",
-};
-static void printHeader(void) {
-    fprintf(
-        stderr,
-        "%s: %ux%u %hhu-bit %s\n",
-        path,
-        header.width, header.height,
-        header.depth, COLOR_STR[header.color]
-    );
-}
-
 static void readHeader(void) {
     struct Chunk ihdr = readChunk();
     if (0 != memcmp(ihdr.type, "IHDR", 4)) {
@@ -185,55 +162,15 @@ static void readHeader(void) {
 
     if (!header.width) errx(EX_DATAERR, "%s: invalid width 0", path);
     if (!header.height) errx(EX_DATAERR, "%s: invalid height 0", path);
-    switch (PAIR(header.color, header.depth)) {
-        case PAIR(GRAYSCALE, 1):
-        case PAIR(GRAYSCALE, 2):
-        case PAIR(GRAYSCALE, 4):
-        case PAIR(GRAYSCALE, 8):
-        case PAIR(GRAYSCALE, 16):
-        case PAIR(TRUECOLOR, 8):
-        case PAIR(TRUECOLOR, 16):
-        case PAIR(INDEXED, 1):
-        case PAIR(INDEXED, 2):
-        case PAIR(INDEXED, 4):
-        case PAIR(INDEXED, 8):
-        case PAIR(GRAYSCALE_ALPHA, 8):
-        case PAIR(GRAYSCALE_ALPHA, 16):
-        case PAIR(TRUECOLOR_ALPHA, 8):
-        case PAIR(TRUECOLOR_ALPHA, 16):
-            break;
-        default:
-            errx(
-                EX_DATAERR, "%s: invalid color type %hhu and bit depth %hhu",
-                path, header.color, header.depth
-            );
-    }
-    if (header.compression != DEFLATE) {
-        errx(
-            EX_DATAERR, "%s: invalid compression method %hhu",
-            path, header.compression
-        );
-    }
-    if (header.filter != ADAPTIVE) {
-        errx(EX_DATAERR, "%s: invalid filter method %hhu", path, header.filter);
-    }
-    if (header.interlace > ADAM7) {
-        errx(EX_DATAERR, "%s: invalid interlace method %hhu", path, header.interlace);
-    }
-
-    if (verbose) printHeader();
 }
 
 static void writeHeader(void) {
-    if (verbose) printHeader();
-
     struct Chunk ihdr = { .size = sizeof(header), .type = "IHDR" };
     writeChunk(ihdr);
     header.width = htonl(header.width);
     header.height = htonl(header.height);
     writeExpect(&header, sizeof(header));
     writeCrc();
-
     header.width = ntohl(header.width);
     header.height = ntohl(header.height);
 }
@@ -243,50 +180,22 @@ static struct {
     uint8_t entries[256][3];
 } palette;
 
-static uint16_t paletteIndex(const uint8_t *rgb) {
-    uint16_t i;
-    for (i = 0; i < palette.len; ++i) {
-        if (0 == memcmp(palette.entries[i], rgb, 3)) break;
-    }
-    return i;
-}
-
-static bool paletteAdd(const uint8_t *rgb) {
-    uint16_t i = paletteIndex(rgb);
-    if (i < palette.len) return true;
-    if (i == 256) return false;
-    memcpy(palette.entries[palette.len++], rgb, 3);
-    return true;
-}
-
 static void readPalette(void) {
     struct Chunk chunk;
     for (;;) {
         chunk = readChunk();
         if (0 == memcmp(chunk.type, "PLTE", 4)) {
             break;
-        } else if (ancillary(chunk)) {
-            skipChunk(chunk);
         } else {
-            errx(
-                EX_DATAERR, "%s: expected PLTE chunk, found %s",
-                path, typeStr(chunk)
-            );
+            skipChunk(chunk);
         }
     }
-    if (chunk.size % 3) {
-        errx(EX_DATAERR, "%s: PLTE size %u not divisible by 3", path, chunk.size);
-    }
-
     palette.len = chunk.size / 3;
     readExpect(palette.entries, chunk.size, "palette data");
     readCrc();
-
-    if (verbose) fprintf(stderr, "%s: palette length %u\n", path, palette.len);
 }
 
 static void writePalette(void) {
-    if (verbose) fprintf(stderr, "%s: palette length %u\n", path, palette.len);
     struct Chunk plte = { .size = 3 * palette.len, .type = "PLTE" };
     writeChunk(plte);
     writeExpect(palette.entries, plte.size);
@@ -301,8 +210,6 @@ static void allocData(void) {
 }
 
 static void readData(void) {
-    if (verbose) fprintf(stderr, "%s: data size %zu\n", path, dataSize());
-
     struct z_stream_s stream = { .next_out = data, .avail_out = dataSize() };
     int error = inflateInit(&stream);
     if (error != Z_OK) errx(EX_SOFTWARE, "%s: inflateInit: %s", path, stream.msg);
@@ -322,13 +229,8 @@ static void readData(void) {
 
         } else if (0 == memcmp(chunk.type, "IEND", 4)) {
             errx(EX_DATAERR, "%s: missing IDAT chunk", path);
-        } else if (ancillary(chunk)) {
-            skipChunk(chunk);
         } else {
-            errx(
-                EX_CONFIG, "%s: unsupported critical chunk %s",
-                path, typeStr(chunk)
-            );
+            skipChunk(chunk);
         }
     }
 
@@ -339,13 +241,9 @@ static void readData(void) {
             path, dataSize(), stream.total_out
         );
     }
-
-    if (verbose) fprintf(stderr, "%s: deflate size %lu\n", path, stream.total_in);
 }
 
 static void writeData(void) {
-    if (verbose) fprintf(stderr, "%s: data size %zu\n", path, dataSize());
-
     uLong size = compressBound(dataSize());
     uint8_t deflate[size];
     int error = compress2(deflate, &size, data, dataSize(), Z_BEST_COMPRESSION);
@@ -355,8 +253,6 @@ static void writeData(void) {
     writeChunk(idat);
     writeExpect(deflate, size);
     writeCrc();
-
-    if (verbose) fprintf(stderr, "%s: deflate size %lu\n", path, size);
 }
 
 static void writeEnd(void) {
@@ -456,7 +352,6 @@ static void reconData(void) {
 }
 
 static void filterData(void) {
-    if (header.color == INDEXED || header.depth < 8) return;
     for (uint32_t y = header.height - 1; y < header.height; --y) {
         uint8_t filter[FILTER_COUNT][lineSize()];
         uint32_t heuristic[FILTER_COUNT] = {0};
@@ -473,183 +368,7 @@ static void filterData(void) {
     }
 }
 
-static void discardAlpha(void) {
-    if (header.color != GRAYSCALE_ALPHA && header.color != TRUECOLOR_ALPHA) return;
-    size_t sampleSize = header.depth / 8;
-    size_t pixelSize = sampleSize * (header.color == GRAYSCALE_ALPHA ? 2 : 4);
-    size_t colorSize = pixelSize - sampleSize;
-    for (uint32_t y = 0; y < header.height; ++y) {
-        for (uint32_t x = 0; x < header.width; ++x) {
-            for (size_t i = 0; i < sampleSize; ++i) {
-                if (lines[y]->data[x * pixelSize + colorSize + i] != 0xFF) return;
-            }
-        }
-    }
-
-    uint8_t *ptr = data;
-    for (uint32_t y = 0; y < header.height; ++y) {
-        *ptr++ = lines[y]->type;
-        for (uint32_t x = 0; x < header.width; ++x) {
-            memmove(ptr, &lines[y]->data[x * pixelSize], colorSize);
-            ptr += colorSize;
-        }
-    }
-    header.color = (header.color == GRAYSCALE_ALPHA) ? GRAYSCALE : TRUECOLOR;
-    scanlines();
-}
-
-static void discardColor(void) {
-    if (header.color != TRUECOLOR && header.color != TRUECOLOR_ALPHA) return;
-    size_t sampleSize = header.depth / 8;
-    size_t pixelSize = sampleSize * (header.color == TRUECOLOR ? 3 : 4);
-    for (uint32_t y = 0; y < header.height; ++y) {
-        for (uint32_t x = 0; x < header.width; ++x) {
-            uint8_t *r = &lines[y]->data[x * pixelSize];
-            uint8_t *g = r + sampleSize;
-            uint8_t *b = g + sampleSize;
-            if (0 != memcmp(r, g, sampleSize)) return;
-            if (0 != memcmp(g, b, sampleSize)) return;
-        }
-    }
-
-    uint8_t *ptr = data;
-    for (uint32_t y = 0; y < header.height; ++y) {
-        *ptr++ = lines[y]->type;
-        for (uint32_t x = 0; x < header.width; ++x) {
-            uint8_t *pixel = &lines[y]->data[x * pixelSize];
-            memmove(ptr, pixel, sampleSize);
-            ptr += sampleSize;
-            if (header.color == TRUECOLOR_ALPHA) {
-                memmove(ptr, pixel + 3 * sampleSize, sampleSize);
-                ptr += sampleSize;
-            }
-        }
-    }
-    header.color = (header.color == TRUECOLOR) ? GRAYSCALE : GRAYSCALE_ALPHA;
-    scanlines();
-}
-
-static void indexColor(void) {
-    if (header.color != TRUECOLOR || header.depth != 8) return;
-    for (uint32_t y = 0; y < header.height; ++y) {
-        for (uint32_t x = 0; x < header.width; ++x) {
-            if (!paletteAdd(&lines[y]->data[x * 3])) return;
-        }
-    }
-
-    uint8_t *ptr = data;
-    for (uint32_t y = 0; y < header.height; ++y) {
-        *ptr++ = lines[y]->type;
-        for (uint32_t x = 0; x < header.width; ++x) {
-            *ptr++ = paletteIndex(&lines[y]->data[x * 3]);
-        }
-    }
-    header.color = INDEXED;
-    scanlines();
-}
-
-static void reduceDepth8(void) {
-    if (header.color != GRAYSCALE && header.color != INDEXED) return;
-    if (header.depth != 8) return;
-    if (header.color == GRAYSCALE) {
-        for (uint32_t y = 0; y < header.height; ++y) {
-            for (size_t i = 0; i < lineSize(); ++i) {
-                uint8_t a = lines[y]->data[i];
-                if ((a >> 4) != (a & 0x0F)) return;
-            }
-        }
-    } else if (palette.len > 16) {
-        return;
-    }
-
-    uint8_t *ptr = data;
-    for (uint32_t y = 0; y < header.height; ++y) {
-        *ptr++ = lines[y]->type;
-        for (size_t i = 0; i < lineSize(); i += 2) {
-            uint8_t iByte = lines[y]->data[i];
-            uint8_t jByte = (i + 1 < lineSize()) ? lines[y]->data[i + 1] : 0;
-            uint8_t a = iByte & 0x0F;
-            uint8_t b = jByte & 0x0F;
-            *ptr++ = a << 4 | b;
-        }
-    }
-    header.depth = 4;
-    scanlines();
-}
-
-static void reduceDepth4(void) {
-    if (header.depth != 4) return;
-    if (header.color == GRAYSCALE) {
-        for (uint32_t y = 0; y < header.height; ++y) {
-            for (size_t i = 0; i < lineSize(); ++i) {
-                uint8_t a = lines[y]->data[i] >> 4;
-                uint8_t b = lines[y]->data[i] & 0x0F;
-                if ((a >> 2) != (a & 0x03)) return;
-                if ((b >> 2) != (b & 0x03)) return;
-            }
-        }
-    } else if (palette.len > 4) {
-        return;
-    }
-
-    uint8_t *ptr = data;
-    for (uint32_t y = 0; y < header.height; ++y) {
-        *ptr++ = lines[y]->type;
-        for (size_t i = 0; i < lineSize(); i += 2) {
-            uint8_t iByte = lines[y]->data[i];
-            uint8_t jByte = (i + 1 < lineSize()) ? lines[y]->data[i + 1] : 0;
-            uint8_t a = iByte >> 4 & 0x03, b = iByte & 0x03;
-            uint8_t c = jByte >> 4 & 0x03, d = jByte & 0x03;
-            *ptr++ = a << 6 | b << 4 | c << 2 | d;
-        }
-    }
-    header.depth = 2;
-    scanlines();
-}
-
-static void reduceDepth2(void) {
-    if (header.depth != 2) return;
-    if (header.color == GRAYSCALE) {
-        for (uint32_t y = 0; y < header.height; ++y) {
-            for (size_t i = 0; i < lineSize(); ++i) {
-                uint8_t a = lines[y]->data[i] >> 6;
-                uint8_t b = lines[y]->data[i] >> 4 & 0x03;
-                uint8_t c = lines[y]->data[i] >> 2 & 0x03;
-                uint8_t d = lines[y]->data[i] & 0x03;
-                if ((a >> 1) != (a & 0x01)) return;
-                if ((b >> 1) != (b & 0x01)) return;
-                if ((c >> 1) != (c & 0x01)) return;
-                if ((d >> 1) != (d & 0x01)) return;
-            }
-        }
-    } else if (palette.len > 2) {
-        return;
-    }
-
-    uint8_t *ptr = data;
-    for (uint32_t y = 0; y < header.height; ++y) {
-        *ptr++ = lines[y]->type;
-        for (size_t i = 0; i < lineSize(); i += 2) {
-            uint8_t iByte = lines[y]->data[i];
-            uint8_t jByte = (i + 1 < lineSize()) ? lines[y]->data[i + 1] : 0;
-            uint8_t a = iByte >> 6 & 0x01, b = iByte >> 4 & 0x01;
-            uint8_t c = iByte >> 2 & 0x01, d = iByte & 0x01;
-            uint8_t e = jByte >> 6 & 0x01, f = jByte >> 4 & 0x01;
-            uint8_t g = jByte >> 2 & 0x01, h = jByte & 0x01;
-            *ptr++ = a << 7 | b << 6 | c << 5 | d << 4 | e << 3 | f << 2 | g << 1 | h;
-        }
-    }
-    header.depth = 1;
-    scanlines();
-}
-
-static void reduceDepth(void) {
-    reduceDepth8();
-    reduceDepth4();
-    reduceDepth2();
-}
-
-static void optimize(const char *inPath, const char *outPath) {
+static void glitch(const char *inPath, const char *outPath) {
     if (inPath) {
         path = inPath;
         file = fopen(path, "r");
@@ -661,12 +380,6 @@ static void optimize(const char *inPath, const char *outPath) {
 
     readSignature();
     readHeader();
-    if (header.interlace != PROGRESSIVE) {
-        errx(
-            EX_CONFIG, "%s: unsupported interlace method %hhu",
-            path, header.interlace
-        );
-    }
     if (header.color == INDEXED) readPalette();
     allocData();
     readData();
@@ -676,10 +389,6 @@ static void optimize(const char *inPath, const char *outPath) {
     scanlines();
     reconData();
 
-    discardAlpha();
-    discardColor();
-    indexColor();
-    reduceDepth();
     filterData();
     free(lines);
 
@@ -708,23 +417,22 @@ int main(int argc, char *argv[]) {
     char *output = NULL;
 
     int opt;
-    while (0 < (opt = getopt(argc, argv, "co:v"))) {
+    while (0 < (opt = getopt(argc, argv, "co:"))) {
         switch (opt) {
             case 'c': stdio = true; break;
             case 'o': output = optarg; break;
-            case 'v': verbose = true; break;
             default: return EX_USAGE;
         }
     }
 
     if (argc - optind == 1 && (output || stdio)) {
-        optimize(argv[optind], output);
+        glitch(argv[optind], output);
     } else if (optind < argc) {
         for (int i = optind; i < argc; ++i) {
-            optimize(argv[i], argv[i]);
+            glitch(argv[i], argv[i]);
         }
     } else {
-        optimize(NULL, output);
+        glitch(NULL, output);
     }
 
     return EX_OK;