Arduboy2/extras/cabi/cabi.c

387 lines
6.8 KiB
C
Raw Normal View History

/*
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 <http://creativecommons.org/publicdomain/zero/1.0/>.
Usage:
cabi in.png [array_name_prefix]
*/
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "lodepng/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;
}