/* cabi - Compress Arduboy Image A command line program to read a PNG file containing a bitmap image, compress it using RLE encoding and convert it to C/C++ code suitable for use with the Team A.R.G. drawCompressed() function. This function is included in the Arduboy2 library. Written by zep https://www.lexaloffle.com/bbs/?uid=1 https://twitter.com/lexaloffle Contributed to Team A.R.G. To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see . Usage: cabi in.png [array_name_prefix] */ #include #include #include #include "lodepng.h" // alternative pixel order mapping //#define READING_ORDER 1 int reading_order = 0; typedef unsigned char uint8_t; #ifndef WIDTH #define WIDTH 128 #define HEIGHT 64 #define PROGMEM #endif // one byte encodes a 1x8 stick; low byte at top // for testing void draw_sprite_ascii(const uint8_t *dat, int w, int h) { int x, y; int row, bit; int rows; rows = (h+7)/8; for (y = 0; y < h; y ++) { row = y/8; bit = y&7; for (x = 0; x < w; x++) { if (dat[x + (row*w)] & (1 << bit)) printf("#"); else printf("."); } printf("\n"); } } // ---------------------------------------------------------------------------- // :: Decompress // ---------------------------------------------------------------------------- // compression / decompression session state typedef struct CSESSION{ int byte; int bit; const uint8_t *src; uint8_t *dest; int src_pos; int out_pos; int w, h; }CSESSION; static CSESSION cs; // get an n-bit number from the compressed data stream static int getval(int bits) { int val = 0; int i; for (i = 0; i < bits; i++) { if (cs.bit == 0x100) { cs.bit = 0x1; cs.byte = cs.src[cs.src_pos]; cs.src_pos ++; } if (cs.byte & cs.bit) val += (1 << i); cs.bit <<= 1; } return val; } // decompress_rle // if not NULL, w and h give back the size of the sprite. void draw_compressed_sprite_ascii(const uint8_t *src) { int col; int pos; int bl, len; int i; int w, h; int x, y; int total = 0; memset(&cs, 0, sizeof(cs)); cs.src = src; cs.bit = 0x100; cs.src_pos = 0; // header w = getval(8) + 1; h = getval(8) + 1; col = getval(1); // starting colour x = y = 0; while (y < h) { bl = 1; while (!getval(1)) bl += 2; len = getval(bl)+1; // span length for (i = 0; i < len; i++) { //if ((x%8) == 0) // every 8th bit (format test) printf("%s", col ? "#":"."); if (col) total++; x++; if (x >= w) { printf("\n"); y ++; x = 0; } //if ((x+y*w)%(w*8) == 0) printf("\n"); // print every 8th line (format test) } col = 1-col; // toggle } printf("\ntotal: %d\n", total); } #if 0 #endif // ---------------------------------------------------------------------------- // :: Compress // ---------------------------------------------------------------------------- /* getcol pos is the index of the pixel: 0 .. w*h-1 */ static int getcol(int pos) { int x, y; // display order if (reading_order == 0) { if (cs.src[pos/8] & (1 << (pos&7))) return 1; return 0; } // reading order (compresses slightly better but harder to optimize sprite blit) // or use this after loading png into display order (no need for extra conversion) x = (pos % cs.w); y = (pos / cs.w); if (cs.src[x + ((y/8)*cs.w)] & (1 << (y&7))) return 1; return 0; } static int find_rlen(int pos, int plen) { int col; int pos0; col = getcol(pos); pos0 = pos; while(getcol(pos) == col && pos < plen) pos ++; return pos-pos0; } // write a bit to the stream. non-zero val means 1, otherwise 0. static void putbit(int val) { if (val) cs.byte |= cs.bit; cs.bit <<= 1; if (cs.bit == 0x100) { //output byte if (cs.out_pos != 0) printf(","); if (cs.out_pos % 16 == 0) printf("\n"); printf("0x%02x", cs.byte); cs.out_pos ++; cs.bit = 0x1; cs.byte = 0; } } // write an n-bit (bits) number (val) to the output steam static void putval(int val, int bits) { int i; if (bits <= 0) return; for (i = 0; i < bits; i++) putbit(val & (1 << i)); } // write a span length // a string of bits encoding the number of bits needed to encode the length, // and then the length. static int putsplen(int len) { int blen = 1; // how bits needed to encode length while ((1 << blen) <= len) blen += 2; // write number of bits (1-terminated string of zeroes) putval(0,(blen-1)/2); putval(1,1); // terminator // write length putval(len, blen); } /* comp compress plen 1-bit pixels from src to dest */ int compress_rle(const uint8_t *src, int w, int h, char *prefix, char *suffix) { int pos; int rlen; int len; printf("const uint8_t PROGMEM %s%s[] = {", prefix, suffix); fflush(stdout); memset(&cs, 0, sizeof(cs)); cs.src = src; cs.bit = 1; cs.w = w; cs.h = h; // header putval(w-1, 8); putval(h-1, 8); putval(getcol(0), 1); // first colour pos = 0; // span data while (pos < w*h) { rlen = find_rlen(pos, w*h); pos += rlen; putsplen(rlen-1); } // pad with zeros and flush while (cs.bit != 0x1) putbit(0); printf("\n};\n"); return cs.out_pos; // bytes } int main(int argc, char **argv) { int compressed_len; int pixels; int w,h; unsigned char *bmp = NULL; unsigned char *bmp0 = NULL; unsigned char *bmp1 = NULL; int result; int rawlen; int x, y; int row,bit; char prefix[256] = "out"; if (argc < 2) { printf("usage: cabi in.png [array_name_prefix]\n"); } if (argc >= 3) strcpy(prefix, argv[2]); result = lodepng_decode_file(&bmp, &w, &h, argv[1], 6, 8); if (result != 0) { printf("could not load %s\n", argv[1]); exit(0); } // generate sprite and mask rawlen = w * (h+7) / 8; bmp0 = malloc(rawlen); memset(bmp0, 0, rawlen); bmp1 = malloc(rawlen); memset(bmp1, 0, rawlen); printf("// %s width: %d height: %d\n", argv[1], w, h); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { row = y/8; bit = y&7; if (bmp[(x+y*w)*4 + 3] > 128) // need to be opaque to count if (bmp[(x+y*w)*4 + 0] > 128) { // set sprite bmp0[x + (row*w)] |= (1 << bit); } if (bmp[(x+y*w)*4 + 3] > 128) { // set mask bmp1[x + (row*w)] |= (1 << bit); } } } compressed_len = compress_rle(bmp0, w, h, prefix, ""); printf("// bytes:%d ratio: %3.3f\n\n", compressed_len, (float)(compressed_len * 8)/ (float)(w*h)); compressed_len = compress_rle(bmp1, w, h, prefix, "_mask"); printf("// bytes:%d ratio: %3.3f\n\n", compressed_len, (float)(compressed_len * 8)/ (float)(w*h)); free(bmp0); free(bmp1); return 0; }