summary refs log tree commit diff
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to 'bin')
-rw-r--r--bin/glitch.c48
1 files changed, 44 insertions, 4 deletions
diff --git a/bin/glitch.c b/bin/glitch.c
index bf673223..cf3e5b6c 100644
--- a/bin/glitch.c
+++ b/bin/glitch.c
@@ -270,6 +270,14 @@ enum PACKED Filter {
 };
 #define FILTER_COUNT (PAETH + 1)
 
+static struct {
+    bool brokenPaeth;
+    bool forceDeclareFilter;
+    bool forceApplyFilter;
+    enum Filter declareFilter;
+    enum Filter applyFilter;
+} options;
+
 struct Bytes {
     uint8_t x;
     uint8_t a;
@@ -283,7 +291,11 @@ static uint8_t paethPredictor(struct Bytes f) {
     int32_t pb = abs(p - (int32_t)f.b);
     int32_t pc = abs(p - (int32_t)f.c);
     if (pa <= pb && pa <= pc) return f.a;
-    if (pb <= pc) return f.b;
+    if (options.brokenPaeth) {
+        if (pb < pc) return f.b;
+    } else {
+        if (pb <= pc) return f.b;
+    }
     return f.c;
 }
 
@@ -363,8 +375,16 @@ static void filterData(void) {
             }
             if (heuristic[type] < heuristic[minType]) minType = type;
         }
-        lines[y]->type = minType;
-        memcpy(lines[y]->data, filter[minType], lineSize());
+        if (options.forceDeclareFilter) {
+            lines[y]->type = options.declareFilter;
+        } else {
+            lines[y]->type = minType;
+        }
+        if (options.forceApplyFilter) {
+            memcpy(lines[y]->data, filter[options.applyFilter], lineSize());
+        } else {
+            memcpy(lines[y]->data, filter[minType], lineSize());
+        }
     }
 }
 
@@ -412,15 +432,35 @@ static void glitch(const char *inPath, const char *outPath) {
     if (error) err(EX_IOERR, "%s", path);
 }
 
+enum Filter parseFilter(const char *s) {
+    switch (s[0]) {
+        case 'N': case 'n': return NONE;
+        case 'S': case 's': return SUB;
+        case 'U': case 'u': return UP;
+        case 'A': case 'a': return AVERAGE;
+        case 'P': case 'p': return PAETH;
+        default: errx(EX_USAGE, "invalid filter type %s", s);
+    }
+}
+
 int main(int argc, char *argv[]) {
     bool stdio = false;
     char *output = NULL;
 
     int opt;
-    while (0 < (opt = getopt(argc, argv, "co:"))) {
+    while (0 < (opt = getopt(argc, argv, "a:cd:o:p"))) {
         switch (opt) {
+            case 'a': {
+                options.forceApplyFilter = true;
+                options.applyFilter = parseFilter(optarg);
+            } break;
             case 'c': stdio = true; break;
+            case 'd': {
+                options.forceDeclareFilter = true;
+                options.declareFilter = parseFilter(optarg);
+            } break;
             case 'o': output = optarg; break;
+            case 'p': options.brokenPaeth = true; break;
             default: return EX_USAGE;
         }
     }