summary refs log tree commit diff
path: root/bin/gfxx.c
diff options
context:
space:
mode:
authorJune McEnroe <programble@gmail.com>2018-02-05 14:27:23 -0500
committerJune McEnroe <programble@gmail.com>2018-02-05 14:40:21 -0500
commitcfa638863a94a1550bacfb29383f9456ebbe323a (patch)
tree03728597fc5f5b9672a998c3c0d2be0ef030ef79 /bin/gfxx.c
parentAdd flip option to gfxx (diff)
downloadsrc-cfa638863a94a1550bacfb29383f9456ebbe323a.tar.gz
src-cfa638863a94a1550bacfb29383f9456ebbe323a.zip
Rewrite gfxx bit handling
Specifies how many bits for each of "alpha" (ignored), red, green, blue.
Separates byte-order and bit-order. Much more flexible, but now won't
render CARDS.DLL graphics properly due to the skip bit being not where
it expects.

Also mmaps the file instead of reading it all in. And the default
palette and sampling got removed again for now, since it's too awkward
to use.
Diffstat (limited to 'bin/gfxx.c')
-rw-r--r--bin/gfxx.c242
1 files changed, 125 insertions, 117 deletions
diff --git a/bin/gfxx.c b/bin/gfxx.c
index 92087d18..d451d22c 100644
--- a/bin/gfxx.c
+++ b/bin/gfxx.c
@@ -15,11 +15,13 @@
  */
 
 #include <err.h>
+#include <fcntl.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/mman.h>
 #include <sys/stat.h>
 #include <sysexits.h>
 #include <unistd.h>
@@ -27,21 +29,22 @@
 #define RGB(r,g,b) ((uint32_t)(r) << 16 | (uint32_t)(g) << 8 | (uint32_t)(b))
 #define GRAY(n)    RGB(n, n, n)
 #define MASK(b)    ((1 << b) - 1)
-#define SCALE(b,n) (uint8_t)(255 * (uint32_t)(n) / MASK(b))
+#define SCALE(b,n) ((b) ? (uint8_t)(255 * (uint32_t)(n) / MASK(b)) : 0)
 
 static enum {
-    COLOR_GRAYSCALE,
     COLOR_PALETTE,
+    COLOR_GRAYSCALE,
     COLOR_RGB,
     COLOR__MAX,
-} space;
-static uint8_t bits = 1;
-static bool endian;
-static uint32_t palette[256] = {
-#define X(...) __VA_ARGS__, __VA_ARGS__
-    X(X(X(X(X(X(X(0x000000, 0xFFFFFF)))))))
-#undef X
-};
+} space = COLOR_RGB;
+static uint32_t palette[256];
+static enum {
+    ENDIAN_LITTLE,
+    ENDIAN_BIG,
+} byteOrder, bitOrder;
+static uint8_t bits[4] = { 8, 8, 8, 8 };
+#define BITS_COLOR (bits[1] + bits[2] + bits[3])
+#define BITS_TOTAL (bits[0] + BITS_COLOR)
 
 static size_t offset;
 static size_t width = 16;
@@ -49,73 +52,80 @@ static bool flip;
 static bool mirror;
 static size_t scale = 1;
 
-static size_t size = 1024 * 1024; // max from pipe.
+static size_t size;
 static uint8_t *data;
 
 extern int init(int argc, char *argv[]) {
     const char *path = NULL;
-    const char *palPath = NULL;
 
     int opt;
-    while (0 < (opt = getopt(argc, argv, "c:p:b:ern:fmw:z:"))) {
+    while (0 < (opt = getopt(argc, argv, "c:b:e:E:n:w:fmz:"))) {
         switch (opt) {
             case 'c': switch (optarg[0]) {
-                case 'g': space = COLOR_GRAYSCALE; break;
                 case 'p': space = COLOR_PALETTE; break;
+                case 'g': space = COLOR_GRAYSCALE; break;
                 case 'r': space = COLOR_RGB; break;
                 default: return EX_USAGE;
             } break;
-            case 'p': palPath  = optarg; break;
-            case 'b': bits     = strtoul(optarg, NULL, 0); break;
-            case 'e': endian  ^= true; break;
-            case 'n': offset   = strtoul(optarg, NULL, 0); break;
-            case 'w': width    = strtoul(optarg, NULL, 0); break;
-            case 'f': flip    ^= true; break;
-            case 'm': mirror  ^= true; break;
-            case 'z': scale    = strtoul(optarg, NULL, 0); break;
+            case 'e': switch (optarg[0]) {
+                case 'l': byteOrder = ENDIAN_LITTLE; break;
+                case 'b': byteOrder = ENDIAN_BIG; break;
+                default: return EX_USAGE;
+            } break;
+            case 'E': switch (optarg[0]) {
+                case 'l': bitOrder = ENDIAN_LITTLE; break;
+                case 'b': bitOrder = ENDIAN_BIG; break;
+                default: return EX_USAGE;
+            } break;
+            case 'b': {
+                size_t len = strlen(optarg);
+                if (len < 3 || len > 4) return EX_USAGE;
+                bits[0] = (len > 3) ? optarg[0] - '0' : 0;
+                bits[1] = optarg[len-3] - '0';
+                bits[2] = optarg[len-2] - '0';
+                bits[3] = optarg[len-1] - '0';
+            } break;
+            case 'n': offset  = strtoul(optarg, NULL, 0); break;
+            case 'w': width   = strtoul(optarg, NULL, 0); break;
+            case 'f': flip   ^= true; break;
+            case 'm': mirror ^= true; break;
+            case 'z': scale   = strtoul(optarg, NULL, 0); break;
             default: return EX_USAGE;
         }
     }
     if (argc > optind) path = argv[optind];
-    if (!bits || !width || !scale) return EX_USAGE;
-
-    if (palPath) {
-        FILE *pal = fopen(palPath, "r");
-        if (!pal) err(EX_NOINPUT, "%s", palPath);
-
-        fread(palette, 1, sizeof(palette), pal);
-        if (ferror(pal)) err(EX_IOERR, "%s", palPath);
-
-        fclose(pal);
-    }
-
-    FILE *file = path ? fopen(path, "r") : stdin;
-    if (!file) err(EX_NOINPUT, "%s", path);
+    if (!width || !scale) return EX_USAGE;
 
     if (path) {
+        int fd = open(path, O_RDONLY);
+        if (fd < 0) err(EX_NOINPUT, "%s", path);
+
         struct stat stat;
-        int error = fstat(fileno(file), &stat);
+        int error = fstat(fd, &stat);
         if (error) err(EX_IOERR, "%s", path);
         size = stat.st_size;
-    }
-
-    data = malloc(size);
-    if (!data) err(EX_OSERR, "malloc(%zu)", size);
 
-    size = fread(data, 1, size, file);
-    if (ferror(file)) err(EX_IOERR, "%s", path);
+        data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+        if (data == MAP_FAILED) err(EX_IOERR, "%s", path);
 
-    fclose(file);
+    } else {
+        size = 1024 * 1024;
+        data = malloc(size);
+        if (!data) err(EX_OSERR, "malloc(%zu)", size);
+        size = fread(data, 1, size, stdin);
+        if (ferror(stdin)) err(EX_IOERR, "stdin");
+    }
 
     return EX_OK;
 }
 
 static void printOpts(void) {
     printf(
-        "gfxx -c %c -b %hhu %s-n %#zx -w %zu %s%s-z %zu\n",
-        "gpr"[space],
-        bits,
-        endian ? "-e " : "",
+        "gfxx -c %s -e %c -E %c -b %c%c%c%c -n %#zx -w %zu %s%s-z %zu\n",
+        (const char *[COLOR__MAX]){ "palette", "grayscale", "rgb" }[space],
+        "lb"[byteOrder],
+        "lb"[bitOrder],
+        bits[0] + '0', bits[1] + '0', bits[2] + '0', bits[3] + '0',
         offset,
         width,
         flip ? "-f " : "",
@@ -186,104 +196,102 @@ static void put(const struct Iter *it, uint32_t p) {
     }
 }
 
+static uint32_t interpolate(uint32_t n) {
+    uint32_t r, g, b;
+    if (bitOrder == ENDIAN_LITTLE) {
+        b = n & MASK(bits[3]);
+        g = (n >>= bits[3]) & MASK(bits[2]);
+        r = (n >>= bits[2]) & MASK(bits[1]);
+    } else {
+        r = (n >>= bits[0]) & MASK(bits[1]);
+        g = (n >>= bits[1]) & MASK(bits[2]);
+        b = (n >>= bits[2]) & MASK(bits[3]);
+    }
+    return RGB(SCALE(bits[1], r), SCALE(bits[2], g), SCALE(bits[3], b));
+}
+
 static void drawBits(struct Iter *it) {
     for (size_t i = offset; i < size; ++i) {
-        for (int s = 0; s < 8; s += bits) {
-            uint8_t n = data[i] >> (endian ? 8 - bits - s : s) & MASK(bits);
+        for (uint8_t b = 0; b < 8; b += BITS_TOTAL) {
+            uint8_t n;
+            if (byteOrder == ENDIAN_BIG) {
+                n = data[i] >> (8 - BITS_TOTAL - b) & MASK(BITS_TOTAL);
+            } else {
+                n = data[i] >> b & MASK(BITS_TOTAL);
+            }
             if (space == COLOR_PALETTE) {
                 put(it, palette[n]);
-            } else if (space == COLOR_RGB && bits == 4) {
-                put(it, RGB(SCALE(1, n & 1), SCALE(1, n & 2), SCALE(1, n & 4)));
-            } else {
-                put(it, GRAY(SCALE(bits, n)));
+            } else if (space == COLOR_GRAYSCALE) {
+                put(it, GRAY(SCALE(BITS_COLOR, n)));
+            } else if (space == COLOR_RGB) {
+                put(it, interpolate(n));
             }
             if (!next(it)) return;
         }
     }
 }
 
-static void draw8(struct Iter *it) {
-    for (size_t i = offset; i < size; ++i) {
-        if (space == COLOR_GRAYSCALE) {
-            put(it, GRAY(data[i]));
-        } else if (space == COLOR_PALETTE) {
-            put(it, palette[data[i]]);
-        } else {
-            uint32_t r = (endian ? data[i] >> 5 : data[i] >> 0) & MASK(3);
-            uint32_t g = (endian ? data[i] >> 2 : data[i] >> 3) & MASK(3);
-            uint32_t b = (endian ? data[i] >> 0 : data[i] >> 6) & MASK(2);
-            put(it, RGB(SCALE(3, r), SCALE(3, g), SCALE(2, b)));
+static void drawBytes(struct Iter *it) {
+    uint8_t bytes = (BITS_TOTAL + 7) / 8;
+    for (size_t i = offset; i < size; i += bytes) {
+        uint32_t n = 0;
+        for (size_t b = 0; b < bytes; ++b) {
+            n <<= 8;
+            n |= (byteOrder == ENDIAN_BIG) ? data[i+b] : data[i+bytes-b-1];
         }
-        if (!next(it)) break;
-    }
-}
-
-static void draw16(struct Iter *it) {
-    for (size_t i = offset; i + 1 < size; i += 2) {
-        uint16_t n = (endian)
-            ? (uint16_t)data[i+0] << 8 | (uint16_t)data[i+1]
-            : (uint16_t)data[i+1] << 8 | (uint16_t)data[i+0];
-        uint32_t r = n >> 11 & MASK(5);
-        uint32_t g = n >>  5 & MASK(6);
-        uint32_t b = n >>  0 & MASK(5);
-        put(it, RGB(SCALE(5, r), SCALE(6, g), SCALE(5, b)));
-        if (!next(it)) break;
-    }
-}
-
-static void draw24(struct Iter *it) {
-    for (size_t i = offset; i + 2 < size; i += 3) {
-        if (endian) {
-            put(it, RGB(data[i + 0], data[i + 1], data[i + 2]));
-        } else {
-            put(it, RGB(data[i + 2], data[i + 1], data[i + 0]));
-        }
-        if (!next(it)) break;
-    }
-}
-
-static void draw32(struct Iter *it) {
-    for (size_t i = offset; i + 3 < size; i += 4) {
-        if (endian) {
-            put(it, RGB(data[i + 1], data[i + 2], data[i + 3]));
-        } else {
-            put(it, RGB(data[i + 2], data[i + 1], data[i + 0]));
+        if (space == COLOR_PALETTE) {
+            put(it, palette[n & 0xFF]);
+        } else if (space == COLOR_GRAYSCALE) {
+            put(it, GRAY(SCALE(BITS_COLOR, n & MASK(BITS_COLOR))));
+        } else if (space == COLOR_RGB) {
+            put(it, interpolate(n));
         }
-        if (!next(it)) break;
+        if (!next(it)) return;
     }
 }
 
 extern void draw(uint32_t *buf, size_t xres, size_t yres) {
     memset(buf, 0, 4 * xres * yres);
     struct Iter it = iter(buf, xres, yres);
-    switch (bits) {
-        case 8:  draw8(&it);  break;
-        case 16: draw16(&it); break;
-        case 24: draw24(&it); break;
-        case 32: draw32(&it); break;
-        default: drawBits(&it);
+    if (BITS_TOTAL >= 8) {
+        drawBytes(&it);
+    } else {
+        drawBits(&it);
     }
 }
 
-static void samplePalette(void) {
-    size_t temp = scale;
-    scale = 1;
-    draw(palette, 256, 1);
-    scale = temp;
+#define PRESETS_LEN (sizeof(PRESETS) / sizeof(PRESETS[0]))
+static const uint8_t PRESETS[][4] = {
+    { 0, 0, 1, 0 },
+    { 0, 1, 1, 0 },
+    { 1, 1, 1, 1 },
+    { 2, 2, 2, 2 },
+    { 0, 3, 3, 2 },
+    { 1, 5, 5, 5 },
+    { 0, 5, 6, 5 },
+    { 0, 8, 8, 8 },
+    { 8, 8, 8, 8 },
+};
+static uint8_t preset = PRESETS_LEN - 1;
+
+static void setPreset(void) {
+    for (int i = 0; i < 4; ++i) {
+        bits[i] = PRESETS[preset][i];
+    }
 }
 
 extern void input(char in) {
-    size_t pixel = (bits + 7) / 8;
-    size_t row = width * bits / 8;
+    size_t pixel = (BITS_TOTAL + 7) / 8;
+    size_t row = width * pixel;
     switch (in) {
         case 'q': printOpts(); exit(EX_OK);
         break; case 'o': printOpts();
         break; case '[': if (!space--) space = COLOR__MAX - 1;
         break; case ']': if (++space == COLOR__MAX) space = 0;
-        break; case 'p': samplePalette();
-        break; case '{': if (bits > 16) bits -= 8; else bits = (bits + 1) / 2;
-        break; case '}': if (bits < 16) bits *= 2; else if (bits < 32) bits += 8;
-        break; case 'e': endian ^= true;
+        break; case '{': if (!preset--) preset = PRESETS_LEN - 1; setPreset();
+        break; case '}': if (++preset == PRESETS_LEN) preset = 0; setPreset();
+        break; case 'e': byteOrder ^= ENDIAN_BIG;
+        break; case 'E': bitOrder ^= ENDIAN_BIG;
         break; case 'h': if (offset) offset--;
         break; case 'j': offset += pixel;
         break; case 'k': if (offset >= pixel) offset -= pixel;