summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--bin/README1
-rw-r--r--bin/pngo.c42
2 files changed, 40 insertions, 3 deletions
diff --git a/bin/README b/bin/README
index 94429275..32f9862d 100644
--- a/bin/README
+++ b/bin/README
@@ -82,6 +82,7 @@ PNG optimizer. Does not support interlaced PNGs.
  - Discards ancillary chunks
  - Discards unnecessary alpha channel
  - Converts unnecessary truecolor to grayscale
+ - Indexes color if possible
  - Applies a simple filter type heuristic
  - Applies zlib's best compression
 
diff --git a/bin/pngo.c b/bin/pngo.c
index 3522eb82..95252fff 100644
--- a/bin/pngo.c
+++ b/bin/pngo.c
@@ -197,9 +197,7 @@ static void writeHeader(void) {
 
 static struct {
     uint32_t len;
-    struct PACKED {
-        uint8_t r, g, b;
-    } entries[256];
+    uint8_t entries[256][3];
 } palette;
 
 static void readPalette(void) {
@@ -473,6 +471,43 @@ static void discardColor(void) {
     header.color &= ~TRUECOLOR;
 }
 
+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) {
+            uint32_t i;
+            for (i = 0; i < palette.len; ++i) {
+                if (0 == memcmp(palette.entries[i], &lines[y].data[x * 3], 3)) {
+                    break;
+                }
+            }
+            if (i < palette.len) continue;
+            if (palette.len == 256) return;
+            memcpy(palette.entries[i], &lines[y].data[x * 3], 3);
+            palette.len++;
+        }
+    }
+
+    uint8_t *ptr = data;
+    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) {
+            uint32_t i;
+            for (i = 0; i < palette.len; ++i) {
+                if (0 == memcmp(palette.entries[i], &lines[y].data[x * 3], 3)) {
+                    break;
+                }
+            }
+            *ptr++ = i;
+        }
+        lines[y].type = type;
+        lines[y].data = data;
+    }
+    header.color = INDEXED;
+}
+
 static void optimize(const char *inPath, const char *outPath) {
     if (inPath) {
         path = inPath;
@@ -501,6 +536,7 @@ static void optimize(const char *inPath, const char *outPath) {
     reconData();
     discardAlpha();
     discardColor();
+    indexColor();
     filterData();
     free(lines);
 
13 -0400'>2021-09-08Up default min-repeat to 30 linesJune McEnroe 2021-09-08Handle dirty lines in git-commentJune McEnroe 2021-09-08Document and install git-commentJune McEnroe 2021-09-08Add repeat and all options to git-commentJune McEnroe 2021-09-08Add group threshold to git-commentJune McEnroe