diff options
Diffstat (limited to '')
-rw-r--r-- | bin/glitch.c | 312 |
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; |