summary refs log tree commit diff
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to 'bin')
-rw-r--r--bin/pngo.c283
1 files changed, 136 insertions, 147 deletions
diff --git a/bin/pngo.c b/bin/pngo.c
index 96695ad5..70692c20 100644
--- a/bin/pngo.c
+++ b/bin/pngo.c
@@ -30,28 +30,39 @@
 
 #define CRC_INIT (crc32(0, Z_NULL, 0))
 
-static void readExpect(
-    const char *path, FILE *file,
-    void *ptr, size_t size,
-    const char *expect
-) {
+static const char *path;
+static FILE *file;
+static uint32_t crc;
+
+static void readExpect(void *ptr, size_t size, const char *expect) {
     fread(ptr, size, 1, file);
     if (ferror(file)) err(EX_IOERR, "%s", path);
     if (feof(file)) errx(EX_DATAERR, "%s: missing %s", path, expect);
+    crc = crc32(crc, ptr, size);
+}
+
+static void writeExpect(const void *ptr, size_t size) {
+    fwrite(ptr, size, 1, file);
+    if (ferror(file)) err(EX_IOERR, "%s", path);
+    crc = crc32(crc, ptr, size);
 }
 
 static const uint8_t SIGNATURE[8] = {
     0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n'
 };
 
-static void readSignature(const char *path, FILE *file) {
+static void readSignature(void) {
     uint8_t signature[8];
-    readExpect(path, file, signature, 8, "signature");
+    readExpect(signature, 8, "signature");
     if (0 != memcmp(signature, SIGNATURE, 8)) {
         errx(EX_DATAERR, "%s: invalid signature", path);
     }
 }
 
+static void writeSignature(void) {
+    writeExpect(SIGNATURE, sizeof(SIGNATURE));
+}
+
 struct PACKED Chunk {
     uint32_t size;
     char type[4];
@@ -63,16 +74,24 @@ static const char *typeStr(struct Chunk chunk) {
     return buf;
 }
 
-static struct Chunk readChunk(const char *path, FILE *file) {
+static struct Chunk readChunk(void) {
     struct Chunk chunk;
-    readExpect(path, file, &chunk, sizeof(chunk), "chunk");
+    readExpect(&chunk, sizeof(chunk), "chunk");
     chunk.size = ntohl(chunk.size);
+    crc = crc32(CRC_INIT, (Bytef *)chunk.type, sizeof(chunk.type));
     return chunk;
 }
 
-static void readCrc(const char *path, FILE *file, uint32_t expected) {
+static void writeChunk(struct Chunk chunk) {
+    chunk.size = htonl(chunk.size);
+    writeExpect(&chunk, sizeof(chunk));
+    crc = crc32(CRC_INIT, (Bytef *)chunk.type, sizeof(chunk.type));
+}
+
+static void readCrc(void) {
+    uint32_t expected = crc;
     uint32_t found;
-    readExpect(path, file, &found, sizeof(found), "CRC32");
+    readExpect(&found, sizeof(found), "CRC32");
     found = ntohl(found);
     if (found != expected) {
         errx(
@@ -82,6 +101,11 @@ static void readCrc(const char *path, FILE *file, uint32_t expected) {
     }
 }
 
+static void writeCrc(void) {
+    uint32_t net = htonl(crc);
+    writeExpect(&net, sizeof(net));
+}
+
 enum PACKED Color {
     GRAYSCALE       = 0,
     TRUECOLOR       = 2,
@@ -91,7 +115,7 @@ enum PACKED Color {
 };
 #define ALPHA (0x04)
 
-struct PACKED Header {
+static struct PACKED {
     uint32_t width;
     uint32_t height;
     uint8_t depth;
@@ -99,9 +123,9 @@ struct PACKED Header {
     enum PACKED { DEFLATE } compression;
     enum PACKED { ADAPTIVE } filter;
     enum PACKED { PROGRESSIVE, ADAM7 } interlace;
-};
+} header;
 
-static size_t lineSize(struct Header header) {
+static size_t lineSize(void) {
     switch (header.color) {
         case GRAYSCALE:       return (header.width * 1 * header.depth + 7) / 8;
         case TRUECOLOR:       return (header.width * 3 * header.depth + 7) / 8;
@@ -111,26 +135,23 @@ static size_t lineSize(struct Header header) {
     }
 }
 
-static size_t dataSize(struct Header header) {
-    return (1 + lineSize(header)) * header.height;
+static size_t dataSize(void) {
+    return (1 + lineSize()) * header.height;
 }
 
-static struct Header readHeader(const char *path, FILE *file) {
-    struct Chunk ihdr = readChunk(path, file);
+static void readHeader(void) {
+    struct Chunk ihdr = readChunk();
     if (0 != memcmp(ihdr.type, "IHDR", 4)) {
         errx(EX_DATAERR, "%s: expected IHDR, found %s", path, typeStr(ihdr));
     }
-    if (ihdr.size != sizeof(struct Header)) {
+    if (ihdr.size != sizeof(header)) {
         errx(
             EX_DATAERR, "%s: expected IHDR size %zu, found %u",
-            path, sizeof(struct Header), ihdr.size
+            path, sizeof(header), ihdr.size
         );
     }
-    uint32_t crc = crc32(CRC_INIT, (Bytef *)ihdr.type, sizeof(ihdr.type));
-
-    struct Header header;
-    readExpect(path, file, &header, sizeof(header), "header");
-    readCrc(path, file, crc32(crc, (Bytef *)&header, sizeof(header)));
+    readExpect(&header, sizeof(header), "header");
+    readCrc();
 
     header.width = ntohl(header.width);
     header.height = ntohl(header.height);
@@ -161,20 +182,31 @@ static struct Header readHeader(const char *path, FILE *file) {
     if (header.interlace > ADAM7) {
         errx(EX_DATAERR, "%s: invalid interlace method %hhu", path, header.interlace);
     }
+}
 
-    return header;
+static void writeHeader(void) {
+    struct Chunk ihdr = { .size = sizeof(header), .type = { 'I', 'H', 'D', 'R' } };
+    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);
 }
 
-static uint8_t *readData(const char *path, FILE *file, struct Header header) {
-    uint8_t *data = malloc(dataSize(header));
-    if (!data) err(EX_OSERR, "malloc(%zu)", dataSize(header));
+static uint8_t *data;
+
+static void readData(void) {
+    data = malloc(dataSize());
+    if (!data) err(EX_OSERR, "malloc(%zu)", dataSize());
 
-    struct z_stream_s stream = { .next_out = data, .avail_out = dataSize(header) };
+    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);
 
     for (;;) {
-        struct Chunk chunk = readChunk(path, file);
+        struct Chunk chunk = readChunk();
         if (0 == memcmp(chunk.type, "IEND", 4)) {
             errx(EX_DATAERR, "%s: missing IDAT chunk", path);
         }
@@ -190,11 +222,9 @@ static uint8_t *readData(const char *path, FILE *file, struct Header header) {
             );
         }
 
-        uint32_t crc = crc32(CRC_INIT, (Bytef *)chunk.type, sizeof(chunk.type));
-
         uint8_t idat[chunk.size];
-        readExpect(path, file, idat, sizeof(idat), "image data");
-        readCrc(path, file, crc32(crc, idat, sizeof(idat)));
+        readExpect(idat, sizeof(idat), "image data");
+        readCrc();
 
         stream.next_in = idat;
         stream.avail_in = chunk.size;
@@ -204,14 +234,30 @@ static uint8_t *readData(const char *path, FILE *file, struct Header header) {
     }
     inflateEnd(&stream);
 
-    if (stream.total_out != dataSize(header)) {
+    if (stream.total_out != dataSize()) {
         errx(
             EX_DATAERR, "%s: expected data size %zu, found %zu",
-            path, dataSize(header), stream.total_out
+            path, dataSize(), stream.total_out
         );
     }
+}
 
-    return data;
+static void writeData(void) {
+    size_t size = compressBound(dataSize());
+    uint8_t deflate[size];
+    int error = compress2(deflate, &size, data, dataSize(), Z_BEST_COMPRESSION);
+    if (error != Z_OK) errx(EX_SOFTWARE, "%s: compress2: %d", path, error);
+
+    struct Chunk idat = { .size = size, .type = { 'I', 'D', 'A', 'T' } };
+    writeChunk(idat);
+    writeExpect(deflate, size);
+    writeCrc();
+}
+
+static void writeEnd(void) {
+    struct Chunk iend = { .size = 0, .type = { 'I', 'E', 'N', 'D' } };
+    writeChunk(iend);
+    writeCrc();
 }
 
 enum PACKED Filter {
@@ -260,18 +306,16 @@ static uint8_t filt(enum Filter type, struct Bytes f) {
     }
 }
 
-struct Scanline {
+static struct {
     enum Filter *type;
     uint8_t *data;
-};
+} *lines;
 
-static struct Scanline *scanlines(
-    const char *path, struct Header header, uint8_t *data
-) {
-    struct Scanline *lines = calloc(header.height, sizeof(*lines));
+static void scanlines(void) {
+    lines = calloc(header.height, sizeof(*lines));
     if (!lines) err(EX_OSERR, "calloc(%u, %zu)", header.height, sizeof(*lines));
 
-    size_t stride = 1 + lineSize(header);
+    size_t stride = 1 + lineSize();
     for (uint32_t y = 0; y < header.height; ++y) {
         lines[y].type = &data[y * stride];
         lines[y].data = &data[y * stride + 1];
@@ -279,15 +323,10 @@ static struct Scanline *scanlines(
             errx(EX_DATAERR, "%s: invalid filter type %hhu", path, *lines[y].type);
         }
     }
-
-    return lines;
 }
 
-static struct Bytes origBytes(
-    struct Header header, const struct Scanline *lines,
-    uint32_t y, size_t i
-) {
-    size_t pixelSize = lineSize(header) / header.width;
+static struct Bytes origBytes(uint32_t y, size_t i) {
+    size_t pixelSize = lineSize() / header.width;
     if (!pixelSize) pixelSize = 1;
     bool a = (i >= pixelSize), b = (y > 0), c = (a && b);
     return (struct Bytes) {
@@ -298,92 +337,42 @@ static struct Bytes origBytes(
     };
 }
 
-static void reconData(struct Header header, const struct Scanline *lines) {
+static void reconData(void) {
     for (uint32_t y = 0; y < header.height; ++y) {
-        for (size_t i = 0; i < lineSize(header); ++i) {
+        for (size_t i = 0; i < lineSize(); ++i) {
             lines[y].data[i] =
-                recon(*lines[y].type, origBytes(header, lines, y, i));
+                recon(*lines[y].type, origBytes(y, i));
         }
         *lines[y].type = NONE;
     }
 }
 
-static void filterData(struct Header header, const struct Scanline *lines) {
+static void filterData(void) {
     for (uint32_t y = header.height - 1; y < header.height; --y) {
-        uint8_t filter[FILTER_COUNT][lineSize(header)];
+        uint8_t filter[FILTER_COUNT][lineSize()];
         uint32_t heuristic[FILTER_COUNT] = { 0 };
         enum Filter minType = NONE;
         for (enum Filter type = NONE; type < FILTER_COUNT; ++type) {
-            for (uint32_t i = 0; i < lineSize(header); ++i) {
-                filter[type][i] = filt(type, origBytes(header, lines, y, i));
+            for (uint32_t i = 0; i < lineSize(); ++i) {
+                filter[type][i] = filt(type, origBytes(y, i));
                 heuristic[type] += abs((int8_t)filter[type][i]);
             }
             if (heuristic[type] < heuristic[minType]) minType = type;
         }
         *lines[y].type = minType;
-        memcpy(lines[y].data, filter[minType], lineSize(header));
+        memcpy(lines[y].data, filter[minType], lineSize());
     }
 }
 
-static void writeExpect(const char *path, FILE *file, const void *ptr, size_t size) {
-    fwrite(ptr, size, 1, file);
-    if (ferror(file)) err(EX_IOERR, "%s", path);
-}
-
-static void writeSignature(const char *path, FILE *file) {
-    writeExpect(path, file, SIGNATURE, sizeof(SIGNATURE));
-}
-
-static void writeChunk(const char *path, FILE *file, struct Chunk chunk) {
-    chunk.size = htonl(chunk.size);
-    writeExpect(path, file, &chunk, sizeof(chunk));
-}
-
-static void writeCrc(const char *path, FILE *file, uint32_t crc) {
-    uint32_t net = htonl(crc);
-    writeExpect(path, file, &net, sizeof(net));
-}
-
-static void writeHeader(const char *path, FILE *file, struct Header header) {
-    struct Chunk ihdr = { .size = sizeof(header), .type = { 'I', 'H', 'D', 'R' } };
-    writeChunk(path, file, ihdr);
-    uint32_t crc = crc32(CRC_INIT, (Bytef *)ihdr.type, sizeof(ihdr.type));
-    header.width = htonl(header.width);
-    header.height = htonl(header.height);
-    writeExpect(path, file, &header, sizeof(header));
-    writeCrc(path, file, crc32(crc, (Bytef *)&header, sizeof(header)));
-}
+static void eliminateAlpha(void) {
+    if (!(header.color & ALPHA)) return;
 
-static void writeData(const char *path, FILE *file, uint8_t *data, size_t size) {
-    size_t bound = compressBound(size);
-    uint8_t deflate[bound];
-    int error = compress2(deflate, &bound, data, size, Z_BEST_COMPRESSION);
-    if (error != Z_OK) errx(EX_SOFTWARE, "%s: compress2: %d", path, error);
-
-    struct Chunk idat = { .size = bound, .type = { 'I', 'D', 'A', 'T' } };
-    writeChunk(path, file, idat);
-    uint32_t crc = crc32(CRC_INIT, (Bytef *)idat.type, sizeof(idat.type));
-    writeExpect(path, file, deflate, bound);
-    writeCrc(path, file, crc32(crc, deflate, bound));
-}
-
-static void writeEnd(const char *path, FILE *file) {
-    struct Chunk iend = { .size = 0, .type = { 'I', 'E', 'N', 'D' } };
-    writeChunk(path, file, iend);
-    writeCrc(path, file, crc32(CRC_INIT, (Bytef *)iend.type, sizeof(iend.type)));
-}
-
-static void eliminateAlpha(
-    struct Header *header, uint8_t *data, struct Scanline *lines
-) {
-    if (!(header->color & ALPHA)) return;
-
-    size_t pixelSize = lineSize(*header) / header->width;
-    size_t alphaSize = header->depth / 8;
+    size_t pixelSize = lineSize() / header.width;
+    size_t alphaSize = header.depth / 8;
     size_t colorSize = pixelSize - alphaSize;
 
-    for (uint32_t y = 0; y < header->height; ++y) {
-        for (uint32_t x = 0; x < header->width; ++x) {
+    for (uint32_t y = 0; y < header.height; ++y) {
+        for (uint32_t x = 0; x < header.width; ++x) {
             for (size_t i = 0; i < alphaSize; ++i) {
                 if (lines[y].data[x * pixelSize + colorSize + i] != 0xFF) return;
             }
@@ -391,11 +380,11 @@ static void eliminateAlpha(
     }
 
     uint8_t *ptr = data;
-    for (uint32_t y = 0; y < header->height; ++y) {
+    for (uint32_t y = 0; y < header.height; ++y) {
         uint8_t *type = ptr++;
         uint8_t *data = ptr;
         *type = *lines[y].type;
-        for (uint32_t x = 0; x < header->width; ++x) {
+        for (uint32_t x = 0; x < header.width; ++x) {
             memcpy(ptr, &lines[y].data[x * pixelSize], colorSize);
             ptr += colorSize;
         }
@@ -403,55 +392,55 @@ static void eliminateAlpha(
         lines[y].data = data;
     }
 
-    header->color &= ~ALPHA;
+    header.color &= ~ALPHA;
 }
 
 static void optimize(const char *inPath, const char *outPath) {
-    FILE *input = stdin;
     if (inPath) {
-        input = fopen(inPath, "r");
-        if (!input) err(EX_NOINPUT, "%s", inPath);
+        path = inPath;
+        file = fopen(path, "r");
+        if (!file) err(EX_NOINPUT, "%s", path);
     } else {
-        inPath = "stdin";
+        path = "(stdin)";
+        file = stdin;
     }
 
-    readSignature(inPath, input);
-    struct Header header = readHeader(inPath, input);
+    readSignature();
+    readHeader();
     if (header.interlace != PROGRESSIVE) {
         errx(
             EX_CONFIG, "%s: unsupported interlace method %hhu",
-            inPath, header.interlace
+            path, header.interlace
         );
     }
-    uint8_t *data = readData(inPath, input, header);
-
-    int error = fclose(input);
-    if (error) err(EX_IOERR, "%s", inPath);
+    readData();
 
-    struct Scanline *lines = scanlines(inPath, header, data);
-    reconData(header, lines);
+    int error = fclose(file);
+    if (error) err(EX_IOERR, "%s", path);
 
-    eliminateAlpha(&header, data, lines);
-    filterData(header, lines);
+    scanlines();
+    reconData();
+    eliminateAlpha();
+    filterData();
+    free(lines);
 
-    FILE *output = stdout;
     if (outPath) {
-        output = fopen(outPath, "wx");
-        if (!output) err(EX_CANTCREAT, "%s", outPath);
+        path = outPath;
+        file = fopen(path, "wx");
+        if (!file) err(EX_CANTCREAT, "%s", path);
     } else {
-        outPath = "stdout";
+        path = "(stdout)";
+        file = stdout;
     }
 
-    writeSignature(outPath, output);
-    writeHeader(outPath, output, header);
-    writeData(outPath, output, data, dataSize(header));
-    writeEnd(outPath, output);
-
-    error = fclose(output);
-    if (error) err(EX_IOERR, "%s", outPath);
-
-    free(lines);
+    writeSignature();
+    writeHeader();
+    writeData();
+    writeEnd();
     free(data);
+
+    error = fclose(file);
+    if (error) err(EX_IOERR, "%s", path);
 }
 
 int main(int argc, char *argv[]) {