diff --git a/extras/cabi/LICENSE b/extras/cabi/LICENSE new file mode 100644 index 0000000..6ca207e --- /dev/null +++ b/extras/cabi/LICENSE @@ -0,0 +1,122 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. + diff --git a/extras/cabi/README.md b/extras/cabi/README.md new file mode 100644 index 0000000..c9a5d0f --- /dev/null +++ b/extras/cabi/README.md @@ -0,0 +1,75 @@ +# Cabi +Arduboy encoder: +Monochrome rle encoding by [Zep @lexaloffle](https://twitter.com/lexaloffle) + +This tool is used for creating a compressed array from an image (png) + +The function to decompress in Arduino (Arduboy) can be found here: + +https://github.com/TEAMarg/drawCompressed + +to build: +```Bash +gcc cabi.c lodepng.c -o cabi +``` + +to use: +```Bash +cabi input_file.png [array_name_prefix] +``` + +Look at the compress ratio, before using the compressed data, it might be worse in some cases (above 1 is bad) + +ouput: +```C++ +// input_file.png width: 128 height: 48 +const uint8_t PROGMEM array_name_prefix[] = { + 0x7f,0x2f,0x30,0x68,0xb7,0xdb,0xed,0xdb,0x53,0x9f,0xfa,0x73,0x97,0xbb,0xd5,0xe7, + 0x2e,0x77,0xb9,0xcb,0x5d,0x6e,0xbb,0xbd,0xdc,0xe5,0x2e,0x77,0xb9,0xcb,0x5d,0xee, + 0x72,0x97,0x5b,0x2b,0xbb,0xdc,0x6e,0xb7,0xdb,0xed,0x76,0xbb,0xdd,0x6e,0xb7,0xdb, + 0xed,0x76,0xbb,0xdd,0x6e,0xb7,0xca,0xae,0x43,0x57,0x76,0xbb,0x5d,0x6e,0xb7,0xdb, + 0xed,0x76,0xbb,0xdd,0x6e,0xb7,0xdb,0xad,0xb2,0xdb,0x75,0xd8,0x5a,0x2a,0xb9,0xdd, + 0x6e,0xb7,0xeb,0x75,0x74,0xd9,0xe5,0x72,0xf9,0xfd,0xdf,0xea,0x72,0x57,0x77,0x75, + 0x97,0x3b,0x6d,0x95,0x93,0xca,0x2a,0xbb,0x0e,0xbf,0x6d,0x6f,0x75,0xab,0xed,0xb9, + 0x5c,0x77,0x2a,0xcf,0x9f,0x76,0xda,0xd5,0xad,0x4e,0xf7,0xaa,0x57,0xe9,0x74,0x3a, + 0x9d,0xbb,0xa4,0xc2,0xd1,0x76,0xb6,0xb4,0xd3,0x2e,0x9f,0x5e,0x5a,0x85,0xbc,0x74, + 0x3a,0x9d,0x4e,0xa7,0x3b,0x67,0x57,0x5b,0xaf,0x93,0x4a,0xb9,0x9d,0x52,0xca,0x2d, + 0x97,0xdb,0x74,0x6c,0x49,0xe5,0x9c,0x92,0x4a,0x2a,0xa9,0xa4,0x53,0x84,0xe9,0xf4, + 0xea,0x53,0xbf,0x56,0x2a,0x65,0xed,0x6e,0xa7,0x94,0x52,0x4a,0x46,0x9f,0x5e,0xaf, + 0x97,0xcb,0x6d,0xbc,0x4b,0xe7,0x5c,0xff,0x13,0x7a,0x9f,0xde,0xce,0xbe,0xd6,0xb9, + 0xee,0xd4,0xae,0x3b,0xb5,0x6e,0x7b,0xab,0x5b,0x8d,0x24,0x25,0x4b,0x29,0x95,0x4a, + 0x76,0x56,0x3a,0x1d,0xdb,0xce,0xcd,0x96,0x4e,0xa7,0xd3,0xe9,0xec,0x4e,0x4a,0xe9, + 0xec,0x08,0xdb,0x3a,0x67,0xe7,0x52,0xb3,0x29,0x2d,0xf5,0xa8,0x4b,0xa7,0xd3,0xe9, + 0x74,0xce,0xb9,0x5b,0x2f,0xdd,0x6a,0x87,0xac,0xa4,0x2a,0x25,0x95,0x54,0x52,0x49, + 0x25,0xed,0x36,0xa4,0xed,0xdc,0x64,0x49,0xa5,0x64,0xc8,0x5a,0x2a,0x1d,0xda,0xc2, + 0x1b,0x11,0x06,0xb9,0x2a,0x95,0x4c,0x55,0x2a,0xa2,0xc6,0xd5,0xea,0x56,0xb7,0xdd, + 0x6e,0xb7,0x4a,0x4a,0x55,0xa5,0x2a,0x95,0x52,0x2a,0x55,0xc9,0x54,0xaa,0x92,0xa9, + 0x54,0x25,0x53,0x29,0xa5,0xdd,0xf4,0x69,0x25,0xa5,0x54,0xed,0x5c,0xe7,0x3a,0xe7, + 0x9c,0x73,0x4a,0x2a,0x3b,0x15,0x51,0x52,0xf1,0x79,0xa5,0xd3,0x49,0x25,0x95,0xdc, + 0x6e,0xb7,0xdb,0xed,0x36,0x9d,0xce,0x6e,0xff,0xff,0xd3,0xa7,0x75,0x3a,0x1d,0xd9, + 0xce,0x39,0xe7,0x1c,0xd3,0xce,0x39,0xe7,0x9c,0xeb,0x5c,0xe7,0x9c,0x73,0x6e,0xf2, + 0xa4,0x54,0x52,0x2a,0xbb,0xdd,0x6e,0x2f,0xad,0xb4,0xd2,0xf9,0xb4,0x72,0x4e,0x6e, + 0x63,0x95,0xce,0x39,0xa9,0x3e,0xf5,0x6f,0x8f,0x08,0x87,0x64,0xc9,0x54,0x32,0xa2, + 0xb6,0xdb,0xed,0xea,0x56,0xb7,0x3d,0x9d,0x4e,0xa7,0x5b,0x5d,0x77,0x4a,0xb9,0xfe, + 0xa7,0x4a,0xd6,0xd2,0x29,0xd5,0x5d,0xab,0x63,0xeb,0x55,0xaf,0xf2,0x4a,0x72,0xef, + 0xdc,0x76,0xbb,0x5d,0x6e,0xb7,0x4a,0x2a,0xe7,0x56,0x3b,0x19,0xd1,0x76,0x2e,0x65, + 0xb7,0xdb,0xd5,0xbd,0xea,0x5c,0xe7,0x3a,0x9d,0xdd,0x6e,0xb7,0x5b,0x25,0x9d,0xb3, + 0xe9,0x94,0x54,0x52,0x49,0x25,0x95,0x54,0xaa,0x94,0x2a,0x25,0x95,0x54,0xd2,0x59, + 0x75,0xdb,0xed,0xae,0x5e,0x75,0xae,0x73,0x9d,0xeb,0xd4,0xa7,0x3e,0xf5,0xb9,0xda, + 0xee,0xa4,0xb4,0xca,0x6e,0xb7,0xdb,0xad,0x73,0xee,0xda,0x39,0xbb,0xba,0xb7,0x53, + 0x25,0x95,0xdc,0x2e,0x95,0x53,0xf6,0x72,0x97,0x93,0x4a,0x3a,0xa9,0xa4,0x3a,0xa5, + 0x7a,0x95,0xfa,0x5c,0xa7,0xd3,0x69,0xae,0xed,0x2a,0xa9,0x64,0x25,0x95,0x4e,0xa7, + 0xd3,0xe9,0xd0,0x97,0x5e,0x67,0xb7,0xa1,0x69,0x9d,0x1d,0xcb,0xe9,0xed,0x36,0x34, + 0xea,0x74,0x3a,0x9d,0x56,0x4a,0x2a,0xa9,0x94,0x4e,0xa7,0x53,0x44,0x4a,0x56,0xa9, + 0xaa,0x54,0x2a,0x25,0x75,0x5a,0x6d,0x95,0x74,0xeb,0x74,0xe7,0x8a,0x4a,0xc9,0x2a, + 0x55,0x95,0x4a,0xa5,0xa4,0x56,0xd2,0x39,0xe7,0xb6,0xdb,0xed,0xf6,0xed,0x29,0x59, + 0xc9,0x4a,0x2a,0xe2,0x74,0xb4,0xe9,0xd0,0xb5,0xce,0x8e,0xfd,0x74,0x3a,0xb4,0x01 +}; +// bytes:640 ratio: 0.833 + + +const uint8_t PROGMEM array_name_prefix_mask[] = { + 0x7f,0x2f,0x81,0xff,0x17 +}; +// bytes:5 ratio: 0.007 +``` diff --git a/extras/cabi/cabi.c b/extras/cabi/cabi.c new file mode 100644 index 0000000..4637f95 --- /dev/null +++ b/extras/cabi/cabi.c @@ -0,0 +1,373 @@ +/* + abe.c + + arduboy encoder + monochrome rle encoding by zep + License: CC-0 + + 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; +}