From bfa95e93ba37926011a12cfb9aa937597bc2da18 Mon Sep 17 00:00:00 2001 From: "D. Bohdan" Date: Fri, 4 Oct 2024 18:06:03 +0000 Subject: [PATCH] build(cute_png): remove --- GNUmakefile | 2 +- vendor/cute_png.h | 1850 --------------------------------------------- 2 files changed, 1 insertion(+), 1851 deletions(-) delete mode 100644 vendor/cute_png.h diff --git a/GNUmakefile b/GNUmakefile index 09928aa..d0d5ffc 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -15,7 +15,7 @@ PREFIX ?= /usr/local all: hicolor -hicolor: cli.c hicolor.h vendor/cute_png.h +hicolor: cli.c hicolor.h $(CC) $< -o $@ $(CFLAGS) $(LIBS) clean: clean-no-ext clean-exe clean-exe: diff --git a/vendor/cute_png.h b/vendor/cute_png.h deleted file mode 100644 index 16926cd..0000000 --- a/vendor/cute_png.h +++ /dev/null @@ -1,1850 +0,0 @@ -/* - ------------------------------------------------------------------------------ - Licensing information can be found at the end of the file. - ------------------------------------------------------------------------------ - - cute_png.h - v1.05 - - To create implementation (the function definitions) - #define CUTE_PNG_IMPLEMENTATION - in *one* C/CPP file (translation unit) that includes this file - - - SUMMARY: - - This header wraps some very nice functions by Richard Mitton from his - tigr (Tiny Graphics) library, with some additional features and small - bug-fixes. - - - Revision history: - 1.00 (12/23/2016) initial release - 1.01 (03/08/2017) tRNS chunk support for paletted images - 1.02 (10/23/2017) support for explicitly loading paletted png images - 1.03 (11/12/2017) construct atlas in memory - 1.04 (08/23/2018) various bug fixes for filter and word decoder - added `cp_load_blank` - 1.05 (11/10/2022) added `cp_save_png_to_memory` - - - EXAMPLES: - - Loading a PNG from disk, then freeing it - cp_image_t img = cp_load_png("images/pic.png"); - ... - free(img.pix); - CUTE_PNG_MEMSET(&img, 0, sizeof(img)); - - Loading a PNG from memory, then freeing it - cp_image_t img = cp_load_png_mem(memory, sizeof(memory)); - ... - free(img.pix); - CUTE_PNG_MEMSET(&img, 0, sizeof(img)); - - Saving a PNG to disk - cp_save_png("images/example.png", &img); - // img is just a raw RGBA buffer, and can come from anywhere, - // not only from cp_load*** functions - - Creating a texture atlas in memory - int w = 1024; - int h = 1024; - cp_atlas_image_t* imgs_out = (cp_atlas_image_t*)malloc(sizeof(cp_atlas_image_t) * my_png_count); - cp_image_t atlas_img = cp_make_atlas(w, int h, my_png_array, my_png_count, imgs_out); - // just pass an array of pointers to images along with the image count. Make sure to also - // provide an array of `cp_atlas_image_t` for `cp_make_atlas` to output important UV info for the - // images that fit into the atlas. - - Using the default atlas saver - int errors = cp_default_save_atlas("atlas.png", "atlas.txt", atlas_img, atlas_imgs, img_count, names_of_all_images ? names_of_all_images : 0); - if (errors) { ... } - // Atlas info (like uv coordinates) are in "atlas.txt", and the image was writen to "atlas.png". - // atlas_imgs was an array of `cp_atlas_image_t` from the `cp_make_atlas` function. - - Inflating a DEFLATE block (decompressing memory stored in DEFLATE format) - cp_inflate(in, in_bytes, out, out_bytes); - // this function requires knowledge of the un-compressed size - // does *not* do any internal realloc! Will return errors if an - // attempt to overwrite the out buffer is made - - CUSTOMIZATION - - There are various macros in this header you can customize by defining them before - including cute_png.h. Simply define one to override the default behavior. - - CUTE_PNG_ALLOCA - CUTE_PNG_ALLOC - CUTE_PNG_FREE - CUTE_PNG_CALLOC - CUTE_PNG_REALLOC - CUTE_PNG_MEMCPY - CUTE_PNG_MEMCMP - CUTE_PNG_MEMSET - CUTE_PNG_ASSERT - CUTE_PNG_FPRINTF - CUTE_PNG_SEEK_SET - CUTE_PNG_SEEK_END - CUTE_PNG_FILE - CUTE_PNG_FOPEN - CUTE_PNG_FSEEK - CUTE_PNG_FREAD - CUTE_PNG_FTELL - CUTE_PNG_FWRITE - CUTE_PNG_FCLOSE - CUTE_PNG_FERROR - CUTE_PNG_ATLAS_MUST_FIT - CUTE_PNG_ATLAS_FLIP_Y_AXIS_FOR_UV - CUTE_PNG_ATLAS_EMPTY_COLOR -*/ - -/* - Contributors: - Zachary Carter 1.01 - bug catch for tRNS chunk in paletted images - Dennis Korpel 1.03 - fix some pointer/memory related bugs - Dennis Korpel 1.04 - fix for filter on first row of pixels -*/ - -#if !defined(CUTE_PNG_H) - -#ifdef _WIN32 - #if !defined(_CRT_SECURE_NO_WARNINGS) - #define _CRT_SECURE_NO_WARNINGS - #endif -#endif - -#ifndef CUTE_PNG_ATLAS_MUST_FIT - #define CUTE_PNG_ATLAS_MUST_FIT 1 // returns error from cp_make_atlas if *any* input image does not fit -#endif // CUTE_PNG_ATLAS_MUST_FIT - -#ifndef CUTE_PNG_ATLAS_FLIP_Y_AXIS_FOR_UV - #define CUTE_PNG_ATLAS_FLIP_Y_AXIS_FOR_UV 1 // flips output uv coordinate's y. Can be useful to "flip image on load" -#endif // CUTE_PNG_ATLAS_FLIP_Y_AXIS_FOR_UV - -#ifndef CUTE_PNG_ATLAS_EMPTY_COLOR - #define CUTE_PNG_ATLAS_EMPTY_COLOR 0x000000FF // the fill color for empty areas in a texture atlas (RGBA) -#endif // CUTE_PNG_ATLAS_EMPTY_COLOR - -#include -#include - -typedef struct cp_pixel_t cp_pixel_t; -typedef struct cp_image_t cp_image_t; -typedef struct cp_indexed_image_t cp_indexed_image_t; -typedef struct cp_atlas_image_t cp_atlas_image_t; - -// Read this in the event of errors from any function -extern const char* cp_error_reason; - -// return 1 for success, 0 for failures -int cp_inflate(void* in, int in_bytes, void* out, int out_bytes); -int cp_save_png(const char* file_name, const cp_image_t* img); - -typedef struct cp_saved_png_t -{ - int size; // Size of the `data` buffer. - void* data; // Pointer to the saved png in memory. - // NULL if something went wrong. - // Call CUTE_PNG_FREE on `data` when done. -} cp_saved_png_t; - -// Saves a png file to memory. -// Call CUTE_PNG_FREE on .data when done. -cp_saved_png_t cp_save_png_to_memory(const cp_image_t* img); - -// Constructs an atlas image in-memory. The atlas pixels are stored in the returned image. free the pixels -// when done with them. The user must provide an array of cp_atlas_image_t for the `imgs` param. `imgs` holds -// information about uv coordinates for an associated image in the `pngs` array. Output image has NULL -// pixels buffer in the event of errors. -cp_image_t cp_make_atlas(int atlasWidth, int atlasHeight, const cp_image_t* pngs, int png_count, cp_atlas_image_t* imgs_out); - -// A decent "default" function, ready to use out-of-the-box. Saves out an easy to parse text formatted info file -// along with an atlas image. `names` param can be optionally NULL. -int cp_default_save_atlas(const char* out_path_image, const char* out_path_atlas_txt, const cp_image_t* atlas, const cp_atlas_image_t* imgs, int img_count, const char** names); - -// these two functions return cp_image_t::pix as 0 in event of errors -// call free on cp_image_t::pix when done, or call cp_free_png -cp_image_t cp_load_png(const char *file_name); -cp_image_t cp_load_png_mem(const void *png_data, int png_length); -cp_image_t cp_load_blank(int w, int h); // Alloc's pixels, but `pix` memory is uninitialized. -void cp_free_png(cp_image_t* img); -void cp_flip_image_horizontal(cp_image_t* img); - -// Reads the w/h of the png without doing any other decompression or parsing. -void cp_load_png_wh(const void* png_data, int png_length, int* w, int* h); - -// loads indexed (paletted) pngs, but does not depalette the image into RGBA pixels -// these two functions return cp_indexed_image_t::pix as 0 in event of errors -// call free on cp_indexed_image_t::pix when done, or call cp_free_indexed_png -cp_indexed_image_t cp_load_indexed_png(const char* file_name); -cp_indexed_image_t cp_load_indexed_png_mem(const void *png_data, int png_length); -void cp_free_indexed_png(cp_indexed_image_t* img); - -// converts paletted image into a standard RGBA image -// call free on cp_image_t::pix when done -cp_image_t cp_depallete_indexed_image(cp_indexed_image_t* img); - -// Pre-process the pixels to transform the image data to a premultiplied alpha format. -// Resource: http://www.essentialmath.com/GDC2015/VanVerth_Jim_DoingMathwRGB.pdf -void cp_premultiply(cp_image_t* img); - -struct cp_pixel_t -{ - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; -}; - -struct cp_image_t -{ - int w; - int h; - cp_pixel_t* pix; -}; - -struct cp_indexed_image_t -{ - int w; - int h; - uint8_t* pix; - uint8_t palette_len; - cp_pixel_t palette[256]; -}; - -struct cp_atlas_image_t -{ - int img_index; // index into the `imgs` array - int w, h; // pixel w/h of original image - float minx, miny; // u coordinate - float maxx, maxy; // v coordinate - int fit; // non-zero if image fit and was placed into the atlas -}; - -#define CUTE_PNG_H -#endif - -#ifdef CUTE_PNG_IMPLEMENTATION -#ifndef CUTE_PNG_IMPLEMENTATION_ONCE -#define CUTE_PNG_IMPLEMENTATION_ONCE - -#if !defined(CUTE_PNG_ALLOCA) - #define CUTE_PNG_ALLOCA alloca - - #ifdef _WIN32 - #include - #elif defined(__linux__) - #include - #endif -#endif - -#if !defined(CUTE_PNG_ALLOC) - #include - #define CUTE_PNG_ALLOC malloc -#endif - -#if !defined(CUTE_PNG_FREE) - #include - #define CUTE_PNG_FREE free -#endif - -#if !defined(CUTE_PNG_CALLOC) - #include - #define CUTE_PNG_CALLOC calloc -#endif - -#if !defined(CUTE_PNG_REALLOC) - #include - #define CUTE_PNG_REALLOC realloc -#endif - -#if !defined(CUTE_PNG_MEMCPY) - #include - #define CUTE_PNG_MEMCPY memcpy -#endif - -#if !defined(CUTE_PNG_MEMCMP) - #include - #define CUTE_PNG_MEMCMP memcmp -#endif - -#if !defined(CUTE_PNG_MEMSET) - #include - #define CUTE_PNG_MEMSET memset -#endif - -#if !defined(CUTE_PNG_ASSERT) - #include - #define CUTE_PNG_ASSERT assert -#endif - -#if !defined(CUTE_PNG_FPRINTF) - #include - #define CUTE_PNG_FPRINTF fprintf -#endif - -#if !defined(CUTE_PNG_SEEK_SET) - #include - #define CUTE_PNG_SEEK_SET SEEK_SET -#endif - -#if !defined(CUTE_PNG_SEEK_END) - #include - #define CUTE_PNG_SEEK_END SEEK_END -#endif - -#if !defined(CUTE_PNG_FILE) - #include - #define CUTE_PNG_FILE FILE -#endif - -#if !defined(CUTE_PNG_FOPEN) - #include - #define CUTE_PNG_FOPEN fopen -#endif - -#if !defined(CUTE_PNG_FSEEK) - #include - #define CUTE_PNG_FSEEK fseek -#endif - -#if !defined(CUTE_PNG_FREAD) - #include - #define CUTE_PNG_FREAD fread -#endif - -#if !defined(CUTE_PNG_FTELL) - #include - #define CUTE_PNG_FTELL ftell -#endif - -#if !defined(CUTE_PNG_FWRITE) - #include - #define CUTE_PNG_FWRITE fwrite -#endif - -#if !defined(CUTE_PNG_FCLOSE) - #include - #define CUTE_PNG_FCLOSE fclose -#endif - -#if !defined(CUTE_PNG_FERROR) - #include - #define CUTE_PNG_FERROR ferror -#endif - -static cp_pixel_t cp_make_pixel_a(uint8_t r, uint8_t g, uint8_t b, uint8_t a) -{ - cp_pixel_t p; - p.r = r; p.g = g; p.b = b; p.a = a; - return p; -} - -static cp_pixel_t cp_make_pixel(uint8_t r, uint8_t g, uint8_t b) -{ - cp_pixel_t p; - p.r = r; p.g = g; p.b = b; p.a = 0xFF; - return p; -} - -const char* cp_error_reason; -#define CUTE_PNG_FAIL() do { goto cp_err; } while (0) -#define CUTE_PNG_CHECK(X, Y) do { if (!(X)) { cp_error_reason = Y; CUTE_PNG_FAIL(); } } while (0) -#define CUTE_PNG_CALL(X) do { if (!(X)) goto cp_err; } while (0) -#define CUTE_PNG_LOOKUP_BITS 9 -#define CUTE_PNG_LOOKUP_COUNT (1 << CUTE_PNG_LOOKUP_BITS) -#define CUTE_PNG_LOOKUP_MASK (CUTE_PNG_LOOKUP_COUNT - 1) -#define CUTE_PNG_DEFLATE_MAX_BITLEN 15 - -// DEFLATE tables from RFC 1951 -uint8_t cp_fixed_table[288 + 32] = { - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -}; // 3.2.6 -uint8_t cp_permutation_order[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; // 3.2.7 -uint8_t cp_len_extra_bits[29 + 2] = { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0, 0,0 }; // 3.2.5 -uint32_t cp_len_base[29 + 2] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 0,0 }; // 3.2.5 -uint8_t cp_dist_extra_bits[30 + 2] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13, 0,0 }; // 3.2.5 -uint32_t cp_dist_base[30 + 2] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 0,0 }; // 3.2.5 - -typedef struct cp_state_t -{ - uint64_t bits; - int count; - uint32_t* words; - int word_count; - int word_index; - int bits_left; - - int final_word_available; - uint32_t final_word; - - char* out; - char* out_end; - char* begin; - - uint16_t lookup[CUTE_PNG_LOOKUP_COUNT]; - uint32_t lit[288]; - uint32_t dst[32]; - uint32_t len[19]; - uint32_t nlit; - uint32_t ndst; - uint32_t nlen; -} cp_state_t; - -static int cp_would_overflow(cp_state_t* s, int num_bits) -{ - return (s->bits_left + s->count) - num_bits < 0; -} - -static char* cp_ptr(cp_state_t* s) -{ - CUTE_PNG_ASSERT(!(s->bits_left & 7)); - return (char*)(s->words + s->word_index) - (s->count / 8); -} - -static uint64_t cp_peak_bits(cp_state_t* s, int num_bits_to_read) -{ - if (s->count < num_bits_to_read) - { - if (s->word_index < s->word_count) - { - uint32_t word = s->words[s->word_index++]; - s->bits |= (uint64_t)word << s->count; - s->count += 32; - CUTE_PNG_ASSERT(s->word_index <= s->word_count); - } - - else if (s->final_word_available) - { - uint32_t word = s->final_word; - s->bits |= (uint64_t)word << s->count; - s->count += s->bits_left; - s->final_word_available = 0; - } - } - - return s->bits; -} - -static uint32_t cp_consume_bits(cp_state_t* s, int num_bits_to_read) -{ - CUTE_PNG_ASSERT(s->count >= num_bits_to_read); - uint32_t bits = s->bits & (((uint64_t)1 << num_bits_to_read) - 1); - s->bits >>= num_bits_to_read; - s->count -= num_bits_to_read; - s->bits_left -= num_bits_to_read; - return bits; -} - -static uint32_t cp_read_bits(cp_state_t* s, int num_bits_to_read) -{ - CUTE_PNG_ASSERT(num_bits_to_read <= 32); - CUTE_PNG_ASSERT(num_bits_to_read >= 0); - CUTE_PNG_ASSERT(s->bits_left > 0); - CUTE_PNG_ASSERT(s->count <= 64); - CUTE_PNG_ASSERT(!cp_would_overflow(s, num_bits_to_read)); - cp_peak_bits(s, num_bits_to_read); - uint32_t bits = cp_consume_bits(s, num_bits_to_read); - return bits; -} - -static char* cp_read_file_to_memory(const char* path, int* size) -{ - char* data = 0; - CUTE_PNG_FILE* fp = CUTE_PNG_FOPEN(path, "rb"); - int sizeNum = 0; - - if (fp) - { - CUTE_PNG_FSEEK(fp, 0, CUTE_PNG_SEEK_END); - sizeNum = CUTE_PNG_FTELL(fp); - CUTE_PNG_FSEEK(fp, 0, CUTE_PNG_SEEK_SET); - data = (char*)CUTE_PNG_ALLOC(sizeNum + 1); - CUTE_PNG_FREAD(data, sizeNum, 1, fp); - data[sizeNum] = 0; - CUTE_PNG_FCLOSE(fp); - } - - if (size) *size = sizeNum; - return data; -} - -static uint32_t cp_rev16(uint32_t a) -{ - a = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1); - a = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2); - a = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4); - a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8); - return a; -} - -// RFC 1951 section 3.2.2 -static int cp_build(cp_state_t* s, uint32_t* tree, uint8_t* lens, int sym_count) -{ - int n, codes[16], first[16], counts[16] = { 0 }; - - // Frequency count - for (n = 0; n < sym_count; n++) counts[lens[n]]++; - - // Distribute codes - counts[0] = codes[0] = first[0] = 0; - for (n = 1; n <= 15; ++n) - { - codes[n] = (codes[n - 1] + counts[n - 1]) << 1; - first[n] = first[n - 1] + counts[n - 1]; - } - - if (s) CUTE_PNG_MEMSET(s->lookup, 0, sizeof(s->lookup)); - for (int i = 0; i < sym_count; ++i) - { - int len = lens[i]; - - if (len != 0) - { - CUTE_PNG_ASSERT(len < 16); - uint32_t code = codes[len]++; - uint32_t slot = first[len]++; - tree[slot] = (code << (32 - len)) | (i << 4) | len; - - if (s && len <= CUTE_PNG_LOOKUP_BITS) - { - int j = cp_rev16(code) >> (16 - len); - while (j < (1 << CUTE_PNG_LOOKUP_BITS)) - { - s->lookup[j] = (uint16_t)((len << CUTE_PNG_LOOKUP_BITS) | i); - j += (1 << len); - } - } - } - } - - int max_index = first[15]; - return max_index; -} - -static int cp_stored(cp_state_t* s) -{ - char* p; - - // 3.2.3 - // skip any remaining bits in current partially processed byte - cp_read_bits(s, s->count & 7); - - // 3.2.4 - // read LEN and NLEN, should complement each other - uint16_t LEN = (uint16_t)cp_read_bits(s, 16); - uint16_t NLEN = (uint16_t)cp_read_bits(s, 16); - CUTE_PNG_CHECK(LEN == (uint16_t)(~NLEN), "Failed to find LEN and NLEN as complements within stored (uncompressed) stream."); - CUTE_PNG_CHECK(s->bits_left / 8 <= (int)LEN, "Stored block extends beyond end of input stream."); - p = cp_ptr(s); - CUTE_PNG_MEMCPY(s->out, p, LEN); - s->out += LEN; - return 1; - -cp_err: - return 0; -} - -// 3.2.6 -static int cp_fixed(cp_state_t* s) -{ - s->nlit = cp_build(s, s->lit, cp_fixed_table, 288); - s->ndst = cp_build(0, s->dst, cp_fixed_table + 288, 32); - return 1; -} - -static int cp_decode(cp_state_t* s, uint32_t* tree, int hi) -{ - uint64_t bits = cp_peak_bits(s, 16); - uint32_t search = (cp_rev16((uint32_t)bits) << 16) | 0xFFFF; - int lo = 0; - while (lo < hi) - { - int guess = (lo + hi) >> 1; - if (search < tree[guess]) hi = guess; - else lo = guess + 1; - } - - uint32_t key = tree[lo - 1]; - uint32_t len = (32 - (key & 0xF)); - CUTE_PNG_ASSERT((search >> len) == (key >> len)); - - int code = cp_consume_bits(s, key & 0xF); - (void)code; - return (key >> 4) & 0xFFF; -} - -// 3.2.7 -static int cp_dynamic(cp_state_t* s) -{ - uint8_t lenlens[19] = { 0 }; - - int nlit = 257 + cp_read_bits(s, 5); - int ndst = 1 + cp_read_bits(s, 5); - int nlen = 4 + cp_read_bits(s, 4); - - for (int i = 0 ; i < nlen; ++i) - lenlens[cp_permutation_order[i]] = (uint8_t)cp_read_bits(s, 3); - - // Build the tree for decoding code lengths - s->nlen = cp_build(0, s->len, lenlens, 19); - uint8_t lens[288 + 32]; - - for (int n = 0; n < nlit + ndst;) - { - int sym = cp_decode(s, s->len, s->nlen); - switch (sym) - { - case 16: for (int i = 3 + cp_read_bits(s, 2); i; --i, ++n) lens[n] = lens[n - 1]; break; - case 17: for (int i = 3 + cp_read_bits(s, 3); i; --i, ++n) lens[n] = 0; break; - case 18: for (int i = 11 + cp_read_bits(s, 7); i; --i, ++n) lens[n] = 0; break; - default: lens[n++] = (uint8_t)sym; break; - } - } - - s->nlit = cp_build(s, s->lit, lens, nlit); - s->ndst = cp_build(0, s->dst, lens + nlit, ndst); - return 1; -} - -// 3.2.3 -static int cp_block(cp_state_t* s) -{ - while (1) - { - int symbol = cp_decode(s, s->lit, s->nlit); - - if (symbol < 256) - { - CUTE_PNG_CHECK(s->out + 1 <= s->out_end, "Attempted to overwrite out buffer while outputting a symbol."); - *s->out = (char)symbol; - s->out += 1; - } - - else if (symbol > 256) - { - symbol -= 257; - int length = cp_read_bits(s, cp_len_extra_bits[symbol]) + cp_len_base[symbol]; - int distance_symbol = cp_decode(s, s->dst, s->ndst); - int backwards_distance = cp_read_bits(s, cp_dist_extra_bits[distance_symbol]) + cp_dist_base[distance_symbol]; - CUTE_PNG_CHECK(s->out - backwards_distance >= s->begin, "Attempted to write before out buffer (invalid backwards distance)."); - CUTE_PNG_CHECK(s->out + length <= s->out_end, "Attempted to overwrite out buffer while outputting a string."); - char* src = s->out - backwards_distance; - char* dst = s->out; - s->out += length; - - switch (backwards_distance) - { - case 1: // very common in images - CUTE_PNG_MEMSET(dst, *src, length); - break; - default: while (length--) *dst++ = *src++; - } - } - - else break; - } - - return 1; - -cp_err: - return 0; -} - -// 3.2.3 -int cp_inflate(void* in, int in_bytes, void* out, int out_bytes) -{ - cp_state_t* s = (cp_state_t*)CUTE_PNG_CALLOC(1, sizeof(cp_state_t)); - s->bits = 0; - s->count = 0; - s->word_index = 0; - s->bits_left = in_bytes * 8; - - // s->words is the in-pointer rounded up to a multiple of 4 - int first_bytes = (int) ((( (size_t) in + 3) & ~3) - (size_t) in); - s->words = (uint32_t*)((char*)in + first_bytes); - s->word_count = (in_bytes - first_bytes) / 4; - int last_bytes = ((in_bytes - first_bytes) & 3); - - for (int i = 0; i < first_bytes; ++i) - s->bits |= (uint64_t)(((uint8_t*)in)[i]) << (i * 8); - - s->final_word_available = last_bytes ? 1 : 0; - s->final_word = 0; - for(int i = 0; i < last_bytes; i++) - s->final_word |= ((uint8_t*)in)[in_bytes - last_bytes+i] << (i * 8); - - s->count = first_bytes * 8; - - s->out = (char*)out; - s->out_end = s->out + out_bytes; - s->begin = (char*)out; - - int count = 0; - int bfinal; - do - { - bfinal = cp_read_bits(s, 1); - int btype = cp_read_bits(s, 2); - - switch (btype) - { - case 0: CUTE_PNG_CALL(cp_stored(s)); break; - case 1: cp_fixed(s); CUTE_PNG_CALL(cp_block(s)); break; - case 2: cp_dynamic(s); CUTE_PNG_CALL(cp_block(s)); break; - case 3: CUTE_PNG_CHECK(0, "Detected unknown block type within input stream."); - } - - ++count; - } - while (!bfinal); - - CUTE_PNG_FREE(s); - return 1; - -cp_err: - CUTE_PNG_FREE(s); - return 0; -} - -static uint8_t cp_paeth(uint8_t a, uint8_t b, uint8_t c) -{ - int p = a + b - c; - int pa = abs(p - a); - int pb = abs(p - b); - int pc = abs(p - c); - return (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; -} - -typedef struct cp_save_png_data_t -{ - uint32_t crc; - uint32_t adler; - uint32_t bits; - uint32_t prev; - uint32_t runlen; - int buflen; - int bufcap; - char* buffer; -} cp_save_png_data_t; - -uint32_t CP_CRC_TABLE[] = { - 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, - 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c -}; - -static void cp_put8(cp_save_png_data_t* s, uint32_t a) -{ - if (s->buflen >= s->bufcap) - { - s->bufcap *= 2; - s->buffer = (char*)CUTE_PNG_REALLOC(s->buffer, s->bufcap); - } - s->buffer[s->buflen++] = a; - s->crc = (s->crc >> 4) ^ CP_CRC_TABLE[(s->crc & 15) ^ (a & 15)]; - s->crc = (s->crc >> 4) ^ CP_CRC_TABLE[(s->crc & 15) ^ (a >> 4)]; -} - -static void cp_update_adler(cp_save_png_data_t* s, uint32_t v) -{ - uint32_t s1 = s->adler & 0xFFFF; - uint32_t s2 = (s->adler >> 16) & 0xFFFF; - s1 = (s1 + v) % 65521; - s2 = (s2 + s1) % 65521; - s->adler = (s2 << 16) + s1; -} - -static void cp_put32(cp_save_png_data_t* s, uint32_t v) -{ - cp_put8(s, (v >> 24) & 0xFF); - cp_put8(s, (v >> 16) & 0xFF); - cp_put8(s, (v >> 8) & 0xFF); - cp_put8(s, v & 0xFF); -} - -static void cp_put_bits(cp_save_png_data_t* s, uint32_t data, uint32_t bitcount) -{ - while (bitcount--) - { - uint32_t prev = s->bits; - s->bits = (s->bits >> 1) | ((data & 1) << 7); - data >>= 1; - - if (prev & 1) - { - cp_put8(s, s->bits); - s->bits = 0x80; - } - } -} - -static void cp_put_bitsr(cp_save_png_data_t* s, uint32_t data, uint32_t bitcount) -{ - while (bitcount--) - cp_put_bits(s, data >> bitcount, 1); -} - -static void cp_begin_chunk(cp_save_png_data_t* s, const char* id, uint32_t len) -{ - cp_put32(s, len); - s->crc = 0xFFFFFFFF; - cp_put8(s, (unsigned char)id[0]); - cp_put8(s, (unsigned char)id[1]); - cp_put8(s, (unsigned char)id[2]); - cp_put8(s, (unsigned char)id[3]); -} - -static void cp_encode_literal(cp_save_png_data_t* s, uint32_t v) -{ - // Encode a literal/length using the built-in tables. - // Could do better with a custom table but whatever. - if (v < 144) cp_put_bitsr(s, 0x030 + v - 0, 8); - else if (v < 256) cp_put_bitsr(s, 0x190 + v - 144, 9); - else if (v < 280) cp_put_bitsr(s, 0x000 + v - 256, 7); - else cp_put_bitsr(s, 0x0c0 + v - 280, 8); -} - -static void cp_encode_len(cp_save_png_data_t* s, uint32_t code, uint32_t bits, uint32_t len) -{ - cp_encode_literal(s, code + (len >> bits)); - cp_put_bits(s, len, bits); - cp_put_bits(s, 0, 5); -} - -static void cp_end_run(cp_save_png_data_t* s) -{ - s->runlen--; - cp_encode_literal(s, s->prev); - - if (s->runlen >= 67) cp_encode_len(s, 277, 4, s->runlen - 67); - else if (s->runlen >= 35) cp_encode_len(s, 273, 3, s->runlen - 35); - else if (s->runlen >= 19) cp_encode_len(s, 269, 2, s->runlen - 19); - else if (s->runlen >= 11) cp_encode_len(s, 265, 1, s->runlen - 11); - else if (s->runlen >= 3) cp_encode_len(s, 257, 0, s->runlen - 3); - else while (s->runlen--) cp_encode_literal(s, s->prev); -} - -static void cp_encode_byte(cp_save_png_data_t *s, uint8_t v) -{ - cp_update_adler(s, v); - - // Simple RLE compression. We could do better by doing a search - // to find matches, but this works pretty well TBH. - if (s->prev == v && s->runlen < 115) s->runlen++; - - else - { - if (s->runlen) cp_end_run(s); - - s->prev = v; - s->runlen = 1; - } -} - -static void cp_save_header(cp_save_png_data_t* s, cp_image_t* img) -{ - const unsigned char* hdr = (const unsigned char*)"\211PNG\r\n\032\n"; - for (int i = 0; i < 8; ++i) { - cp_put8(s, *hdr++); - } - cp_begin_chunk(s, "IHDR", 13); - cp_put32(s, img->w); - cp_put32(s, img->h); - cp_put8(s, 8); // bit depth - cp_put8(s, 6); // RGBA - cp_put8(s, 0); // compression (deflate) - cp_put8(s, 0); // filter (standard) - cp_put8(s, 0); // interlace off - cp_put32(s, ~s->crc); -} - -static void cp_save_data(cp_save_png_data_t* s, cp_image_t* img, long dataPos, long* dataSize) -{ - cp_begin_chunk(s, "IDAT", 0); - cp_put8(s, 0x08); // zlib compression method - cp_put8(s, 0x1D); // zlib compression flags - cp_put_bits(s, 3, 3); // zlib last block + fixed dictionary - - for (int y = 0; y < img->h; ++y) - { - cp_pixel_t *row = &img->pix[y * img->w]; - cp_pixel_t prev = cp_make_pixel_a(0, 0, 0, 0); - - cp_encode_byte(s, 1); // sub filter - for (int x = 0; x < img->w; ++x) - { - cp_encode_byte(s, row[x].r - prev.r); - cp_encode_byte(s, row[x].g - prev.g); - cp_encode_byte(s, row[x].b - prev.b); - cp_encode_byte(s, row[x].a - prev.a); - prev = row[x]; - } - } - - cp_end_run(s); - cp_encode_literal(s, 256); // terminator - while (s->bits != 0x80) cp_put_bits(s, 0, 1); - cp_put32(s, s->adler); - *dataSize = (s->buflen - dataPos) - 8; - cp_put32(s, ~s->crc); -} - -cp_saved_png_t cp_save_png_to_memory(const cp_image_t* img) -{ - cp_saved_png_t result = { 0 }; - cp_save_png_data_t s = { 0 }; - long dataPos, dataSize, fileSize; - if (!img) return result; - - s.adler = 1; - s.bits = 0x80; - s.prev = 0xFFFF; - s.bufcap = 1024; - s.buffer = (char*)CUTE_PNG_ALLOC(1024); - - cp_save_header(&s, (cp_image_t*)img); - dataPos = s.buflen; - cp_save_data(&s, (cp_image_t*)img, dataPos, &dataSize); - - // End chunk. - cp_begin_chunk(&s, "IEND", 0); - cp_put32(&s, ~s.crc); - - // Write back payload size. - fileSize = s.buflen; - s.buflen = dataPos; - cp_put32(&s, dataSize); - - result.size = fileSize; - result.data = s.buffer; - return result; -} - -int cp_save_png(const char* file_name, const cp_image_t* img) -{ - cp_saved_png_t s; - long err; - CUTE_PNG_FILE* fp = CUTE_PNG_FOPEN(file_name, "wb"); - if (!fp) return 1; - s = cp_save_png_to_memory(img); - CUTE_PNG_FWRITE(s.data, s.size, 1, fp); - err = CUTE_PNG_FERROR(fp); - CUTE_PNG_FCLOSE(fp); - CUTE_PNG_FREE(s.data); - return !err; -} - -typedef struct cp_raw_png_t -{ - const uint8_t* p; - const uint8_t* end; -} cp_raw_png_t; - -static uint32_t cp_make32(const uint8_t* s) -{ - return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; -} - -static const uint8_t* cp_chunk(cp_raw_png_t* png, const char* chunk, uint32_t minlen) -{ - uint32_t len = cp_make32(png->p); - const uint8_t* start = png->p; - - if (!CUTE_PNG_MEMCMP(start + 4, chunk, 4) && len >= minlen) - { - int offset = len + 12; - - if (png->p + offset <= png->end) - { - png->p += offset; - return start + 8; - } - } - - return 0; -} - -static const uint8_t* cp_find(cp_raw_png_t* png, const char* chunk, uint32_t minlen) -{ - const uint8_t *start; - while (png->p < png->end) - { - uint32_t len = cp_make32(png->p); - start = png->p; - png->p += len + 12; - - if (!CUTE_PNG_MEMCMP(start+4, chunk, 4) && len >= minlen && png->p <= png->end) - return start + 8; - } - - return 0; -} - -static int cp_unfilter(int w, int h, int bpp, uint8_t* raw) -{ - int len = w * bpp; - uint8_t *prev; - int x; - - if (h > 0) - { -#define FILTER_LOOP_FIRST(A) for (x = bpp; x < len; x++) raw[x] += A; break - switch (*raw++) - { - case 0: break; - case 1: FILTER_LOOP_FIRST(raw[x - bpp]); - case 2: break; - case 3: FILTER_LOOP_FIRST(raw[x - bpp] / 2); - case 4: FILTER_LOOP_FIRST(cp_paeth(raw[x - bpp], 0, 0)); - default: return 0; - } -#undef FILTER_LOOP_FIRST - } - - prev = raw; - raw += len; - - for (int y = 1; y < h; y++, prev = raw, raw += len) - { -#define FILTER_LOOP(A, B) for (x = 0 ; x < bpp; x++) raw[x] += A; for (; x < len; x++) raw[x] += B; break - switch (*raw++) - { - case 0: break; - case 1: FILTER_LOOP(0 , raw[x - bpp] ); - case 2: FILTER_LOOP(prev[x] , prev[x]); - case 3: FILTER_LOOP(prev[x] / 2, (raw[x - bpp] + prev[x]) / 2); - case 4: FILTER_LOOP(prev[x] , cp_paeth(raw[x - bpp], prev[x], prev[x -bpp])); - default: return 0; - } -#undef FILTER_LOOP - } - - return 1; -} - -static void cp_convert(int bpp, int w, int h, uint8_t* src, cp_pixel_t* dst) -{ - for (int y = 0; y < h; y++) - { - // skip filter byte - src++; - - for (int x = 0; x < w; x++, src += bpp) - { - switch (bpp) - { - case 1: *dst++ = cp_make_pixel(src[0], src[0], src[0]); break; - case 2: *dst++ = cp_make_pixel_a(src[0], src[0], src[0], src[1]); break; - case 3: *dst++ = cp_make_pixel(src[0], src[1], src[2]); break; - case 4: *dst++ = cp_make_pixel_a(src[0], src[1], src[2], src[3]); break; - } - } - } -} - -// http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.tRNS -static uint8_t cp_get_alpha_for_indexed_image(int index, const uint8_t* trns, uint32_t trns_len) -{ - if (!trns) return 255; - else if ((uint32_t)index >= trns_len) return 255; - else return trns[index]; -} - -static void cp_depalette(int w, int h, uint8_t* src, cp_pixel_t* dst, const uint8_t* plte, const uint8_t* trns, uint32_t trns_len) -{ - for (int y = 0; y < h; ++y) - { - // skip filter byte - ++src; - - for (int x = 0; x < w; ++x, ++src) - { - int c = *src; - uint8_t r = plte[c * 3]; - uint8_t g = plte[c * 3 + 1]; - uint8_t b = plte[c * 3 + 2]; - uint8_t a = cp_get_alpha_for_indexed_image(c, trns, trns_len); - *dst++ = cp_make_pixel_a(r, g, b, a); - } - } -} - -static uint32_t cp_get_chunk_byte_length(const uint8_t* chunk) -{ - return cp_make32(chunk - 8); -} - -static int cp_out_size(cp_image_t* img, int bpp) -{ - return (img->w + 1) * img->h * bpp; -} - -cp_image_t cp_load_png_mem(const void* png_data, int png_length) -{ - const char* sig = "\211PNG\r\n\032\n"; - const uint8_t* ihdr, *first, *plte, *trns; - int bit_depth, color_type, bpp, w, h, pix_bytes; - int compression, filter, interlace; - int datalen, offset; - uint8_t* out; - cp_image_t img = { 0 }; - uint8_t* data = 0; - cp_raw_png_t png; - png.p = (uint8_t*)png_data; - png.end = (uint8_t*)png_data + png_length; - - CUTE_PNG_CHECK(!CUTE_PNG_MEMCMP(png.p, sig, 8), "incorrect file signature (is this a png file?)"); - png.p += 8; - - ihdr = cp_chunk(&png, "IHDR", 13); - CUTE_PNG_CHECK(ihdr, "unable to find IHDR chunk"); - bit_depth = ihdr[8]; - color_type = ihdr[9]; - CUTE_PNG_CHECK(bit_depth == 8, "only bit-depth of 8 is supported"); - - switch (color_type) - { - case 0: bpp = 1; break; // greyscale - case 2: bpp = 3; break; // RGB - case 3: bpp = 1; break; // paletted - case 4: bpp = 2; break; // grey+alpha - case 6: bpp = 4; break; // RGBA - default: CUTE_PNG_CHECK(0, "unknown color type"); - } - - // +1 for filter byte (which is dumb! just stick this at file header...) - w = cp_make32(ihdr) + 1; - h = cp_make32(ihdr + 4); - CUTE_PNG_CHECK(w >= 1, "invalid IHDR chunk found, image width was less than 1"); - CUTE_PNG_CHECK(h >= 1, "invalid IHDR chunk found, image height was less than 1"); - CUTE_PNG_CHECK((int64_t) w * h * sizeof(cp_pixel_t) < INT_MAX, "image too large"); - pix_bytes = w * h * sizeof(cp_pixel_t); - img.w = w - 1; - img.h = h; - img.pix = (cp_pixel_t*)CUTE_PNG_ALLOC(pix_bytes); - CUTE_PNG_CHECK(img.pix, "unable to allocate raw image space"); - - compression = ihdr[10]; - filter = ihdr[11]; - interlace = ihdr[12]; - CUTE_PNG_CHECK(!compression, "only standard compression DEFLATE is supported"); - CUTE_PNG_CHECK(!filter, "only standard adaptive filtering is supported"); - CUTE_PNG_CHECK(!interlace, "interlacing is not supported"); - - // PLTE must come before any IDAT chunk - first = png.p; - plte = cp_find(&png, "PLTE", 0); - if (!plte) png.p = first; - else first = png.p; - - // tRNS can come after PLTE - trns = cp_find(&png, "tRNS", 0); - if (!trns) png.p = first; - else first = png.p; - - // Compute length of the DEFLATE stream through IDAT chunk data sizes - datalen = 0; - for (const uint8_t* idat = cp_find(&png, "IDAT", 0); idat; idat = cp_chunk(&png, "IDAT", 0)) - { - uint32_t len = cp_get_chunk_byte_length(idat); - datalen += len; - } - - // Copy in IDAT chunk data sections to form the compressed DEFLATE stream - png.p = first; - data = (uint8_t*)CUTE_PNG_ALLOC(datalen); - offset = 0; - for (const uint8_t* idat = cp_find(&png, "IDAT", 0); idat; idat = cp_chunk(&png, "IDAT", 0)) - { - uint32_t len = cp_get_chunk_byte_length(idat); - CUTE_PNG_MEMCPY(data + offset, idat, len); - offset += len; - } - - // check for proper zlib structure in DEFLATE stream - CUTE_PNG_CHECK(data && datalen >= 6, "corrupt zlib structure in DEFLATE stream"); - CUTE_PNG_CHECK((data[0] & 0x0f) == 0x08, "only zlib compression method (RFC 1950) is supported"); - CUTE_PNG_CHECK((data[0] & 0xf0) <= 0x70, "innapropriate window size detected"); - CUTE_PNG_CHECK(!(data[1] & 0x20), "preset dictionary is present and not supported"); - - // check for integer overflow - CUTE_PNG_CHECK(cp_out_size(&img, 4) >= 1, "invalid image size found"); - CUTE_PNG_CHECK(cp_out_size(&img, bpp) >= 1, "invalid image size found"); - - out = (uint8_t*)img.pix + cp_out_size(&img, 4) - cp_out_size(&img, bpp); - CUTE_PNG_CHECK(cp_inflate(data + 2, datalen - 6, out, pix_bytes), "DEFLATE algorithm failed"); - CUTE_PNG_CHECK(cp_unfilter(img.w, img.h, bpp, out), "invalid filter byte found"); - - if (color_type == 3) - { - CUTE_PNG_CHECK(plte, "color type of indexed requires a PLTE chunk"); - uint32_t trns_len = trns ? cp_get_chunk_byte_length(trns) : 0; - cp_depalette(img.w, img.h, out, img.pix, plte, trns, trns_len); - } - else cp_convert(bpp, img.w, img.h, out, img.pix); - - CUTE_PNG_FREE(data); - return img; - -cp_err: - CUTE_PNG_FREE(data); - CUTE_PNG_FREE(img.pix); - img.pix = 0; - - return img; -} - -cp_image_t cp_load_blank(int w, int h) -{ - cp_image_t img; - img.w = w; - img.h = h; - img.pix = (cp_pixel_t*)CUTE_PNG_ALLOC(w * h * sizeof(cp_pixel_t)); - return img; -} - -cp_image_t cp_load_png(const char *file_name) -{ - cp_image_t img = { 0 }; - int len; - void* data = cp_read_file_to_memory(file_name, &len); - if (!data) return img; - img = cp_load_png_mem(data, len); - CUTE_PNG_FREE(data); - return img; -} - -void cp_free_png(cp_image_t* img) -{ - CUTE_PNG_FREE(img->pix); - img->pix = 0; - img->w = img->h = 0; -} - -void cp_flip_image_horizontal(cp_image_t* img) -{ - cp_pixel_t* pix = img->pix; - int w = img->w; - int h = img->h; - int flips = h / 2; - for (int i = 0; i < flips; ++i) - { - cp_pixel_t* a = pix + w * i; - cp_pixel_t* b = pix + w * (h - i - 1); - for (int j = 0; j < w; ++j) - { - cp_pixel_t t = *a; - *a = *b; - *b = t; - ++a; - ++b; - } - } -} - -void cp_load_png_wh(const void* png_data, int png_length, int* w_out, int* h_out) -{ - const char* sig = "\211PNG\r\n\032\n"; - const uint8_t* ihdr; - cp_raw_png_t png; - int w, h; - png.p = (uint8_t*)png_data; - png.end = (uint8_t*)png_data + png_length; - - if (w_out) *w_out = 0; - if (h_out) *h_out = 0; - - CUTE_PNG_CHECK(!CUTE_PNG_MEMCMP(png.p, sig, 8), "incorrect file signature (is this a png file?)"); - png.p += 8; - - ihdr = cp_chunk(&png, "IHDR", 13); - CUTE_PNG_CHECK(ihdr, "unable to find IHDR chunk"); - - // +1 for filter byte (which is dumb! just stick this at file header...) - w = cp_make32(ihdr) + 1; - h = cp_make32(ihdr + 4); - if (w_out) *w_out = w - 1; - if (h_out) *h_out = h; - - cp_err:; -} - -cp_indexed_image_t cp_load_indexed_png(const char* file_name) -{ - cp_indexed_image_t img = { 0 }; - int len; - void* data = cp_read_file_to_memory(file_name, &len); - if (!data) return img; - img = cp_load_indexed_png_mem(data, len); - CUTE_PNG_FREE(data); - return img; -} - -static void cp_unpack_indexed_rows(int w, int h, uint8_t* src, uint8_t* dst) -{ - for (int y = 0; y < h; ++y) - { - // skip filter byte - ++src; - - for (int x = 0; x < w; ++x, ++src) - { - *dst++ = *src; - } - } -} - -void cp_unpack_palette(cp_pixel_t* dst, const uint8_t* plte, int plte_len, const uint8_t* trns, int trns_len) -{ - for (int i = 0; i < plte_len * 3; i += 3) - { - unsigned char r = plte[i]; - unsigned char g = plte[i + 1]; - unsigned char b = plte[i + 2]; - unsigned char a = cp_get_alpha_for_indexed_image(i / 3, trns, trns_len); - cp_pixel_t p = cp_make_pixel_a(r, g, b, a); - *dst++ = p; - } -} - -cp_indexed_image_t cp_load_indexed_png_mem(const void *png_data, int png_length) -{ - const char* sig = "\211PNG\r\n\032\n"; - const uint8_t* ihdr, *first, *plte, *trns; - int bit_depth, color_type, bpp, w, h, pix_bytes; - int compression, filter, interlace; - int datalen, offset; - int plte_len; - uint8_t* out; - cp_indexed_image_t img = { 0 }; - uint8_t* data = 0; - cp_raw_png_t png; - png.p = (uint8_t*)png_data; - png.end = (uint8_t*)png_data + png_length; - - CUTE_PNG_CHECK(!CUTE_PNG_MEMCMP(png.p, sig, 8), "incorrect file signature (is this a png file?)"); - png.p += 8; - - ihdr = cp_chunk(&png, "IHDR", 13); - CUTE_PNG_CHECK(ihdr, "unable to find IHDR chunk"); - bit_depth = ihdr[8]; - color_type = ihdr[9]; - bpp = 1; // bytes per pixel - CUTE_PNG_CHECK(bit_depth == 8, "only bit-depth of 8 is supported"); - CUTE_PNG_CHECK(color_type == 3, "only indexed png images (images with a palette) are valid for cp_load_indexed_png_mem"); - - // +1 for filter byte (which is dumb! just stick this at file header...) - w = cp_make32(ihdr) + 1; - h = cp_make32(ihdr + 4); - CUTE_PNG_CHECK((int64_t) w * h * sizeof(uint8_t) < INT_MAX, "image too large"); - pix_bytes = w * h * sizeof(uint8_t); - img.w = w - 1; - img.h = h; - img.pix = (uint8_t*)CUTE_PNG_ALLOC(pix_bytes); - CUTE_PNG_CHECK(img.pix, "unable to allocate raw image space"); - - compression = ihdr[10]; - filter = ihdr[11]; - interlace = ihdr[12]; - CUTE_PNG_CHECK(!compression, "only standard compression DEFLATE is supported"); - CUTE_PNG_CHECK(!filter, "only standard adaptive filtering is supported"); - CUTE_PNG_CHECK(!interlace, "interlacing is not supported"); - - // PLTE must come before any IDAT chunk - first = png.p; - plte = cp_find(&png, "PLTE", 0); - if (!plte) png.p = first; - else first = png.p; - - // tRNS can come after PLTE - trns = cp_find(&png, "tRNS", 0); - if (!trns) png.p = first; - else first = png.p; - - // Compute length of the DEFLATE stream through IDAT chunk data sizes - datalen = 0; - for (const uint8_t* idat = cp_find(&png, "IDAT", 0); idat; idat = cp_chunk(&png, "IDAT", 0)) - { - uint32_t len = cp_get_chunk_byte_length(idat); - datalen += len; - } - - // Copy in IDAT chunk data sections to form the compressed DEFLATE stream - png.p = first; - data = (uint8_t*)CUTE_PNG_ALLOC(datalen); - offset = 0; - for (const uint8_t* idat = cp_find(&png, "IDAT", 0); idat; idat = cp_chunk(&png, "IDAT", 0)) - { - uint32_t len = cp_get_chunk_byte_length(idat); - CUTE_PNG_MEMCPY(data + offset, idat, len); - offset += len; - } - - // check for proper zlib structure in DEFLATE stream - CUTE_PNG_CHECK(data && datalen >= 6, "corrupt zlib structure in DEFLATE stream"); - CUTE_PNG_CHECK((data[0] & 0x0f) == 0x08, "only zlib compression method (RFC 1950) is supported"); - CUTE_PNG_CHECK((data[0] & 0xf0) <= 0x70, "innapropriate window size detected"); - CUTE_PNG_CHECK(!(data[1] & 0x20), "preset dictionary is present and not supported"); - - out = img.pix; - CUTE_PNG_CHECK(cp_inflate(data + 2, datalen - 6, out, pix_bytes), "DEFLATE algorithm failed"); - CUTE_PNG_CHECK(cp_unfilter(img.w, img.h, bpp, out), "invalid filter byte found"); - cp_unpack_indexed_rows(img.w, img.h, out, img.pix); - - plte_len = cp_get_chunk_byte_length(plte) / 3; - cp_unpack_palette(img.palette, plte, plte_len, trns, cp_get_chunk_byte_length(trns)); - img.palette_len = (uint8_t)plte_len; - - CUTE_PNG_FREE(data); - return img; - -cp_err: - CUTE_PNG_FREE(data); - CUTE_PNG_FREE(img.pix); - img.pix = 0; - - return img; -} - -void cp_free_indexed_png(cp_indexed_image_t* img) -{ - CUTE_PNG_FREE(img->pix); - img->pix = 0; - img->w = img->h = 0; -} - -cp_image_t cp_depallete_indexed_image(cp_indexed_image_t* img) -{ - cp_image_t out = { 0 }; - out.w = img->w; - out.h = img->h; - out.pix = (cp_pixel_t*)CUTE_PNG_ALLOC(sizeof(cp_pixel_t) * out.w * out.h); - - cp_pixel_t* dst = out.pix; - uint8_t* src = img->pix; - - for (int y = 0; y < out.h; ++y) - { - for (int x = 0; x < out.w; ++x) - { - int index = *src++; - cp_pixel_t p = img->palette[index]; - *dst++ = p; - } - } - - return out; -} - -typedef struct cp_v2i_t -{ - int x; - int y; -} cp_v2i_t; - -typedef struct cp_integer_image_t -{ - int img_index; - cp_v2i_t size; - cp_v2i_t min; - cp_v2i_t max; - int fit; -} cp_integer_image_t; - -static cp_v2i_t cp_v2i(int x, int y) -{ - cp_v2i_t v; - v.x = x; - v.y = y; - return v; -} - -static cp_v2i_t cp_sub(cp_v2i_t a, cp_v2i_t b) -{ - cp_v2i_t v; - v.x = a.x - b.x; - v.y = a.y - b.y; - return v; -} - -static cp_v2i_t cp_add(cp_v2i_t a, cp_v2i_t b) -{ - cp_v2i_t v; - v.x = a.x + b.x; - v.y = a.y + b.y; - return v; -} - -typedef struct cp_atlas_node_t -{ - cp_v2i_t size; - cp_v2i_t min; - cp_v2i_t max; -} cp_atlas_node_t; - -static cp_atlas_node_t* cp_best_fit(int sp, const cp_image_t* png, cp_atlas_node_t* nodes) -{ - int bestVolume = INT_MAX; - cp_atlas_node_t *best_node = 0; - int width = png->w; - int height = png->h; - int png_volume = width * height; - - for (int i = 0; i < sp; ++i) - { - cp_atlas_node_t *node = nodes + i; - int can_contain = node->size.x >= width && node->size.y >= height; - if (can_contain) - { - int node_volume = node->size.x * node->size.y; - if (node_volume == png_volume) return node; - if (node_volume < bestVolume) - { - bestVolume = node_volume; - best_node = node; - } - } - } - - return best_node; -} - -static int cp_perimeter_pred(cp_integer_image_t* a, cp_integer_image_t* b) -{ - int perimeterA = 2 * (a->size.x + a->size.y); - int perimeterB = 2 * (b->size.x + b->size.y); - return perimeterB < perimeterA; -} - -void cp_premultiply(cp_image_t* img) -{ - int w = img->w; - int h = img->h; - int stride = w * sizeof(cp_pixel_t); - uint8_t* data = (uint8_t*)img->pix; - - for(int i = 0; i < (int)stride * h; i += sizeof(cp_pixel_t)) - { - float a = (float)data[i + 3] / 255.0f; - float r = (float)data[i + 0] / 255.0f; - float g = (float)data[i + 1] / 255.0f; - float b = (float)data[i + 2] / 255.0f; - r *= a; - g *= a; - b *= a; - data[i + 0] = (uint8_t)(r * 255.0f); - data[i + 1] = (uint8_t)(g * 255.0f); - data[i + 2] = (uint8_t)(b * 255.0f); - } -} - -static void cp_qsort(cp_integer_image_t* items, int count) -{ - if (count <= 1) return; - - cp_integer_image_t pivot = items[count - 1]; - int low = 0; - for (int i = 0; i < count - 1; ++i) - { - if (cp_perimeter_pred(items + i, &pivot)) - { - cp_integer_image_t tmp = items[i]; - items[i] = items[low]; - items[low] = tmp; - low++; - } - } - - items[count - 1] = items[low]; - items[low] = pivot; - cp_qsort(items, low); - cp_qsort(items + low + 1, count - 1 - low); -} - -static void cp_write_pixel(char* mem, long color) { - mem[0] = (color >> 24) & 0xFF; - mem[1] = (color >> 16) & 0xFF; - mem[2] = (color >> 8) & 0xFF; - mem[3] = (color >> 0) & 0xFF; -} - -cp_image_t cp_make_atlas(int atlas_width, int atlas_height, const cp_image_t* pngs, int png_count, cp_atlas_image_t* imgs_out) -{ - float w0, h0, div, wTol, hTol; - int atlas_image_size, atlas_stride, sp; - void* atlas_pixels = 0; - int atlas_node_capacity = png_count * 2; - cp_image_t atlas_image; - cp_integer_image_t* images = 0; - cp_atlas_node_t* nodes = 0; - - atlas_image.w = atlas_width; - atlas_image.h = atlas_height; - atlas_image.pix = 0; - - CUTE_PNG_CHECK(pngs, "pngs array was NULL"); - CUTE_PNG_CHECK(imgs_out, "imgs_out array was NULL"); - - images = (cp_integer_image_t*)CUTE_PNG_ALLOCA(sizeof(cp_integer_image_t) * png_count); - nodes = (cp_atlas_node_t*)CUTE_PNG_ALLOC(sizeof(cp_atlas_node_t) * atlas_node_capacity); - CUTE_PNG_CHECK(images, "out of mem"); - CUTE_PNG_CHECK(nodes, "out of mem"); - - for (int i = 0; i < png_count; ++i) - { - const cp_image_t* png = pngs + i; - cp_integer_image_t* image = images + i; - image->fit = 0; - image->size = cp_v2i(png->w, png->h); - image->img_index = i; - } - - // Sort PNGs from largest to smallest - cp_qsort(images, png_count); - - // stack pointer, the stack is the nodes array which we will - // allocate nodes from as necessary. - sp = 1; - - nodes[0].min = cp_v2i(0, 0); - nodes[0].max = cp_v2i(atlas_width, atlas_height); - nodes[0].size = cp_v2i(atlas_width, atlas_height); - - // Nodes represent empty space in the atlas. Placing a texture into the - // atlas involves splitting a node into two smaller pieces (or, if a - // perfect fit is found, deleting the node). - for (int i = 0; i < png_count; ++i) - { - cp_integer_image_t* image = images + i; - const cp_image_t* png = pngs + image->img_index; - int width = png->w; - int height = png->h; - cp_atlas_node_t *best_fit = cp_best_fit(sp, png, nodes); - if (CUTE_PNG_ATLAS_MUST_FIT) CUTE_PNG_CHECK(best_fit, "Not enough room to place image in atlas."); - else if (!best_fit) - { - image->fit = 0; - continue; - } - - image->min = best_fit->min; - image->max = cp_add(image->min, image->size); - - if (best_fit->size.x == width && best_fit->size.y == height) - { - cp_atlas_node_t* last_node = nodes + --sp; - *best_fit = *last_node; - image->fit = 1; - - continue; - } - - image->fit = 1; - - if (sp == atlas_node_capacity) - { - int new_capacity = atlas_node_capacity * 2; - cp_atlas_node_t* new_nodes = (cp_atlas_node_t*)CUTE_PNG_ALLOC(sizeof(cp_atlas_node_t) * new_capacity); - CUTE_PNG_CHECK(new_nodes, "out of mem"); - CUTE_PNG_MEMCPY(new_nodes, nodes, sizeof(cp_atlas_node_t) * sp); - CUTE_PNG_FREE(nodes); - // best_fit became a dangling pointer, so relocate it - best_fit = new_nodes + (best_fit - nodes); - nodes = new_nodes; - atlas_node_capacity = new_capacity; - } - - cp_atlas_node_t* new_node = nodes + sp++; - new_node->min = best_fit->min; - - // Split bestFit along x or y, whichever minimizes - // fragmentation of empty space - cp_v2i_t d = cp_sub(best_fit->size, cp_v2i(width, height)); - if (d.x < d.y) - { - new_node->size.x = d.x; - new_node->size.y = height; - new_node->min.x += width; - - best_fit->size.y = d.y; - best_fit->min.y += height; - } - - else - { - new_node->size.x = width; - new_node->size.y = d.y; - new_node->min.y += height; - - best_fit->size.x = d.x; - best_fit->min.x += width; - } - - new_node->max = cp_add(new_node->min, new_node->size); - } - - // Write the final atlas image, use CUTE_PNG_ATLAS_EMPTY_COLOR as base color - atlas_stride = atlas_width * sizeof(cp_pixel_t); - atlas_image_size = atlas_width * atlas_height * sizeof(cp_pixel_t); - atlas_pixels = CUTE_PNG_ALLOC(atlas_image_size); - CUTE_PNG_CHECK(atlas_pixels, "out of mem"); - - for(int i = 0; i < atlas_image_size; i += sizeof(cp_pixel_t)) { - cp_write_pixel((char*)atlas_pixels + i, CUTE_PNG_ATLAS_EMPTY_COLOR); - } - - for (int i = 0; i < png_count; ++i) - { - cp_integer_image_t* image = images + i; - - if (image->fit) - { - const cp_image_t* png = pngs + image->img_index; - char* pixels = (char*)png->pix; - cp_v2i_t min = image->min; - cp_v2i_t max = image->max; - int atlas_offset = min.x * sizeof(cp_pixel_t); - int tex_stride = png->w * sizeof(cp_pixel_t); - - for (int row = min.y, y = 0; row < max.y; ++row, ++y) - { - void* row_ptr = (char*)atlas_pixels + (row * atlas_stride + atlas_offset); - CUTE_PNG_MEMCPY(row_ptr, pixels + y * tex_stride, tex_stride); - } - } - } - - atlas_image.pix = (cp_pixel_t*)atlas_pixels; - - // squeeze UVs inward by 128th of a pixel - // this prevents atlas bleeding. tune as necessary for good results. - w0 = 1.0f / (float)(atlas_width); - h0 = 1.0f / (float)(atlas_height); - div = 1.0f / 128.0f; - wTol = w0 * div; - hTol = h0 * div; - - for (int i = 0; i < png_count; ++i) - { - cp_integer_image_t* image = images + i; - cp_atlas_image_t* img_out = imgs_out + i; - - img_out->img_index = image->img_index; - img_out->w = image->size.x; - img_out->h = image->size.y; - img_out->fit = image->fit; - - if (image->fit) - { - cp_v2i_t min = image->min; - cp_v2i_t max = image->max; - - float min_x = (float)min.x * w0 + wTol; - float min_y = (float)min.y * h0 + hTol; - float max_x = (float)max.x * w0 - wTol; - float max_y = (float)max.y * h0 - hTol; - - // flip image on y axis - if (CUTE_PNG_ATLAS_FLIP_Y_AXIS_FOR_UV) - { - float tmp = min_y; - min_y = max_y; - max_y = tmp; - } - - img_out->minx = min_x; - img_out->miny = min_y; - img_out->maxx = max_x; - img_out->maxy = max_y; - } - } - - CUTE_PNG_FREE(nodes); - return atlas_image; - -cp_err: - CUTE_PNG_FREE(atlas_pixels); - CUTE_PNG_FREE(nodes); - atlas_image.pix = 0; - return atlas_image; -} - -int cp_default_save_atlas(const char* out_path_image, const char* out_path_atlas_txt, const cp_image_t* atlas, const cp_atlas_image_t* imgs, int img_count, const char** names) -{ - CUTE_PNG_FILE* fp = CUTE_PNG_FOPEN(out_path_atlas_txt, "wt"); - CUTE_PNG_CHECK(fp, "unable to open out_path_atlas_txt in cp_default_save_atlas"); - - CUTE_PNG_FPRINTF(fp, "%s\n%d\n\n", out_path_image, img_count); - - for (int i = 0; i < img_count; ++i) - { - const cp_atlas_image_t* image = imgs + i; - const char* name = names ? names[image->img_index] : 0; - - if (image->fit) - { - int width = image->w; - int height = image->h; - float min_x = image->minx; - float min_y = image->miny; - float max_x = image->maxx; - float max_y = image->maxy; - - if (name) CUTE_PNG_FPRINTF(fp, "{ \"%s\", w = %d, h = %d, u = { %.10f, %.10f }, v = { %.10f, %.10f } }\n", name, width, height, min_x, min_y, max_x, max_y); - else CUTE_PNG_FPRINTF(fp, "{ w = %d, h = %d, u = { %.10f, %.10f }, v = { %.10f, %.10f } }\n", width, height, min_x, min_y, max_x, max_y); - } - } - - // Save atlas image PNG to disk - CUTE_PNG_CHECK(cp_save_png(out_path_image, atlas), "failed to save atlas image to disk"); - -cp_err: - CUTE_PNG_FCLOSE(fp); - return 0; -} - -#endif // CUTE_PNG_IMPLEMENTATION_ONCE -#endif // CUTE_PNG_IMPLEMENTATION - -/* - ------------------------------------------------------------------------------ - This software is available under 2 licenses - you may choose the one you like. - ------------------------------------------------------------------------------ - ALTERNATIVE A - zlib license - Copyright (c) 2019 Randy Gaul http://www.randygaul.net - This software is provided 'as-is', without any express or implied warranty. - In no event will the authors be held liable for any damages arising from - the use of this software. - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - ------------------------------------------------------------------------------ - ALTERNATIVE B - Public Domain (www.unlicense.org) - This is free and unencumbered software released into the public domain. - Anyone is free to copy, modify, publish, use, compile, sell, or distribute this - software, either in source code form or as a compiled binary, for any purpose, - commercial or non-commercial, and by any means. - In jurisdictions that recognize copyright laws, the author or authors of this - software dedicate any and all copyright interest in the software to the public - domain. We make this dedication for the benefit of the public at large and to - the detriment of our heirs and successors. We intend this dedication to be an - overt act of relinquishment in perpetuity of all present and future rights to - this software under copyright law. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ------------------------------------------------------------------------------ -*/