diff --git a/tools/3rd_aseprite.h b/tools/3rd_aseprite.h deleted file mode 100644 index f892b1e..0000000 --- a/tools/3rd_aseprite.h +++ /dev/null @@ -1,1348 +0,0 @@ -#define ASE_TRIMS 1 //< @r-lyeh - -/* - ------------------------------------------------------------------------------ - Licensing information can be found at the end of the file. - ------------------------------------------------------------------------------ - - cute_aseprite.h - v1.02 - - To create implementation (the function definitions) - #define CUTE_ASEPRITE_IMPLEMENTATION - in *one* C/CPP file (translation unit) that includes this file - - - SUMMARY - - cute_asesprite.h is a single-file header that implements some functions to - parse .ase/.aseprite files. The entire file is parsed all at once and some - structs are filled out then handed back to you. - - - LIMITATIONS - - Only the "normal" blend mode for layers is supported. As a workaround try - using the "merge down" function in Aseprite to create a normal layer. - Supporting all blend modes would take too much code to be worth it. - - Does not support very old versions of Aseprite (with old palette chunks - 0x0004 or 0x0011). Also does not support deprecated mask chunk. - - sRGB and ICC profiles are parsed but completely ignored when blending - frames together. If you want these to be used when composing frames you - have to do this yourself. - - - SPECIAL THANKS - - Special thanks to Noel Berry for the blend code in his reference C++ - implementation (https://github.com/NoelFB/blah). - - Special thanks to Richard Mitton for the initial implementation of the - zlib inflater. - - - Revision history: - 1.00 (08/25/2020) initial release - 1.01 (08/31/2020) fixed memleaks, tag parsing bug (crash), blend bugs - 1.02 (02/05/2022) fixed icc profile parse bug, support transparent pal- - ette index, can parse 1.3 files (no tileset support) -*/ - -/* - DOCUMENTATION - - Simply load an .ase or .aseprite file from disk or from memory like so. - - ase_t* ase = cute_aseprite_load_from_file("data/player.aseprite", NULL); - - - Then access the fields directly, assuming you have your own `Animation` type. - - int w = ase->w; - int h = ase->h; - Animation anim = { 0 }; // Your custom animation data type. - - for (int i = 0; i < ase->frame_count; ++i) { - ase_frame_t* frame = ase->frames + i; - anim.add_frame(frame->duration_milliseconds, frame->pixels); - } - - - Then free it up when done. - - cute_aseprite_free(ase); - - - DATA STRUCTURES - - Aseprite files have frames, layers, and cels. A single frame is one frame of an - animation, formed by blending all the cels of an animation together. There is - one cel per layer per frame. Each cel contains its own pixel data. - - The frame's pixels are automatically assumed to have been blended by the `normal` - blend mode. A warning is emit if any other blend mode is encountered. Feel free - to update the pixels of each frame with your own implementation of blending - functions. The frame's pixels are merely provided like this for convenience. - - - BUGS AND CRASHES - - This header is quite new and it takes time to test all the parse paths. Don't be - shy about opening a GitHub issue if there's a crash! It's quite easy to update - the parser as long as you upload your .ase file that shows the bug. - - https://github.com/RandyGaul/cute_headers/issues -*/ - -#ifndef CUTE_ASEPRITE_H -#define CUTE_ASEPRITE_H - -typedef struct ase_t ase_t; - -ase_t* cute_aseprite_load_from_file(const char* path, void* mem_ctx); -ase_t* cute_aseprite_load_from_memory(const void* memory, int size, void* mem_ctx); -void cute_aseprite_free(ase_t* aseprite); - -#define CUTE_ASEPRITE_MAX_LAYERS (64) -#define CUTE_ASEPRITE_MAX_SLICES (128) -#define CUTE_ASEPRITE_MAX_PALETTE_ENTRIES (1024) -#define CUTE_ASEPRITE_MAX_TAGS (256) - -#include - -typedef struct ase_color_t ase_color_t; -typedef struct ase_frame_t ase_frame_t; -typedef struct ase_layer_t ase_layer_t; -typedef struct ase_cel_t ase_cel_t; -typedef struct ase_tag_t ase_tag_t; -typedef struct ase_slice_t ase_slice_t; -typedef struct ase_palette_entry_t ase_palette_entry_t; -typedef struct ase_palette_t ase_palette_t; -typedef struct ase_udata_t ase_udata_t; -typedef struct ase_cel_extra_chunk_t ase_cel_extra_chunk_t; -typedef struct ase_color_profile_t ase_color_profile_t; -typedef struct ase_fixed_t ase_fixed_t; -typedef struct ase_cel_extra_chunk_t ase_cel_extra_chunk_t; - -struct ase_color_t -{ - uint8_t r, g, b, a; -}; - -struct ase_fixed_t -{ - uint16_t a; - uint16_t b; -}; - -struct ase_udata_t -{ - int has_color; - ase_color_t color; - int has_text; - const char* text; -}; - -typedef enum ase_layer_flags_t -{ - ASE_LAYER_FLAGS_VISIBLE = 0x01, - ASE_LAYER_FLAGS_EDITABLE = 0x02, - ASE_LAYER_FLAGS_LOCK_MOVEMENT = 0x04, - ASE_LAYER_FLAGS_BACKGROUND = 0x08, - ASE_LAYER_FLAGS_PREFER_LINKED_CELS = 0x10, - ASE_LAYER_FLAGS_COLLAPSED = 0x20, - ASE_LAYER_FLAGS_REFERENCE = 0x40, -} ase_layer_flags_t; - -typedef enum ase_layer_type_t -{ - ASE_LAYER_TYPE_NORMAL, - ASE_LAYER_TYPE_GROUP, -} ase_layer_type_t; - -struct ase_layer_t -{ - ase_layer_flags_t flags; - ase_layer_type_t type; - const char* name; - ase_layer_t* parent; - float opacity; - ase_udata_t udata; -}; - -struct ase_cel_extra_chunk_t -{ - int precise_bounds_are_set; - ase_fixed_t precise_x; - ase_fixed_t precise_y; - ase_fixed_t w, h; -}; - -struct ase_cel_t -{ - ase_layer_t* layer; - void* pixels; - int w, h; - int x, y; - float opacity; - int is_linked; - uint16_t linked_frame_index; - int has_extra; - ase_cel_extra_chunk_t extra; - ase_udata_t udata; -}; - -struct ase_frame_t -{ - ase_t* ase; - int duration_milliseconds; - ase_color_t* pixels; - int cel_count; - ase_cel_t cels[CUTE_ASEPRITE_MAX_LAYERS]; -}; - -typedef enum ase_animation_direction_t -{ - ASE_ANIMATION_DIRECTION_FORWARDS, - ASE_ANIMATION_DIRECTION_BACKWARDS, - ASE_ANIMATION_DIRECTION_PINGPONG, -} ase_animation_direction_t; - -struct ase_tag_t -{ - int from_frame; - int to_frame; - ase_animation_direction_t loop_animation_direction; - uint8_t r, g, b; - const char* name; - ase_udata_t udata; -}; - -struct ase_slice_t -{ - const char* name; - int frame_number; - int origin_x; - int origin_y; - int w, h; - - int has_center_as_9_slice; - int center_x; - int center_y; - int center_w; - int center_h; - - int has_pivot; - int pivot_x; - int pivot_y; - - ase_udata_t udata; -}; - -struct ase_palette_entry_t -{ - ase_color_t color; - const char* color_name; -}; - -struct ase_palette_t -{ - int entry_count; - ase_palette_entry_t entries[CUTE_ASEPRITE_MAX_PALETTE_ENTRIES]; -}; - -typedef enum ase_color_profile_type_t -{ - ASE_COLOR_PROFILE_TYPE_NONE, - ASE_COLOR_PROFILE_TYPE_SRGB, - ASE_COLOR_PROFILE_TYPE_EMBEDDED_ICC, -} ase_color_profile_type_t; - -struct ase_color_profile_t -{ - ase_color_profile_type_t type; - int use_fixed_gamma; - ase_fixed_t gamma; - uint32_t icc_profile_data_length; - void* icc_profile_data; -}; - -typedef enum ase_mode_t -{ - ASE_MODE_RGBA, - ASE_MODE_GRAYSCALE, - ASE_MODE_INDEXED -} ase_mode_t; - -struct ase_t -{ - ase_mode_t mode; - int w, h; - int transparent_palette_entry_index; - int number_of_colors; - int pixel_w; - int pixel_h; - int grid_x; - int grid_y; - int grid_w; - int grid_h; - int has_color_profile; - ase_color_profile_t color_profile; - ase_palette_t palette; - - int layer_count; - ase_layer_t layers[CUTE_ASEPRITE_MAX_LAYERS]; - - int frame_count; - ase_frame_t* frames; - - int tag_count; - ase_tag_t tags[CUTE_ASEPRITE_MAX_TAGS]; - - int slice_count; - ase_slice_t slices[CUTE_ASEPRITE_MAX_SLICES]; - - void* mem_ctx; -}; - -#endif // CUTE_ASEPRITE_H - -#ifdef CUTE_ASEPRITE_IMPLEMENTATION -#ifndef CUTE_ASEPRITE_IMPLEMENTATION_ONCE -#define CUTE_ASEPRITE_IMPLEMENTATION_ONCE - -#ifndef _CRT_SECURE_NO_WARNINGS - #define _CRT_SECURE_NO_WARNINGS -#endif - -#ifndef _CRT_NONSTDC_NO_DEPRECATE - #define _CRT_NONSTDC_NO_DEPRECATE -#endif - -#if !defined(CUTE_ASEPRITE_ALLOC) - #include - #define CUTE_ASEPRITE_ALLOC(size, ctx) malloc(size) - #define CUTE_ASEPRITE_FREE(mem, ctx) free(mem) -#endif - -#if !defined(CUTE_ASEPRITE_UNUSED) - #if defined(_MSC_VER) - #define CUTE_ASEPRITE_UNUSED(x) (void)x - #else - #define CUTE_ASEPRITE_UNUSED(x) (void)(sizeof(x)) - #endif -#endif - -#if !defined(CUTE_ASEPRITE_MEMCPY) - #include // memcpy - #define CUTE_ASEPRITE_MEMCPY memcpy -#endif - -#if !defined(CUTE_ASEPRITE_MEMSET) - #include // memset - #define CUTE_ASEPRITE_MEMSET memset -#endif - -#if !defined(CUTE_ASEPRITE_ASSERT) - #include - #define CUTE_ASEPRITE_ASSERT assert -#endif - -#if !defined(CUTE_ASEPRITE_SEEK_SET) - #include // SEEK_SET - #define CUTE_ASEPRITE_SEEK_SET SEEK_SET -#endif - -#if !defined(CUTE_ASEPRITE_SEEK_END) - #include // SEEK_END - #define CUTE_ASEPRITE_SEEK_END SEEK_END -#endif - -#if !defined(CUTE_ASEPRITE_FILE) - #include // FILE - #define CUTE_ASEPRITE_FILE FILE -#endif - -#if !defined(CUTE_ASEPRITE_FOPEN) - #include // fopen - #define CUTE_ASEPRITE_FOPEN fopen -#endif - -#if !defined(CUTE_ASEPRITE_FSEEK) - #include // fseek - #define CUTE_ASEPRITE_FSEEK fseek -#endif - -#if !defined(CUTE_ASEPRITE_FREAD) - #include // fread - #define CUTE_ASEPRITE_FREAD fread -#endif - -#if !defined(CUTE_ASEPRITE_FTELL) - #include // ftell - #define CUTE_ASEPRITE_FTELL ftell -#endif - -#if !defined(CUTE_ASEPRITE_FCLOSE) - #include // fclose - #define CUTE_ASEPRITE_FCLOSE fclose -#endif - -static const char* s_error_file = NULL; // The filepath of the file being parsed. NULL if from memory. -static const char* s_error_reason; // Used to capture errors during DEFLATE parsing. - -#if !defined(CUTE_ASEPRITE_WARNING) - #define CUTE_ASEPRITE_WARNING(msg) cute_aseprite_warning(msg, __LINE__) - - static int s_error_cline; // The line in cute_aseprite.h where the error was triggered. - void cute_aseprite_warning(const char* warning, int line) - { - s_error_cline = line; - const char *error_file = s_error_file ? s_error_file : "MEMORY"; - printf("WARNING (cute_aseprite.h:%i): %s (%s)\n", s_error_cline, warning, error_file); - } -#endif - -#define CUTE_ASEPRITE_FAIL() do { goto ase_err; } while (0) -#define CUTE_ASEPRITE_CHECK(X, Y) do { if (!(X)) { s_error_reason = Y; CUTE_ASEPRITE_FAIL(); } } while (0) -#define CUTE_ASEPRITE_CALL(X) do { if (!(X)) goto ase_err; } while (0) -#define CUTE_ASEPRITE_DEFLATE_MAX_BITLEN 15 - -// DEFLATE tables from RFC 1951 -static uint8_t s_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 -static uint8_t s_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 -static uint8_t s_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 -static uint32_t s_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 -static uint8_t s_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 -static uint32_t s_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 deflate_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; - - uint32_t lit[288]; - uint32_t dst[32]; - uint32_t len[19]; - uint32_t nlit; - uint32_t ndst; - uint32_t nlen; -} deflate_t; - -static int s_would_overflow(deflate_t* s, int num_bits) -{ - return (s->bits_left + s->count) - num_bits < 0; -} - -static char* s_ptr(deflate_t* s) -{ - CUTE_ASEPRITE_ASSERT(!(s->bits_left & 7)); - return (char*)(s->words + s->word_index) - (s->count / 8); -} - -static uint64_t s_peak_bits(deflate_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_ASEPRITE_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 s_consume_bits(deflate_t* s, int num_bits_to_read) -{ - CUTE_ASEPRITE_ASSERT(s->count >= num_bits_to_read); - uint32_t bits = (uint32_t)(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 s_read_bits(deflate_t* s, int num_bits_to_read) -{ - CUTE_ASEPRITE_ASSERT(num_bits_to_read <= 32); - CUTE_ASEPRITE_ASSERT(num_bits_to_read >= 0); - CUTE_ASEPRITE_ASSERT(s->bits_left > 0); - CUTE_ASEPRITE_ASSERT(s->count <= 64); - CUTE_ASEPRITE_ASSERT(!s_would_overflow(s, num_bits_to_read)); - s_peak_bits(s, num_bits_to_read); - uint32_t bits = s_consume_bits(s, num_bits_to_read); - return bits; -} - -static uint32_t s_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 uint32_t s_build(deflate_t* s, uint32_t* tree, uint8_t* lens, int sym_count) -{ - int n, codes[16], first[16], counts[16] = { 0 }; - CUTE_ASEPRITE_UNUSED(s); - - // 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]; - } - - for (uint32_t i = 0; i < (uint32_t)sym_count; ++i) - { - uint8_t len = lens[i]; - - if (len != 0) - { - CUTE_ASEPRITE_ASSERT(len < 16); - uint32_t code = (uint32_t)codes[len]++; - uint32_t slot = (uint32_t)first[len]++; - tree[slot] = (code << (32 - (uint32_t)len)) | (i << 4) | len; - } - } - - return (uint32_t)first[15]; -} - -static int s_stored(deflate_t* s) -{ - char* p; - - // 3.2.3 - // skip any remaining bits in current partially processed byte - s_read_bits(s, s->count & 7); - - // 3.2.4 - // read LEN and NLEN, should complement each other - uint16_t LEN = (uint16_t)s_read_bits(s, 16); - uint16_t NLEN = (uint16_t)s_read_bits(s, 16); - uint16_t TILDE_NLEN = ~NLEN; - CUTE_ASEPRITE_CHECK(LEN == TILDE_NLEN, "Failed to find LEN and NLEN as complements within stored (uncompressed) stream."); - CUTE_ASEPRITE_CHECK(s->bits_left / 8 <= (int)LEN, "Stored block extends beyond end of input stream."); - p = s_ptr(s); - CUTE_ASEPRITE_MEMCPY(s->out, p, LEN); - s->out += LEN; - return 1; - -ase_err: - return 0; -} - -// 3.2.6 -static int s_fixed(deflate_t* s) -{ - s->nlit = s_build(s, s->lit, s_fixed_table, 288); - s->ndst = s_build(0, s->dst, s_fixed_table + 288, 32); - return 1; -} - -static int s_decode(deflate_t* s, uint32_t* tree, int hi) -{ - uint64_t bits = s_peak_bits(s, 16); - uint32_t search = (s_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_ASEPRITE_ASSERT((search >> len) == (key >> len)); - - s_consume_bits(s, key & 0xF); - return (key >> 4) & 0xFFF; -} - -// 3.2.7 -static int s_dynamic(deflate_t* s) -{ - uint8_t lenlens[19] = { 0 }; - - uint32_t nlit = 257 + s_read_bits(s, 5); - uint32_t ndst = 1 + s_read_bits(s, 5); - uint32_t nlen = 4 + s_read_bits(s, 4); - - for (uint32_t i = 0 ; i < nlen; ++i) - lenlens[s_permutation_order[i]] = (uint8_t)s_read_bits(s, 3); - - // Build the tree for decoding code lengths - s->nlen = s_build(0, s->len, lenlens, 19); - uint8_t lens[288 + 32]; - - for (uint32_t n = 0; n < nlit + ndst;) - { - int sym = s_decode(s, s->len, (int)s->nlen); - switch (sym) - { - case 16: for (uint32_t i = 3 + s_read_bits(s, 2); i; --i, ++n) lens[n] = lens[n - 1]; break; - case 17: for (uint32_t i = 3 + s_read_bits(s, 3); i; --i, ++n) lens[n] = 0; break; - case 18: for (uint32_t i = 11 + s_read_bits(s, 7); i; --i, ++n) lens[n] = 0; break; - default: lens[n++] = (uint8_t)sym; break; - } - } - - s->nlit = s_build(s, s->lit, lens, (int)nlit); - s->ndst = s_build(0, s->dst, lens + nlit, (int)ndst); - return 1; -} - -// 3.2.3 -static int s_block(deflate_t* s) -{ - while (1) - { - int symbol = s_decode(s, s->lit, (int)s->nlit); - - if (symbol < 256) - { - CUTE_ASEPRITE_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; - uint32_t length = s_read_bits(s, (int)(s_len_extra_bits[symbol])) + s_len_base[symbol]; - int distance_symbol = s_decode(s, s->dst, (int)s->ndst); - uint32_t backwards_distance = s_read_bits(s, s_dist_extra_bits[distance_symbol]) + s_dist_base[distance_symbol]; - CUTE_ASEPRITE_CHECK(s->out - backwards_distance >= s->begin, "Attempted to write before out buffer (invalid backwards distance)."); - CUTE_ASEPRITE_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_ASEPRITE_MEMSET(dst, *src, (size_t)length); - break; - default: while (length--) *dst++ = *src++; - } - } - - else break; - } - - return 1; - -ase_err: - return 0; -} - -// 3.2.3 -static int s_inflate(const void* in, int in_bytes, void* out, int out_bytes, void* mem_ctx) -{ - CUTE_ASEPRITE_UNUSED(mem_ctx); - deflate_t* s = (deflate_t*)CUTE_ASEPRITE_ALLOC(sizeof(deflate_t), mem_ctx); - 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) & (size_t)(~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; - uint32_t bfinal; - do - { - bfinal = s_read_bits(s, 1); - uint32_t btype = s_read_bits(s, 2); - - switch (btype) - { - case 0: CUTE_ASEPRITE_CALL(s_stored(s)); break; - case 1: s_fixed(s); CUTE_ASEPRITE_CALL(s_block(s)); break; - case 2: s_dynamic(s); CUTE_ASEPRITE_CALL(s_block(s)); break; - case 3: CUTE_ASEPRITE_CHECK(0, "Detected unknown block type within input stream."); - } - - ++count; - } - while (!bfinal); - - CUTE_ASEPRITE_FREE(s, mem_ctx); - return 1; - -ase_err: - CUTE_ASEPRITE_FREE(s, mem_ctx); - return 0; -} - -typedef struct ase_state_t -{ - uint8_t* in; - uint8_t* end; - void* mem_ctx; -} ase_state_t; - -static uint8_t s_read_uint8(ase_state_t* s) -{ - CUTE_ASEPRITE_ASSERT(s->in <= s->end + sizeof(uint8_t)); - uint8_t** p = &s->in; - uint8_t value = **p; - ++(*p); - return value; -} - -static uint16_t s_read_uint16(ase_state_t* s) -{ - CUTE_ASEPRITE_ASSERT(s->in <= s->end + sizeof(uint16_t)); - uint8_t** p = &s->in; - uint16_t value; - value = (*p)[0]; - value |= (((uint16_t)((*p)[1])) << 8); - *p += 2; - return value; -} - -static ase_fixed_t s_read_fixed(ase_state_t* s) -{ - ase_fixed_t value; - value.a = s_read_uint16(s); - value.b = s_read_uint16(s); - return value; -} - -static uint32_t s_read_uint32(ase_state_t* s) -{ - CUTE_ASEPRITE_ASSERT(s->in <= s->end + sizeof(uint32_t)); - uint8_t** p = &s->in; - uint32_t value; - value = (*p)[0]; - value |= (((uint32_t)((*p)[1])) << 8); - value |= (((uint32_t)((*p)[2])) << 16); - value |= (((uint32_t)((*p)[3])) << 24); - *p += 4; - return value; -} - -#ifdef CUTE_ASPRITE_S_READ_UINT64 -// s_read_uint64() is not currently used. -static uint64_t s_read_uint64(ase_state_t* s) -{ - CUTE_ASEPRITE_ASSERT(s->in <= s->end + sizeof(uint64_t)); - uint8_t** p = &s->in; - uint64_t value; - value = (*p)[0]; - value |= (((uint64_t)((*p)[1])) << 8 ); - value |= (((uint64_t)((*p)[2])) << 16); - value |= (((uint64_t)((*p)[3])) << 24); - value |= (((uint64_t)((*p)[4])) << 32); - value |= (((uint64_t)((*p)[5])) << 40); - value |= (((uint64_t)((*p)[6])) << 48); - value |= (((uint64_t)((*p)[7])) << 56); - *p += 8; - return value; -} -#endif - -#define s_read_int16(s) (int16_t)s_read_uint16(s) -#define s_read_int32(s) (int32_t)s_read_uint32(s) - -#ifdef CUTE_ASPRITE_S_READ_BYTES -// s_read_bytes() is not currently used. -static void s_read_bytes(ase_state_t* s, uint8_t* bytes, int num_bytes) -{ - for (int i = 0; i < num_bytes; ++i) { - bytes[i] = s_read_uint8(s); - } -} -#endif - -static const char* s_read_string(ase_state_t* s) -{ - int len = (int)s_read_uint16(s); - char* bytes = (char*)CUTE_ASEPRITE_ALLOC(len + 1, s->mem_ctx); - for (int i = 0; i < len; ++i) { - bytes[i] = (char)s_read_uint8(s); - } - bytes[len] = 0; - return bytes; -} - -static void s_skip(ase_state_t* ase, int num_bytes) -{ - CUTE_ASEPRITE_ASSERT(ase->in <= ase->end + num_bytes); - ase->in += num_bytes; -} - -static char* s_fopen(const char* path, int* size, void* mem_ctx) -{ - CUTE_ASEPRITE_UNUSED(mem_ctx); - char* data = 0; - CUTE_ASEPRITE_FILE* fp = CUTE_ASEPRITE_FOPEN(path, "rb"); - int sz = 0; - - if (fp) - { - CUTE_ASEPRITE_FSEEK(fp, 0, CUTE_ASEPRITE_SEEK_END); - sz = CUTE_ASEPRITE_FTELL(fp); - CUTE_ASEPRITE_FSEEK(fp, 0, CUTE_ASEPRITE_SEEK_SET); - data = (char*)CUTE_ASEPRITE_ALLOC(sz + 1, mem_ctx); - CUTE_ASEPRITE_FREAD(data, sz, 1, fp); - data[sz] = 0; - CUTE_ASEPRITE_FCLOSE(fp); - } - - if (size) *size = sz; - return data; -} - -ase_t* cute_aseprite_load_from_file(const char* path, void* mem_ctx) -{ - s_error_file = path; - int sz; - void* file = s_fopen(path, &sz, mem_ctx); - if (!file) { - CUTE_ASEPRITE_WARNING("Unable to find map file."); - return NULL; - } - ase_t* aseprite = cute_aseprite_load_from_memory(file, sz, mem_ctx); - CUTE_ASEPRITE_FREE(file, mem_ctx); - s_error_file = NULL; - return aseprite; -} - -static int s_mul_un8(int a, int b) -{ - int t = (a * b) + 0x80; - return (((t >> 8) + t) >> 8); -} - -static ase_color_t s_blend(ase_color_t src, ase_color_t dst, uint8_t opacity) -{ - src.a = (uint8_t)s_mul_un8(src.a, opacity); - int a = src.a + dst.a - s_mul_un8(src.a, dst.a); - int r, g, b; - if (a == 0) { - r = g = b = 0; - } else { - r = dst.r + (src.r - dst.r) * src.a / a; - g = dst.g + (src.g - dst.g) * src.a / a; - b = dst.b + (src.b - dst.b) * src.a / a; - } - ase_color_t ret = { (uint8_t)r, (uint8_t)g, (uint8_t)b, (uint8_t)a }; - return ret; -} - -static int s_min(int a, int b) -{ - return a < b ? a : b; -} - -static int s_max(int a, int b) -{ - return a < b ? b : a; -} - -static ase_color_t s_color(ase_t* ase, void* src, int index) -{ - ase_color_t result; - if (ase->mode == ASE_MODE_RGBA) { - result = ((ase_color_t*)src)[index]; - } else if (ase->mode == ASE_MODE_GRAYSCALE) { - uint8_t saturation = ((uint8_t*)src)[index * 2]; - uint8_t a = ((uint8_t*)src)[index * 2 + 1]; - result.r = result.g = result.b = saturation; - result.a = a; - } else { - CUTE_ASEPRITE_ASSERT(ase->mode == ASE_MODE_INDEXED); - uint8_t palette_index = ((uint8_t*)src)[index]; - if (palette_index == ase->transparent_palette_entry_index) { - result.r = 0; - result.g = 0; - result.b = 0; - result.a = 0; - } else { - result = ase->palette.entries[palette_index].color; - } - } - return result; -} - -ase_t* cute_aseprite_load_from_memory(const void* memory, int size, void* mem_ctx) -{ - ase_t* ase = (ase_t*)CUTE_ASEPRITE_ALLOC(sizeof(ase_t), mem_ctx); - CUTE_ASEPRITE_MEMSET(ase, 0, sizeof(*ase)); - - ase_state_t state = { 0 }; - ase_state_t* s = &state; - s->in = (uint8_t*)memory; - s->end = s->in + size; - s->mem_ctx = mem_ctx; - - s_skip(s, sizeof(uint32_t)); // File size. - int magic = (int)s_read_uint16(s); - if (magic != 0xA5E0) return CUTE_ASEPRITE_FREE(ase, mem_ctx), 0; // CUTE_ASEPRITE_ASSERT(magic == 0xA5E0); //< r-lyeh: soft abort - - ase->frame_count = (int)s_read_uint16(s); - ase->w = s_read_uint16(s); - ase->h = s_read_uint16(s); - uint16_t bpp = s_read_uint16(s) / 8; - if (bpp == 4) ase->mode = ASE_MODE_RGBA; - else if (bpp == 2) ase->mode = ASE_MODE_GRAYSCALE; - else { - CUTE_ASEPRITE_ASSERT(bpp == 1); - ase->mode = ASE_MODE_INDEXED; - } - uint32_t valid_layer_opacity = s_read_uint32(s) & 1; - int speed = s_read_uint16(s); - s_skip(s, sizeof(uint32_t) * 2); // Spec says skip these bytes, as they're zero'd. - ase->transparent_palette_entry_index = s_read_uint8(s); - s_skip(s, 3); // Spec says skip these bytes. - ase->number_of_colors = (int)s_read_uint16(s); - ase->pixel_w = (int)s_read_uint8(s); - ase->pixel_h = (int)s_read_uint8(s); - ase->grid_x = (int)s_read_int16(s); - ase->grid_y = (int)s_read_int16(s); - ase->grid_w = (int)s_read_uint16(s); - ase->grid_h = (int)s_read_uint16(s); - s_skip(s, 84); // For future use (set to zero). - - ase->frames = (ase_frame_t*)CUTE_ASEPRITE_ALLOC((int)(sizeof(ase_frame_t)) * ase->frame_count, mem_ctx); - CUTE_ASEPRITE_MEMSET(ase->frames, 0, sizeof(ase_frame_t) * (size_t)ase->frame_count); - - ase_udata_t* last_udata = NULL; - int was_on_tags = 0; - int tag_index = 0; - - ase_layer_t* layer_stack[CUTE_ASEPRITE_MAX_LAYERS]; - - // Parse all chunks in the .aseprite file. - for (int i = 0; i < ase->frame_count; ++i) { - ase_frame_t* frame = ase->frames + i; - frame->ase = ase; - s_skip(s, sizeof(uint32_t)); // Frame size. - magic = (int)s_read_uint16(s); - CUTE_ASEPRITE_ASSERT(magic == 0xF1FA); - int chunk_count = (int)s_read_uint16(s); - frame->duration_milliseconds = s_read_uint16(s); - if (frame->duration_milliseconds == 0) frame->duration_milliseconds = speed; - s_skip(s, 2); // For future use (set to zero). - uint32_t new_chunk_count = s_read_uint32(s); - if (new_chunk_count) chunk_count = (int)new_chunk_count; - - for (int j = 0; j < chunk_count; ++j) { - uint32_t chunk_size = s_read_uint32(s); - uint16_t chunk_type = s_read_uint16(s); - chunk_size -= (uint32_t)(sizeof(uint32_t) + sizeof(uint16_t)); - uint8_t* chunk_start = s->in; - - switch (chunk_type) { - case 0x2004: // Layer chunk. - { - CUTE_ASEPRITE_ASSERT(ase->layer_count < CUTE_ASEPRITE_MAX_LAYERS); - ase_layer_t* layer = ase->layers + ase->layer_count++; - layer->flags = (ase_layer_flags_t)s_read_uint16(s); - layer->type = (ase_layer_type_t)s_read_uint16(s); - layer->parent = NULL; - int child_level = (int)s_read_uint16(s); - layer_stack[child_level] = layer; - if (child_level) { - layer->parent = layer_stack[child_level - 1]; - } - s_skip(s, sizeof(uint16_t)); // Default layer width in pixels (ignored). - s_skip(s, sizeof(uint16_t)); // Default layer height in pixels (ignored). - int blend_mode = (int)s_read_uint16(s); - if (blend_mode) CUTE_ASEPRITE_WARNING("Unknown blend mode encountered."); - layer->opacity = s_read_uint8(s) / 255.0f; - if (!valid_layer_opacity) layer->opacity = 1.0f; - s_skip(s, 3); // For future use (set to zero). - layer->name = s_read_string(s); - last_udata = &layer->udata; - } break; - - case 0x2005: // Cel chunk. - { - CUTE_ASEPRITE_ASSERT(frame->cel_count < CUTE_ASEPRITE_MAX_LAYERS); - ase_cel_t* cel = frame->cels + frame->cel_count++; - int layer_index = (int)s_read_uint16(s); - cel->layer = ase->layers + layer_index; - cel->x = s_read_int16(s); - cel->y = s_read_int16(s); - cel->opacity = s_read_uint8(s) / 255.0f; - int cel_type = (int)s_read_uint16(s); - s_skip(s, 7); // For future (set to zero). - switch (cel_type) { - case 0: // Raw cel. - cel->w = s_read_uint16(s); - cel->h = s_read_uint16(s); - cel->pixels = CUTE_ASEPRITE_ALLOC(cel->w * cel->h * bpp, mem_ctx); - CUTE_ASEPRITE_MEMCPY(cel->pixels, s->in, (size_t)(cel->w * cel->h * bpp)); - s_skip(s, cel->w * cel->h * bpp); - break; - - case 1: // Linked cel. - cel->is_linked = 1; - cel->linked_frame_index = s_read_uint16(s); - break; - - case 2: // Compressed image cel. - { - cel->w = s_read_uint16(s); - cel->h = s_read_uint16(s); - int zlib_byte0 = s_read_uint8(s); - int zlib_byte1 = s_read_uint8(s); - int deflate_bytes = (int)chunk_size - (int)(s->in - chunk_start); - void* pixels = s->in; - CUTE_ASEPRITE_ASSERT((zlib_byte0 & 0x0F) == 0x08); // Only zlib compression method (RFC 1950) is supported. - CUTE_ASEPRITE_ASSERT((zlib_byte0 & 0xF0) <= 0x70); // Innapropriate window size detected. - CUTE_ASEPRITE_ASSERT(!(zlib_byte1 & 0x20)); // Preset dictionary is present and not supported. - int pixels_sz = cel->w * cel->h * bpp; - void* pixels_decompressed = CUTE_ASEPRITE_ALLOC(pixels_sz, mem_ctx); - int ret = s_inflate(pixels, deflate_bytes, pixels_decompressed, pixels_sz, mem_ctx); - if (!ret) CUTE_ASEPRITE_WARNING(s_error_reason); - cel->pixels = pixels_decompressed; - s_skip(s, deflate_bytes); - } break; - } - last_udata = &cel->udata; - } break; - - case 0x2006: // Cel extra chunk. - { - ase_cel_t* cel = frame->cels + frame->cel_count; - cel->has_extra = 1; - cel->extra.precise_bounds_are_set = (int)s_read_uint32(s); - cel->extra.precise_x = s_read_fixed(s); - cel->extra.precise_y = s_read_fixed(s); - cel->extra.w = s_read_fixed(s); - cel->extra.h = s_read_fixed(s); - s_skip(s, 16); // For future use (set to zero). - } break; - - case 0x2007: // Color profile chunk. - { - ase->has_color_profile = 1; - ase->color_profile.type = (ase_color_profile_type_t)s_read_uint16(s); - ase->color_profile.use_fixed_gamma = (int)s_read_uint16(s) & 1; - ase->color_profile.gamma = s_read_fixed(s); - s_skip(s, 8); // For future use (set to zero). - if (ase->color_profile.type == ASE_COLOR_PROFILE_TYPE_EMBEDDED_ICC) { - // Use the embedded ICC profile. - ase->color_profile.icc_profile_data_length = s_read_uint32(s); - ase->color_profile.icc_profile_data = CUTE_ASEPRITE_ALLOC(ase->color_profile.icc_profile_data_length, mem_ctx); - CUTE_ASEPRITE_MEMCPY(ase->color_profile.icc_profile_data, s->in, ase->color_profile.icc_profile_data_length); - s->in += ase->color_profile.icc_profile_data_length; - } - } break; - - case 0x2018: // Tags chunk. - { - ase->tag_count = (int)s_read_uint16(s); - s_skip(s, 8); // For future (set to zero). - CUTE_ASEPRITE_ASSERT(ase->tag_count < CUTE_ASEPRITE_MAX_TAGS); - for (int k = 0; k < ase->tag_count; ++k) { - ase_tag_t tag; - tag.from_frame = (int)s_read_uint16(s); - tag.to_frame = (int)s_read_uint16(s); - tag.loop_animation_direction = (ase_animation_direction_t)s_read_uint8(s); - s_skip(s, 8); // For future (set to zero). - tag.r = s_read_uint8(s); - tag.g = s_read_uint8(s); - tag.b = s_read_uint8(s); - s_skip(s, 1); // Extra byte (zero). - tag.name = s_read_string(s); - ase->tags[k] = tag; - } - was_on_tags = 1; - } break; - - case 0x2019: // Palette chunk. - { - ase->palette.entry_count = (int)s_read_uint32(s); - CUTE_ASEPRITE_ASSERT(ase->palette.entry_count <= CUTE_ASEPRITE_MAX_PALETTE_ENTRIES); - int first_index = (int)s_read_uint32(s); - int last_index = (int)s_read_uint32(s); - s_skip(s, 8); // For future (set to zero). - for (int k = first_index; k <= last_index; ++k) { - int has_name = s_read_uint16(s); - ase_palette_entry_t entry; - entry.color.r = s_read_uint8(s); - entry.color.g = s_read_uint8(s); - entry.color.b = s_read_uint8(s); - entry.color.a = s_read_uint8(s); - if (has_name) { - entry.color_name = s_read_string(s); - } else { - entry.color_name = NULL; - } - CUTE_ASEPRITE_ASSERT(k < CUTE_ASEPRITE_MAX_PALETTE_ENTRIES); - ase->palette.entries[k] = entry; - } - } break; - - case 0x2020: // Udata chunk. - { - CUTE_ASEPRITE_ASSERT(last_udata || was_on_tags); - if (was_on_tags && !last_udata) { - CUTE_ASEPRITE_ASSERT(tag_index < ase->tag_count); - last_udata = &ase->tags[tag_index++].udata; - } - int flags = (int)s_read_uint32(s); - if (flags & 1) { - last_udata->has_text = 1; - last_udata->text = s_read_string(s); - } - if (flags & 2) { - last_udata->color.r = s_read_uint8(s); - last_udata->color.g = s_read_uint8(s); - last_udata->color.b = s_read_uint8(s); - last_udata->color.a = s_read_uint8(s); - } - last_udata = NULL; - } break; - - case 0x2022: // Slice chunk. - { - int slice_count = (int)s_read_uint32(s); - int flags = (int)s_read_uint32(s); - s_skip(s, sizeof(uint32_t)); // Reserved. - const char* name = s_read_string(s); - for (int k = 0; k < (int)slice_count; ++k) { - ase_slice_t slice = { 0 }; - slice.name = name; - slice.frame_number = (int)s_read_uint32(s); - slice.origin_x = (int)s_read_int32(s); - slice.origin_y = (int)s_read_int32(s); - slice.w = (int)s_read_uint32(s); - slice.h = (int)s_read_uint32(s); - if (flags & 1) { - // It's a 9-patches slice. - slice.has_center_as_9_slice = 1; - slice.center_x = (int)s_read_int32(s); - slice.center_y = (int)s_read_int32(s); - slice.center_w = (int)s_read_uint32(s); - slice.center_h = (int)s_read_uint32(s); - } else if (flags & 2) { - // Has pivot information. - slice.has_pivot = 1; - slice.pivot_x = (int)s_read_int32(s); - slice.pivot_y = (int)s_read_int32(s); - } - CUTE_ASEPRITE_ASSERT(ase->slice_count < CUTE_ASEPRITE_MAX_SLICES); - ase->slices[ase->slice_count++] = slice; - last_udata = &ase->slices[ase->slice_count - 1].udata; - } - } break; - - default: - s_skip(s, (int)chunk_size); - break; - } - - uint32_t size_read = (uint32_t)(s->in - chunk_start); - CUTE_ASEPRITE_ASSERT(size_read == chunk_size); - } - } - - //< @r-lyeh: if num_layers > 1, then assume last layer #0 is a background layer. hide it - #if ASE_TRIMS - if(ase->layer_count > 1) ase->layers[0].flags &= ~ASE_LAYER_FLAGS_VISIBLE; - if(0) - for( int i = 0; i < ase->layer_count; ++i ) { - int match = 0; - match = !strcmpi(ase->layers[i].name, "grid"); - match |= !strcmpi(ase->layers[i].name, "background"); - match |= !strcmpi(ase->layers[i].name, "fondo"); - if(match) ase->layers[i].flags &= ~ASE_LAYER_FLAGS_VISIBLE; - } - #endif - //< - - // Blend all cel pixels into each of their respective frames, for convenience. - for (int i = 0; i < ase->frame_count; ++i) { - ase_frame_t* frame = ase->frames + i; - frame->pixels = (ase_color_t*)CUTE_ASEPRITE_ALLOC((int)(sizeof(ase_color_t)) * ase->w * ase->h, mem_ctx); - CUTE_ASEPRITE_MEMSET(frame->pixels, 0, sizeof(ase_color_t) * (size_t)ase->w * (size_t)ase->h); - ase_color_t* dst = frame->pixels; - for (int j = 0; j < frame->cel_count; ++j) { - ase_cel_t* cel = frame->cels + j; - if (!(cel->layer->flags & ASE_LAYER_FLAGS_VISIBLE)) { - continue; - } - if (cel->layer->parent && !(cel->layer->parent->flags & ASE_LAYER_FLAGS_VISIBLE)) { - continue; - } - while (cel->is_linked) { - ase_frame_t* frame = ase->frames + cel->linked_frame_index; - int found = 0; - for (int k = 0; k < frame->cel_count; ++k) { - if (frame->cels[k].layer == cel->layer) { - cel = frame->cels + k; - found = 1; - break; - } - } - CUTE_ASEPRITE_ASSERT(found); - } - void* src = cel->pixels; - uint8_t opacity = (uint8_t)(cel->opacity * cel->layer->opacity * 255.0f); - int cx = cel->x; - int cy = cel->y; - int cw = cel->w; - int ch = cel->h; - int cl = -s_min(cx, 0); - int ct = -s_min(cy, 0); - int dl = s_max(cx, 0); - int dt = s_max(cy, 0); - int dr = s_min(ase->w, cw + cx); - int db = s_min(ase->h, ch + cy); - int aw = ase->w; - for (int dx = dl, sx = cl; dx < dr; dx++, sx++) { - for (int dy = dt, sy = ct; dy < db; dy++, sy++) { - int dst_index = aw * dy + dx; - ase_color_t src_color = s_color(ase, src, cw * sy + sx); - ase_color_t dst_color = dst[dst_index]; - ase_color_t result = s_blend(src_color, dst_color, opacity); - dst[dst_index] = result; - } - } - } - } - - ase->mem_ctx = mem_ctx; - return ase; -} - -void cute_aseprite_free(ase_t* ase) -{ - for (int i = 0; i < ase->frame_count; ++i) { - ase_frame_t* frame = ase->frames + i; - CUTE_ASEPRITE_FREE(frame->pixels, ase->mem_ctx); - for (int j = 0; j < frame->cel_count; ++j) { - ase_cel_t* cel = frame->cels + j; - CUTE_ASEPRITE_FREE(cel->pixels, ase->mem_ctx); - CUTE_ASEPRITE_FREE((void*)cel->udata.text, ase->mem_ctx); - } - } - for (int i = 0; i < ase->layer_count; ++i) { - ase_layer_t* layer = ase->layers + i; - CUTE_ASEPRITE_FREE((void*)layer->name, ase->mem_ctx); - CUTE_ASEPRITE_FREE((void*)layer->udata.text, ase->mem_ctx); - } - for (int i = 0; i < ase->tag_count; ++i) { - ase_tag_t* tag = ase->tags + i; - CUTE_ASEPRITE_FREE((void*)tag->name, ase->mem_ctx); - } - for (int i = 0; i < ase->slice_count; ++i) { - ase_slice_t* slice = ase->slices + i; - CUTE_ASEPRITE_FREE((void*)slice->udata.text, ase->mem_ctx); - } - if (ase->slice_count) { - CUTE_ASEPRITE_FREE((void*)ase->slices[0].name, ase->mem_ctx); - } - for (int i = 0; i < ase->palette.entry_count; ++i) { - CUTE_ASEPRITE_FREE((void*)ase->palette.entries[i].color_name, ase->mem_ctx); - } - CUTE_ASEPRITE_FREE(ase->color_profile.icc_profile_data, ase->mem_ctx); - CUTE_ASEPRITE_FREE(ase->frames, ase->mem_ctx); - CUTE_ASEPRITE_FREE(ase, ase->mem_ctx); -} - -#endif // CUTE_ASEPRITE_IMPLEMENTATION_ONCE -#endif // CUTE_ASEPRITE_IMPLEMENTATION - -/* - ------------------------------------------------------------------------------ - This software is available under 2 licenses - you may choose the one you like. - ------------------------------------------------------------------------------ - ALTERNATIVE A - zlib license - Copyright (c) 2017 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. - ------------------------------------------------------------------------------ -*/ diff --git a/tools/3rd_atlasc.h b/tools/3rd_atlasc.h deleted file mode 100644 index ae2b121..0000000 --- a/tools/3rd_atlasc.h +++ /dev/null @@ -1,886 +0,0 @@ -// atlasc.c -// Copyright 2019 Sepehr Taghdisian (septag@github). All rights reserved. -// License: https://github.com/septag/atlasc#license-bsd-2-clause -// -// sx_math.h -// Copyright 2018 Sepehr Taghdisian (septag@github). All rights reserved. -// License: https://github.com/septag/sx#license-bsd-2-clause - -#ifndef ATLASC_HEADER -#define ATLASC_HEADER - -#define ATLASC_VERSION "1.2.3" - -#include -#include -#include - -#ifndef __cplusplus -#define ATLAS_CAST -#else -#define ATLAS_CAST(T) T -extern "C" { -#endif - -typedef union vec2 { struct { float x, y; }; float f[2]; } vec2; -typedef union vec3 { struct { float x, y, z; }; float f[3]; } vec3; -typedef union vec2i { struct { int x, y; }; int n[2]; } vec2i; -typedef union recti { struct { int xmin, ymin, xmax, ymax; }; struct { vec2i vmin, vmax; }; int f[4]; } recti; - -typedef struct atlas_flags { - int alpha_threshold; - float dist_threshold; - int max_width; - int max_height; - int border; - int pot; - int padding; - int mesh; - int max_verts_per_mesh; - float scale; -} atlas_flags; - -typedef struct atlas_image { - uint8_t* pixels; // only supports 32bpp RGBA format - int width; - int height; - char *name; -} atlas_image; - -typedef struct atlas_sprite { - uint8_t* src_image; // RGBA image buffer (32bpp) - vec2i src_size; // widthxheight - recti sprite_rect; // cropped rectangle relative to sprite's source image (pixels) - recti sheet_rect; // rectangle in final sheet (pixels) - char *name; - unsigned frame; - - // sprite-mesh data (if flag is set. see atlas_flags) - uint16_t num_tris; - int num_points; - vec2i* pts; - vec2i* uvs; - uint16_t* tris; -} atlas_sprite; - -typedef struct atlas_t { - atlas_sprite* sprites; - int num_sprites; - int* frames; - int num_frames; - atlas_image output; -} atlas_t; - -// receives input files and common arguments. returns atlas_t -// you have to free the data after use with `atlas_free` -atlas_t* atlas_loadfiles(array(char*) files, atlas_flags flags); - -// receives input image buffers and common arguments. returns atlas_t -// you have to free the data after use with `atlas_free` -atlas_t* atlas_loadimages(array(atlas_image) images, atlas_flags flags); - -// -bool atlas_save(const char *outfile, const atlas_t* atlas, atlas_flags flags); - -// frees atlas_t memory -void atlas_free(atlas_t* atlas); - -// returns the last error string -const char* atlas_last_error(); - -#ifdef __cplusplus -} -#endif -#endif // ATLASC_HEADER - -// -#ifdef ATLASC_IMPLEMENTATION -#include -#include - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Types/Primitives - -#define vec2(x,y) (ATLAS_CAST(vec2) { (float)(x), (float)(y) }) -#define vec3(x,y,z) (ATLAS_CAST(vec3) { (float)(x), (float)(y), (float)(z) }) -#define vec2i(x,y) (ATLAS_CAST(vec2i) { (int)(x), (int)(y) }) -#define recti(x,y,X,Y) (ATLAS_CAST(recti) { (int)(x), (int)(y), (int)(X), (int)(Y) }) - -#define minf(a,b) ((a) < (b) ? (a) : (b)) -#define maxf(a,b) ((a) > (b) ? (a) : (b)) -#define clampf(a,b,c) ( (a) < (b) ? (b) : (a) > (c) ? (c) : (a)) - -static int nearest_pow2(int n) { return --n, n |= n >> 1, n |= n >> 2, n |= n >> 4, n |= n >> 8, n |= n >> 16, ++n; } // https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 -static float sx_abs(float _a) { union { float f; unsigned int ui; } u = { _a }; return u.ui &= 0x7FFFFFFF, u.f; } -static bool equalf(float _a, float _b, float _epsilon) { const float lhs = sx_abs(_a - _b), aa = sx_abs(_a), ab = sx_abs(_b), rhs = _epsilon * maxf(1.0f, maxf(aa, ab)); return lhs <= rhs; } // http://realtimecollisiondetection.net/blog/?t=89 - -static vec3 cross3(const vec3 _a, const vec3 _b) { return vec3(_a.y * _b.z - _a.z * _b.y, _a.z * _b.x - _a.x * _b.z, _a.x * _b.y - _a.y * _b.x); } - -static float dot2(const vec2 _a, const vec2 _b) { return _a.x * _b.x + _a.y * _b.y; } -static float len2(const vec2 _a) { return sqrt(dot2(_a, _a)); } -static vec2 norm2(const vec2 _a) { const float len = len2(_a); /*assert(len > 0 && "Divide by zero");*/ return vec2(_a.x / (len + !len), _a.y / (len + !len)); } -static vec2 add2(const vec2 _a, const vec2 _b) { return vec2(_a.x + _b.x, _a.y + _b.y); } -static vec2 sub2(const vec2 _a, const vec2 _b) { return vec2(_a.x - _b.x, _a.y - _b.y); } -static vec2 scale2(const vec2 _a, float _b) { return vec2(_a.x * _b, _a.y * _b); } - -static vec2i add2i(const vec2i _a, const vec2i _b) { return vec2i(_a.x + _b.x, _a.y + _b.y); } -static vec2i sub2i(const vec2i _a, const vec2i _b) { return vec2i(_a.x - _b.x, _a.y - _b.y); } -static vec2i min2i(const vec2i _a, const vec2i _b) { return vec2i(minf(_a.x, _b.x), minf(_a.y, _b.y)); } -static vec2i max2i(const vec2i _a, const vec2i _b) { return vec2i(maxf(_a.x, _b.x), maxf(_a.y, _b.y)); } - -static recti rectiwh(int _x, int _y, int _w, int _h) { return recti(_x, _y, _x + _w, _y + _h); } -static recti recti_expand(const recti rc, const vec2i expand) { return recti(rc.xmin - expand.x, rc.ymin - expand.y, rc.xmax + expand.x, rc.ymax + expand.y); } -static void recti_add_point(recti* rc, const vec2i pt) { rc->vmin = min2i(rc->vmin, pt); rc->vmax = max2i(rc->vmax, pt); } - -// ---------------------------------------------------------------------------- - -#ifndef ATLAS_REALLOC -#define ATLAS_REALLOC realloc -#endif -#ifndef ATLAS_MSIZE -#define ATLAS_MSIZE _msize -#endif -#ifndef ATLAS_CALLOC -#define ATLAS_CALLOC(n,m) memset(ATLAS_REALLOC(0, (n)*(m)), 0, (n)*(m)) -#endif -#ifndef ATLAS_FREE -#define ATLAS_FREE(ptr) ((ptr) = ATLAS_REALLOC((ptr), 0)) -#endif - -#define align_mask(_value, _mask) (((_value) + (_mask)) & ((~0) & (~(_mask)))) - -static void panic_if(int fail) { if(fail) exit(-fprintf(stderr, "out of memory!\n")); } - -static void path_unixpath(char *buf, unsigned buflen, const char *inpath) { - snprintf(buf, buflen, "%s", inpath); - while( strchr(buf, '\\') ) *strchr(buf, '\\') = '/'; -} -static void path_basename(char *buf, unsigned buflen, const char *inpath) { - const char *a = strrchr(inpath, '\\'); - const char *b = strrchr(inpath, '/'); - snprintf(buf, buflen, "%s", a > b ? a+1 : b > a ? b+1 : inpath ); -} -static bool path_isfile(const char* filepath) { - FILE *f = fopen(filepath, "rb"); - return f ? fclose(f), 1 : 0; -} - -static char g_error_str[512]; -const char* atlas_last_error() -{ - return g_error_str; -} - -static void atlas__free_sprites(atlas_sprite* sprites, int num_sprites) -{ - for (int i = 0; i < num_sprites; i++) { - if (sprites[i].src_image) { - stbi_image_free(sprites[i].src_image); - } - - if (sprites[i].tris) { - ATLAS_FREE(sprites[i].tris); - } - - if (sprites[i].pts) { - ATLAS_FREE(sprites[i].pts); - } - - if (sprites[i].uvs) { - ATLAS_FREE(sprites[i].uvs); - } - - if (sprites[i].name) { - ATLAS_FREE(sprites[i].name); - } - } - ATLAS_FREE(sprites); -} - -static void atlas__blit(uint8_t* dst, int dst_x, int dst_y, int dst_pitch, const uint8_t* src, - int src_x, int src_y, int src_w, int src_h, int src_pitch, int bpp) -{ - assert(dst); - assert(src); - - const int pixel_sz = bpp / 8; - const uint8_t* src_ptr = src + src_y * src_pitch + src_x * pixel_sz; - uint8_t* dst_ptr = dst + dst_y * dst_pitch + dst_x * pixel_sz; - for (int y = src_y; y < (src_y + src_h); y++) { - memcpy(dst_ptr, src_ptr, src_w * pixel_sz); - src_ptr += src_pitch; - dst_ptr += dst_pitch; - } -} - -static vec2 atlas__itof2(const s2o_point p) -{ - return vec2((float)p.x, (float)p.y); -} - -// modified version of: -// https://github.com/anael-seghezzi/Maratis-Tiny-C-library/blob/master/include/m_raster.h -static bool atlas__test_line(const uint8_t* buffer, int w, int h, s2o_point p0, s2o_point p1) -{ - const uint8_t* data = buffer; - - int x0 = p0.x; - int y0 = p0.y; - int x1 = p1.x; - int y1 = p1.y; - int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; - int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; - int err = dx + dy, e2; - - while (1) { - if (x0 > -1 && y0 > -1 && x0 < w && y0 < h) { - const uint8_t* pixel = data + (y0 * w + x0); - if (*pixel) - return true; // line intersects with image data - } - - if (x0 == x1 && y0 == y1) - break; - - e2 = 2 * err; - if (e2 >= dy) { - err += dy; - x0 += sx; - } - if (e2 <= dx) { - err += dx; - y0 += sy; - } - } - - return false; -} - -// returns true if 'pts' buffer is changed -static bool atlas__offset_pt(s2o_point* pts, int num_pts, int pt_idx, float amount, int w, int h) -{ - s2o_point ipt = pts[pt_idx]; - s2o_point _ipt = ipt; - vec2 pt = atlas__itof2(ipt); - vec2 prev_pt = (pt_idx > 0) ? atlas__itof2(pts[pt_idx - 1]) : atlas__itof2(pts[num_pts - 1]); - vec2 next_pt = (pt_idx + 1) < num_pts ? atlas__itof2(pts[pt_idx + 1]) : atlas__itof2(pts[0]); - vec2 edge1 = norm2(sub2(prev_pt, pt)); - vec2 edge2 = norm2(sub2(next_pt, pt)); - - // calculate normal vector to move the point away from the polygon - vec2 n; - vec3 c = cross3(vec3(edge1.x, edge1.y, 0), vec3(edge2.x, edge2.y, 0)); - if (equalf(c.z, 0.0f, 0.00001f)) { - n = scale2(vec2(-edge1.y, edge1.x), amount); - } else { - // c.z < 0 -> point intersecting convex edges - // c.z > 0 -> point intersecting concave edges - float k = c.z < 0.0f ? -1.0f : 1.0f; - n = scale2(norm2(add2(edge1, edge2)), k * amount); - } - - pt = add2(pt, n); - ipt.x = (int)pt.x; - ipt.y = (int)pt.y; - ipt.x = clampf(ipt.x, 0, w); - ipt.y = clampf(ipt.y, 0, h); - pts[pt_idx] = ipt; - return (_ipt.x != ipt.x) || (_ipt.y != ipt.y); -} - -static void atlas__fix_outline_pts(const uint8_t* thresholded, int tw, int th, s2o_point* pts, - int num_pts) -{ - // NOTE: winding is assumed to be CW - const float offset_amount = 2.0f; - - for (int i = 0; i < num_pts; i++) { - s2o_point pt = pts[i]; - int next_i = (i + 1) < num_pts ? (i + 1) : 0; - -// assert(!thresholded[pt.y * tw + pt.x]); // point shouldn't be inside threshold - - s2o_point next_pt = pts[next_i]; - while (atlas__test_line(thresholded, tw, th, pt, next_pt)) { - if (!atlas__offset_pt(pts, num_pts, i, offset_amount, tw, th)) - break; - atlas__offset_pt(pts, num_pts, next_i, offset_amount, tw, th); - // refresh points for the new line intersection test - pt = pts[i]; - next_pt = pts[next_i]; - } - } -} - -static void atlas__make_mesh(atlas_sprite* spr, const s2o_point* pts, int pt_count, int max_verts, - const uint8_t* thresholded, int width, int height) -{ - s2o_point* temp_pts = ATLAS_CALLOC(pt_count,sizeof(s2o_point)); - panic_if(!temp_pts); - - memcpy(temp_pts, pts, sizeof(s2o_point)*pt_count); - int num_verts = pt_count; - - if (width > 1 && height > 1) { - const float delta = 0.5f; - const float threshold_start = 0.5f; - float threshold = threshold_start; - - for(;;) { - s2o_distance_based_path_simplification(temp_pts, &num_verts, threshold); - - if(num_verts <= max_verts) break; - - memcpy(temp_pts, pts, sizeof(s2o_point)*pt_count); - num_verts = pt_count; - - threshold += delta; - } - - // fix any collisions with the actual image // @r-lyeh: method below is buggy. will return dupe points - atlas__fix_outline_pts(thresholded, width, height, temp_pts, num_verts); - } - - //< @r-lyeh: remove dupes - for (int i = 0; i < num_verts - 1; i++) { - for (int j = i + 1; j < num_verts; j++) { - if( temp_pts[i].x == temp_pts[j].x && temp_pts[i].y == temp_pts[j].y ) { - temp_pts[j].x = temp_pts[num_verts - 1].x; - temp_pts[j].y = temp_pts[num_verts - 1].y; - --num_verts; - --j; - } - } - } - //< - - // triangulate - del_point2d_t* dpts = ATLAS_CALLOC(num_verts, sizeof(del_point2d_t)); - panic_if(!dpts); - - for (int i = 0; i < num_verts; i++) { - dpts[i].x = (double)temp_pts[i].x; - dpts[i].y = (double)temp_pts[i].y; - //printf("%d) %f,%f\n", i, dpts[i].x, dpts[i].y); //< @r-lyeh: debug dupe points - } - - delaunay2d_t* polys = delaunay2d_from(dpts, num_verts); - assert(polys); - tri_delaunay2d_t* tris = tri_delaunay2d_from(polys); - assert(tris); - ATLAS_FREE(dpts); - delaunay2d_release(polys); - - assert(tris->num_triangles < UINT16_MAX); - spr->tris = ATLAS_CALLOC(tris->num_triangles * 3,sizeof(uint16_t)); - spr->pts = ATLAS_CALLOC(tris->num_points, sizeof(vec2i)); - assert(spr->tris); - assert(spr->pts); - - for (unsigned int i = 0; i < tris->num_triangles; i++) { - unsigned int index = i * 3; - spr->tris[index] = (uint16_t)tris->tris[index]; - spr->tris[index + 1] = (uint16_t)tris->tris[index + 1]; - spr->tris[index + 2] = (uint16_t)tris->tris[index + 2]; - } - for (unsigned int i = 0; i < tris->num_points; i++) { - spr->pts[i] = vec2i((int)tris->points[i].x, (int)tris->points[i].y); - } - spr->num_tris = (uint16_t)tris->num_triangles; - spr->num_points = (int)tris->num_points; - - tri_delaunay2d_release(tris); - ATLAS_FREE(temp_pts); -} - -atlas_t* atlas_loadimages(array(atlas_image) images, atlas_flags flags) -{ - assert(images); - - array(int) frames = 0; - array(atlas_sprite) sprites = 0; - - for (int i = 0; i < array_count(images); i++) { - - // find is_cached - { - int found = 0, k = 0; - static array(uint64_t) cache = 0; - static array(uint64_t) index = 0; - uint64_t hash = hash_init; - hash = hash_bin(&images[i].width, sizeof(images[i].width), hash); - hash = hash_bin(&images[i].height, sizeof(images[i].height), hash); - hash = hash_bin((char*)images[i].pixels, images[i].width * images[i].height * 4, hash); - for (; k < array_count(cache); ++k) - if (cache[k] == hash) { found = 1; break; } - if (found) { - array_push(frames, index[k]); - continue; - } else { - array_push(cache, hash); - array_push(index, k); - array_push(frames, k); - } - //printf("%d) %llx\n", array_count(cache), hash); - } - - atlas_sprite zero = {0}; - atlas_sprite* spr = &zero; - if(images[i].name) spr->name = STRDUP(images[i].name); - spr->frame = i; - - spr->src_size.x = images[i].width; - spr->src_size.y = images[i].height; - assert(images[i].width > 0 && images[i].height > 0); - assert(images[i].pixels); - uint8_t* pixels = images[i].pixels; - - // rescale - if (!equalf(flags.scale, 1.0f, 0.0001f)) { - int target_w = (int)((float)spr->src_size.x * flags.scale); - int target_h = (int)((float)spr->src_size.y * flags.scale); - uint8_t* resized_pixels = ATLAS_CALLOC(1, 4 * target_w * target_h); - panic_if(!resized_pixels); - - if (!stbir_resize_uint8(pixels, spr->src_size.x, spr->src_size.y, 4 * spr->src_size.x, - resized_pixels, target_w, target_h, 4 * target_w, 4)) { - snprintf(g_error_str, sizeof(g_error_str), "could not resize image: #%d", i + 1); - atlas__free_sprites(sprites, array_count(sprites)); - return NULL; - } - - stbi_image_free(pixels); - - spr->src_size.x = target_w; - spr->src_size.y = target_h; - pixels = resized_pixels; - } - - spr->src_image = pixels; - - recti sprite_rect = {0}; - int pt_count = 0; - s2o_point* pts = 0; - uint8_t* alpha = s2o_rgba_to_alpha(spr->src_image, spr->src_size.x, spr->src_size.y); - uint8_t* thresholded = s2o_alpha_to_thresholded(alpha, spr->src_size.x, spr->src_size.y, flags.alpha_threshold); - free(alpha); - - if (flags.mesh && spr->src_size.x > 1 && spr->src_size.y > 1) { - uint8_t* dilate_thres = s2o_dilate_thresholded(thresholded, spr->src_size.x, spr->src_size.y); - - uint8_t* outlined = s2o_thresholded_to_outlined(dilate_thres, spr->src_size.x, spr->src_size.y); - free(dilate_thres); - - pts = s2o_extract_outline_path(outlined, spr->src_size.x, spr->src_size.y, &pt_count, NULL); - free(outlined); - - //< @r-lyeh @fixme: many sprites will return extremely low num of points (like 8) even if the sprite is complex enough. - //< this will lead to produce here a nearly zero sprite_rect, then sheet_rect, then eventually an empty frame at end of pipeline. - - // calculate cropped rectangle - sprite_rect = recti(INT_MAX, INT_MAX, INT_MIN, INT_MIN); - for (int k = 0; k < pt_count; k++) { - recti_add_point(&sprite_rect, vec2i(pts[k].x, pts[k].y)); - } - sprite_rect.xmax++; - sprite_rect.ymax++; - } else { - sprite_rect = recti(0, 0, spr->src_size.x, spr->src_size.y); - pt_count = 4; - pts = ATLAS_CALLOC(pt_count, sizeof(s2o_point)); - pts[0] = (s2o_point) {0, 0}; - pts[1] = (s2o_point) {spr->src_size.x, 0}; - pts[2] = (s2o_point) {spr->src_size.x, spr->src_size.y}; - pts[3] = (s2o_point) {0, spr->src_size.y}; - } - - // generate mesh if set in arguments - if (flags.mesh) { - atlas__make_mesh(spr, pts, pt_count, flags.max_verts_per_mesh, thresholded, - spr->src_size.x, spr->src_size.y); - } - - ATLAS_FREE(pts); - free(thresholded); - spr->sprite_rect = sprite_rect; - - array_push(sprites, *spr); - } - - int num_sprites = array_count(sprites); - - // pack sprites into a sheet - stbrp_context rp_ctx = {0}; - int max_width = flags.max_width; - int max_height = flags.max_height; - int num_rp_nodes = max_width + max_height; - stbrp_rect* rp_rects = ATLAS_CALLOC(num_sprites, sizeof(stbrp_rect)); - stbrp_node* rp_nodes = ATLAS_CALLOC(num_rp_nodes, sizeof(stbrp_node)); - panic_if(!rp_rects || !rp_nodes); - - for (int i = 0; i < num_sprites; i++) { - recti rc = sprites[i].sprite_rect; - int rc_resize = (flags.border + flags.padding) * 2; - rp_rects[i].w = (rc.xmax - rc.xmin) + rc_resize; - rp_rects[i].h = (rc.ymax - rc.ymin) + rc_resize; - } - stbrp_init_target(&rp_ctx, max_width, max_height, rp_nodes, num_rp_nodes); - recti final_rect = recti(INT_MAX, INT_MAX, INT_MIN, INT_MIN); - if (stbrp_pack_rects(&rp_ctx, rp_rects, num_sprites)) { - for (int i = 0; i < num_sprites; i++) { - atlas_sprite* spr = &sprites[i]; - recti sheet_rect = rectiwh(rp_rects[i].x, rp_rects[i].y, rp_rects[i].w, rp_rects[i].h); - - // calculate the total size of output image - recti_add_point(&final_rect, sheet_rect.vmin); - recti_add_point(&final_rect, sheet_rect.vmax); - - // shrink back rect and set the real sheet_rect for the sprite - spr->sheet_rect = - recti_expand(sheet_rect, vec2i(-flags.border, -flags.border)); - } - } - - int dst_w = final_rect.xmax - final_rect.xmin; - int dst_h = final_rect.ymax - final_rect.ymin; - // make output size divide by 4 by default - dst_w = align_mask(dst_w, 3); - dst_h = align_mask(dst_h, 3); - - if (flags.pot) { - dst_w = nearest_pow2(dst_w); - dst_h = nearest_pow2(dst_h); - } - - uint8_t* dst = ATLAS_CALLOC(1, dst_w * dst_h * 4); - panic_if(!dst); - - // calculate UVs for sprite meshes - if (flags.mesh) { - for (int i = 0; i < num_sprites; i++) { - atlas_sprite* spr = &sprites[i]; - // if sprite has mesh, calculate UVs for it - if (spr->pts && spr->num_points) { - const int padding = flags.padding; - vec2i offset = spr->sprite_rect.vmin; - vec2i sheet_pos = - vec2i(spr->sheet_rect.xmin + padding, spr->sheet_rect.ymin + padding); - vec2i* uvs = ATLAS_CALLOC(spr->num_points, sizeof(vec2i)); - assert(uvs); - for (int pi = 0; pi < spr->num_points; pi++) { - vec2i pt = spr->pts[pi]; - uvs[pi] = add2i(sub2i(pt, offset), sheet_pos); - } - - spr->uvs = uvs; - } // generate uvs - } - } - - for (int i = 0; i < num_sprites; i++) { - const atlas_sprite* spr = &sprites[i]; - - // calculate UVs for sprite-meshes - - // remove padding and blit from src_image to dst - recti dstrc = recti_expand(spr->sheet_rect, vec2i(-flags.padding, -flags.padding)); - recti srcrc = spr->sprite_rect; - atlas__blit(dst, dstrc.xmin, dstrc.ymin, dst_w * 4, spr->src_image, srcrc.xmin, srcrc.ymin, - srcrc.xmax - srcrc.xmin, srcrc.ymax - srcrc.ymin, spr->src_size.x * 4, 32); - } - - atlas_t* atlas = ATLAS_CALLOC(1, sizeof(atlas_t)); - panic_if(!atlas); - - atlas->output.pixels = dst; - atlas->output.width = dst_w; - atlas->output.height = dst_h; - atlas->sprites = sprites; - atlas->num_sprites = num_sprites; - atlas->frames = frames; - atlas->num_frames = array_count(frames); - - ATLAS_FREE(rp_nodes); - ATLAS_FREE(rp_rects); - - return atlas; -} - -static char *atlas_anims = 0; -static char *atlas_slices = 0; -static char *atlas_current_anim = 0; - -atlas_t* atlas_loadfiles(array(char*) files, atlas_flags flags) -{ - assert(files); - - array(atlas_image) images = 0; - - for (int i = 0; i < array_count(files); ++i) { - if (!path_isfile(files[i])) { - snprintf(g_error_str, sizeof(g_error_str), "input image not found: %s", files[i]); - goto err_cleanup; - } - - int comp; - atlas_image img = {0}; - img.pixels = stbi_load(files[i], &img.width, &img.height, &comp, 4); - -#ifdef CUTE_ASEPRITE_H - if (!img.pixels) { - bool loaded = 0; - - for( ase_t* ase = cute_aseprite_load_from_file(files[i], NULL); ase; cute_aseprite_free(ase), ase = 0, loaded = 1) { - ase_tag_t *parent = ase->tags + 0; - - //< abc/def/ghi.aseprite -> ghi - if( atlas_current_anim ) *atlas_current_anim = '\0'; - strcatf(&atlas_current_anim, files[i]); - path_basename(atlas_current_anim, strlen(atlas_current_anim), files[i]); - if( strrchr(atlas_current_anim, '.')) *strrchr(atlas_current_anim, '.') = '\0'; - trimspace(atlas_current_anim); - //< - - for( int f = 0; f < ase->frame_count; ++f) { - ase_frame_t *frame = ase->frames + f; - - // find rect - int x = INT_MAX, y = INT_MAX, x2 = INT_MIN, y2 = INT_MIN; - for( int c = 0; c < frame->cel_count; ++c ) { - ase_cel_t *cel = frame->cels + c; - if( cel->layer->flags & ASE_LAYER_FLAGS_VISIBLE ) { - if( cel->x < x ) x = cel->x; - if( cel->h < y ) y = cel->y; - if( (cel->x + cel->w) > x2 ) x2 = cel->x + cel->w; - if( (cel->y + cel->h) > y2 ) y2 = cel->y + cel->h; - } - } - if (x2 <= 0 || y2 <= 0) { // submit empty frame - img.width = 1; - img.height = 1; - img.pixels = calloc(1, 1*1*4); - array_push(images, img); - continue; - } - int cx = x; - int cy = y; - int cw = x2-x; - int ch = y2-y; - int tn = 4; - int tw = ase->w; - - // find clip - img.width = cw; - img.height = ch; - img.pixels = calloc(1, cw*ch*4); // @fixme: because of a stbi_image_free() within rescale section, this should be allocated with stbi allocator - for( unsigned y = 0; y < ch; ++y ) - memcpy((char *)img.pixels + (0+(0+y)*cw)*tn, (char*)frame->pixels + (cx+(cy+y)*tw)*tn, cw*tn); - array_push(images, img); - } - - static int slice_idx = -1; - static int slice_frame_idx = 0; - static const char *slice_name = 0; - if(!atlas_slices) strcatf(&atlas_slices, "[slices]\n"); - - for( int t = 0; t < ase->slice_count; ++t) { - ase_slice_t *slice = ase->slices + t; - if (!slice_name || strcmp(slice_name, slice->name)) { - ++slice_idx; - strcatf(&atlas_slices, "[%d].sl_name=%s\n", slice_idx, slice->name); - strcatf(&atlas_slices, "[%d].sl_frames=", slice_idx); - for( int u = 0; u < ase->slice_count; ++u) { - if (!strcmp(slice->name, ase->slices[u].name)) { - strcatf(&atlas_slices, "%d,", u); - } - } - strcatf(&atlas_slices, "\n"); - } - strcatf(&atlas_slices, "[%d].sl_bounds=%d,%d,%d,%d\n", slice_idx, slice->origin_x, slice->origin_y, slice->w, slice->h); - strcatf(&atlas_slices, "[%d].sl_9slice=%d\n", slice_idx, slice->has_center_as_9_slice); - if (slice->has_center_as_9_slice) - strcatf(&atlas_slices, "[%d].sl_core=%d,%d,%d,%d\n", slice_idx, slice->center_x, slice->center_y, slice->center_w, slice->center_h); - - slice_name = slice->name; - ++slice_frame_idx; - } - - static int anim_idx = 0; - if(!atlas_anims) strcatf(&atlas_anims, "[anims]\n"); - - for( int t = 0; t < ase->tag_count; ++t) { - ase_tag_t *tag = ase->tags + t; - - // find full name - int range[2] = {tag->from_frame, tag->to_frame}; - char name[256] = {0}; - for( int tt = 0; tt < ase->tag_count; ++tt ) { - ase_tag_t *ttag = ase->tags + tt; - if( range[0] >= ttag->from_frame && range[1] <= ttag->to_frame ) - strcat(name, "."), strcat(name, ttag->name); - } - trimspace(name); - - char *sep = ""; - strcatf(&atlas_anims, "[%d].name=%s.%s\n", anim_idx, atlas_current_anim, name+1); - strcatf(&atlas_anims, "[%d].frames=", anim_idx); - if( tag->loop_animation_direction != ASE_ANIMATION_DIRECTION_BACKWARDS) - for( int from = tag->from_frame; from <= tag->to_frame; ++from ) { - strcatf(&atlas_anims, "%s%d,%d", sep, from, ase->frames[from].duration_milliseconds), sep = ","; - } - sep = ""; - if( tag->loop_animation_direction != ASE_ANIMATION_DIRECTION_FORWARDS) - for( int from = tag->from_frame; from <= tag->to_frame; ++from ) { - strcatf(&atlas_anims, "%s%d,%d", sep, from, ase->frames[from].duration_milliseconds), sep = ","; - } - strcatf(&atlas_anims,"\n"); - - ++anim_idx; - } - } - - if( loaded ) continue; - } -#endif - - if (!img.pixels) { - continue; //< @r-lyeh: keep going - - snprintf(g_error_str, sizeof(g_error_str), "invalid image format: %s", files[i]); - goto err_cleanup; - } - - if( !img.name ) img.name = STRDUP(files[i]); - - array_push(images, img); - } - - atlas_t* atlas = atlas_loadimages(images, flags); - return atlas; - -err_cleanup: - for (int i = 0; i < array_count(images); i++) { - if (images[i].pixels) { - stbi_image_free(images[i].pixels); - } - if (images[i].name) { - ATLAS_FREE(images[i].name); - } - } - array_free(images); - return NULL; -} - -void atlas_free(atlas_t* atlas) -{ - assert(atlas); - - if (atlas->sprites) - atlas__free_sprites(atlas->sprites, atlas->num_sprites); - if (atlas->frames) - ATLAS_FREE(atlas->frames); - if (atlas->output.pixels) - ATLAS_FREE(atlas->output.pixels); - ATLAS_FREE(atlas); -} - - - -// custom write function -typedef struct { - int offset; - void *buffer; -} stbi_mem_context; -static void stbi_write_mem(void *context, void *data, int size) { - stbi_mem_context *ctx = (stbi_mem_context*)context; - memcpy( ctx->buffer, data, size ); - ctx->offset += size; -} - -bool atlas_save(const char *outfile, const atlas_t *atlas, atlas_flags flags) -{ - assert(outfile); - - const bool is_file = strcmp(outfile, "stdout"); - const atlas_sprite* sprites = atlas->sprites; - const int* frames = atlas->frames; - const int num_frames = atlas->num_frames; - const int num_sprites = atlas->num_sprites; - const uint8_t* dst = atlas->output.pixels; - const int dst_w = atlas->output.width; - const int dst_h = atlas->output.height; - - char image_filepath[256]; - char image_filename[256]; - snprintf(image_filepath, sizeof(image_filepath), "%s.png", outfile); - path_basename(image_filename, sizeof(image_filename), image_filepath); - - stbi_write_png_compression_level = 5; // 8 - - // write texture, if needed - if( is_file ) { - if (!stbi_write_png(image_filepath, dst_w, dst_h, 4, dst, dst_w * 4)) { - fprintf(stderr, "could not write image file `%s`\n", image_filepath); - return false; - } - } - - // write atlas description into .ini file - FILE *writer = is_file ? fopen(outfile, "wt") : stdout; - if (!writer) { - fprintf(stderr, "could not write ini file `%s`\n", outfile); - return false; - } - - fprintf(writer, "[atlas]\n"); - - if (is_file) { - fprintf(writer, "file=%s\n", image_filepath); - } else { - stbi_mem_context ctx = {0, ATLAS_CALLOC(1, dst_w*dst_h*4+256) }; - int result = stbi_write_png_to_func(stbi_write_mem, &ctx, dst_w, dst_h, 4, dst, dst_w*4); - char *b64 = base64_encode(ctx.buffer, ctx.offset); - fprintf(writer, "bitmap=%s\n", b64); // %d:%s\n", ctx.offset, b64); - // ATLAS_FREE(ctx.buffer); - // free(b64); - } - - fprintf(writer, "size=%d,%d\n", dst_w, dst_h); - fprintf(writer, "border=%d,%d\n", flags.border, flags.border); - fprintf(writer, "padding=%d,%d\n", flags.padding, flags.padding); - - for( int i = 0; i < num_frames; i++ ) { - const atlas_sprite* spr = sprites + frames[i]; - - char name[256]; - path_unixpath(name, sizeof(name), spr->name ? spr->name : ""); - - if(name[0]) - fprintf(writer, "[%d].name=%s\n", i, name); - fprintf(writer, "[%d].frame=%u\n", i, spr->frame); - //fprintf(writer, "[%d].size=%d,%d\n", i, spr->src_size.n[0], spr->src_size.n[1]); - //fprintf(writer, "[%d].rect=%u,%u,%u,%u\n", i, spr->sprite_rect.f[0], spr->sprite_rect.f[1], spr->sprite_rect.f[2], spr->sprite_rect.f[3]); - fprintf(writer, "[%d].sheet=%u,%u,%u,%u\n", i, spr->sheet_rect.f[0], spr->sheet_rect.f[1], spr->sheet_rect.f[2], spr->sheet_rect.f[3]); - if( spr->num_tris ) { - fprintf(writer, "[%d].indices=", i); // %d:", i, (int)spr->num_tris * 3); - for( int j = 0, jend = (int)spr->num_tris * 3; j < jend; ++j ) - fprintf(writer, "%u%s", spr->tris[j], j < (jend-1) ? "," : "\n"); - - fprintf(writer, "[%d].coords=", i); // %d:", i, spr->num_points*2); - for( int j = 0, jend = spr->num_points; j < jend; j++ ) - fprintf(writer, "%.f,%.f%s", (double)spr->pts[j].x, (double)spr->pts[j].y, j < (jend-1) ? ",":"\n" ); - - fprintf(writer, "[%d].uvs=", i); // %d:", i, spr->num_points*2); - for( int j = 0, jend = spr->num_points; j < jend; j++ ) - fprintf(writer, "%.f,%.f%s", (double)spr->uvs[j].x, (double)spr->uvs[j].y, j < (jend-1) ? ",":"\n" ); - } - } - - if( atlas_anims ) fprintf(writer, "%s\n", atlas_anims); - if( atlas_slices ) fprintf(writer, "%s\n", atlas_slices); - - if(writer != stdout) fclose(writer); - return true; -} - -#endif // ATLASC_IMPLEMENTATION diff --git a/tools/3rd_delaunay.h b/tools/3rd_delaunay.h deleted file mode 100644 index 0ca03dc..0000000 --- a/tools/3rd_delaunay.h +++ /dev/null @@ -1,1059 +0,0 @@ -#ifndef DELAUNAY_H -#define DELAUNAY_H - -/* -** delaunay.c : compute 2D delaunay triangulation in the plane. -** Copyright (C) 2005 Wael El Oraiby -** -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU Affero General Public License as -** published by the Free Software Foundation, either version 3 of the -** License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU Affero General Public License for more details. -** -** You should have received a copy of the GNU Affero General Public License -** along with this program. If not, see . -*/ - - - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef double real; - -typedef struct { - real x, y; -} del_point2d_t; - -typedef struct { - /** input points count */ - unsigned int num_points; - - /** the input points */ - del_point2d_t* points; - - /** number of returned faces */ - unsigned int num_faces; - - /** the faces are given as a sequence: num verts, verts indices, num verts, verts indices... - * the first face is the external face */ - unsigned int* faces; -} delaunay2d_t; - -/* - * build the 2D Delaunay triangulation given a set of points of at least 3 points - * - * @points: point set given as a sequence of tuple x0, y0, x1, y1, .... - * @num_points: number of given point - * @preds: the incircle predicate - * @faces: the triangles given as a sequence: num verts, verts indices, num verts, verts indices. - * Note that the first face is the external face - * @return: the created topology - */ -delaunay2d_t* delaunay2d_from(del_point2d_t *points, unsigned int num_points); - -/* - * release a delaunay2d object - */ -void delaunay2d_release(delaunay2d_t* del); - - -typedef struct { - /** input points count */ - unsigned int num_points; - - /** input points */ - del_point2d_t* points; - - /** number of triangles */ - unsigned int num_triangles; - - /** the triangles indices v0,v1,v2, v0,v1,v2 .... */ - unsigned int* tris; -} tri_delaunay2d_t; - -/** - * build a tri_delaunay2d_t out of a delaunay2d_t object - */ -tri_delaunay2d_t* tri_delaunay2d_from(delaunay2d_t* del); - -/** - * release a tri_delaunay2d_t object - */ -void tri_delaunay2d_release(tri_delaunay2d_t* tdel); - -#ifdef __cplusplus -} -#endif - -#endif // DELAUNAY_H - -#ifdef DELAUNAY_C - -/* -** delaunay.c : compute 2D delaunay triangulation in the plane. -** Copyright (C) 2005 Wael El Oraiby -** -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU Affero General Public License as -** published by the Free Software Foundation, either version 3 of the -** License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU Affero General Public License for more details. -** -** You should have received a copy of the GNU Affero General Public License -** along with this program. If not, see . -*/ - -#include -#include -#include -#include -#include - -#define ON_RIGHT 1 -#define ON_SEG 0 -#define ON_LEFT -1 - -#define OUTSIDE -1 -#define ON_CIRCLE 0 -#define INSIDE 1 - -struct point2d_s; -struct face_s; -struct halfedge_s; -struct delaunay_s; - - -#define REAL_ZERO 0.0l -#define REAL_ONE 1.0l -#define REAL_TWO 2.0l -#define REAL_FOUR 4.0l - - -typedef struct point2d_s point2d_t; -typedef struct face_s face_t; -typedef struct halfedge_s halfedge_t; -typedef struct delaunay_s delaunay_t; -typedef struct working_set_s working_set_t; - -typedef long double lreal; -typedef lreal mat3_t[3][3]; - -struct point2d_s { - real x, y; /* point coordinates */ - halfedge_t* he; /* point halfedge */ - unsigned int idx; /* point index in input buffer */ -}; - -struct face_s { - halfedge_t* he; /* a pointing half edge */ - unsigned int num_verts; /* number of vertices on this face */ -}; - -struct halfedge_s { - point2d_t* vertex; /* vertex */ - halfedge_t* pair; /* pair */ - halfedge_t* next; /* next */ - halfedge_t* prev; /* next^-1 */ - face_t* face; /* halfedge face */ -}; - -struct delaunay_s { - halfedge_t* rightmost_he; /* right most halfedge */ - halfedge_t* leftmost_he; /* left most halfedge */ - point2d_t* points; /* pointer to points */ - face_t* faces; /* faces of delaunay */ - unsigned int num_faces; /* face count */ - unsigned int start_point; /* start point index */ - unsigned int end_point; /* end point index */ -}; - -struct working_set_s { - halfedge_t* edges; /* all the edges (allocated in one shot) */ - face_t* faces; /* all the faces (allocated in one shot) */ - - unsigned int max_edge; /* maximum edge count: 2 * 3 * n where n is point count */ - unsigned int max_face; /* maximum face count: 2 * n where n is point count */ - - unsigned int num_edges; /* number of allocated edges */ - unsigned int num_faces; /* number of allocated faces */ - - halfedge_t* free_edge; /* pointer to the first free edge */ - face_t* free_face; /* pointer to the first free face */ -}; - -/* -* 3x3 matrix determinant -*/ -static lreal det3(mat3_t m) -{ - lreal res = m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) - - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) - + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]); - - return res; -} - -/* -* allocate a halfedge -*/ -static halfedge_t* halfedge_alloc() -{ - halfedge_t* d; - - d = (halfedge_t*)malloc(sizeof(halfedge_t)); - assert( NULL != d ); - memset(d, 0, sizeof(halfedge_t)); - - return d; -} - -/* -* free a halfedge -*/ -static void halfedge_free( halfedge_t* d ) -{ - assert( d != NULL ); - memset(d, 0, sizeof(halfedge_t)); - free(d); -} - -/* -* free all delaunay halfedges -*/ -void del_free_halfedges( delaunay_t *del ) -{ - unsigned int i; - halfedge_t *d, *sig; - - /* if there is nothing to do */ - if( del->points == NULL ) - return; - - for( i = 0; i <= (del->end_point - del->start_point); i++ ) - { - /* free all the halfedges around the point */ - d = del->points[i].he; - if( d != NULL ) - { - do { - sig = d->next; - halfedge_free( d ); - d = sig; - } while( d != del->points[i].he ); - del->points[i].he = NULL; - } - } -} - -/* -* compare 2 points when sorting -*/ -static int cmp_points( const void *_pt0, const void *_pt1 ) -{ - point2d_t *pt0, *pt1; - - pt0 = (point2d_t*)(_pt0); - pt1 = (point2d_t*)(_pt1); - - if( pt0->x < pt1->x ) - return -1; - else if( pt0->x > pt1->x ) - return 1; - else if( pt0->y < pt1->y ) - return -1; - else if( pt0->y > pt1->y ) - return 1; - printf("2 or more points share the same exact coordinate: (%f,%f)(%f,%f)\n", pt0->x,pt0->y,pt1->x,pt1->y); - assert(0 && "2 or more points share the same exact coordinate"); - return 0; /* Should not be given! */ -} - -/* -* classify a point relative to a segment -*/ -static int classify_point_seg( point2d_t *s, point2d_t *e, point2d_t *pt ) -{ - lreal se_x, se_y, spt_x, spt_y; - lreal res; - - se_x = e->x - s->x; - se_y = e->y - s->y; - - spt_x = pt->x - s->x; - spt_y = pt->y - s->y; - - res = (( se_x * spt_y ) - ( se_y * spt_x )); - if( res < REAL_ZERO ) - return ON_RIGHT; - else if( res > REAL_ZERO ) - return ON_LEFT; - - return ON_SEG; -} - -/* -* classify a point relative to a halfedge, -1 is left, 0 is on, 1 is right -*/ -static int del_classify_point( halfedge_t *d, point2d_t *pt ) -{ - point2d_t *s, *e; - - s = d->vertex; - e = d->pair->vertex; - - return classify_point_seg(s, e, pt); -} - -/* -* test if a point is inside a circle given by 3 points, 1 if inside, 0 if outside -*/ -static int in_circle( point2d_t *pt0, point2d_t *pt1, point2d_t *pt2, point2d_t *p ) -{ - // reduce the computational complexity by substracting the last row of the matrix - // ref: https://www.cs.cmu.edu/~quake/robust.html - lreal p0p_x, p0p_y, p1p_x, p1p_y, p2p_x, p2p_y, p0p, p1p, p2p, res; - mat3_t m; - - p0p_x = pt0->x - p->x; - p0p_y = pt0->y - p->y; - - p1p_x = pt1->x - p->x; - p1p_y = pt1->y - p->y; - - p2p_x = pt2->x - p->x; - p2p_y = pt2->y - p->y; - - p0p = p0p_x * p0p_x + p0p_y * p0p_y; - p1p = p1p_x * p1p_x + p1p_y * p1p_y; - p2p = p2p_x * p2p_x + p2p_y * p2p_y; - - m[0][0] = p0p_x; - m[0][1] = p0p_y; - m[0][2] = p0p; - - m[1][0] = p1p_x; - m[1][1] = p1p_y; - m[1][2] = p1p; - - m[2][0] = p2p_x; - m[2][1] = p2p_y; - m[2][2] = p2p; - - res = -det3(m); - - if( res < REAL_ZERO ) - return INSIDE; - else if( res > REAL_ZERO ) - return OUTSIDE; - - return ON_CIRCLE; -} - -/* -* initialize delaunay segment -*/ -static int del_init_seg( delaunay_t *del, int start ) -{ - halfedge_t *d0, *d1; - point2d_t *pt0, *pt1; - - /* init delaunay */ - del->start_point = start; - del->end_point = start + 1; - - /* setup pt0 and pt1 */ - pt0 = &(del->points[start]); - pt1 = &(del->points[start + 1]); - - /* allocate the halfedges and setup them */ - d0 = halfedge_alloc(); - d1 = halfedge_alloc(); - - d0->vertex = pt0; - d1->vertex = pt1; - - d0->next = d0->prev = d0; - d1->next = d1->prev = d1; - - d0->pair = d1; - d1->pair = d0; - - pt0->he = d0; - pt1->he = d1; - - del->rightmost_he = d1; - del->leftmost_he = d0; - - - return 0; -} - -/* -* initialize delaunay triangle -*/ -static int del_init_tri( delaunay_t *del, int start ) -{ - halfedge_t *d0, *d1, *d2, *d3, *d4, *d5; - point2d_t *pt0, *pt1, *pt2; - - /* initiate delaunay */ - del->start_point = start; - del->end_point = start + 2; - - /* setup the points */ - pt0 = &(del->points[start]); - pt1 = &(del->points[start + 1]); - pt2 = &(del->points[start + 2]); - - /* allocate the 6 halfedges */ - d0 = halfedge_alloc(); - d1 = halfedge_alloc(); - d2 = halfedge_alloc(); - d3 = halfedge_alloc(); - d4 = halfedge_alloc(); - d5 = halfedge_alloc(); - - if( classify_point_seg(pt0, pt2, pt1) == ON_LEFT ) /* first case */ - { - /* set halfedges points */ - d0->vertex = pt0; - d1->vertex = pt2; - d2->vertex = pt1; - - d3->vertex = pt2; - d4->vertex = pt1; - d5->vertex = pt0; - - /* set points halfedges */ - pt0->he = d0; - pt1->he = d2; - pt2->he = d1; - - /* next and next -1 setup */ - d0->next = d5; - d0->prev = d5; - - d1->next = d3; - d1->prev = d3; - - d2->next = d4; - d2->prev = d4; - - d3->next = d1; - d3->prev = d1; - - d4->next = d2; - d4->prev = d2; - - d5->next = d0; - d5->prev = d0; - - /* set halfedges pair */ - d0->pair = d3; - d3->pair = d0; - - d1->pair = d4; - d4->pair = d1; - - d2->pair = d5; - d5->pair = d2; - - del->rightmost_he = d1; - del->leftmost_he = d0; - - } else /* 2nd case */ - { - /* set halfedges points */ - d0->vertex = pt0; - d1->vertex = pt1; - d2->vertex = pt2; - - d3->vertex = pt1; - d4->vertex = pt2; - d5->vertex = pt0; - - /* set points halfedges */ - pt0->he = d0; - pt1->he = d1; - pt2->he = d2; - - /* next and next -1 setup */ - d0->next = d5; - d0->prev = d5; - - d1->next = d3; - d1->prev = d3; - - d2->next = d4; - d2->prev = d4; - - d3->next = d1; - d3->prev = d1; - - d4->next = d2; - d4->prev = d2; - - d5->next = d0; - d5->prev = d0; - - /* set halfedges pair */ - d0->pair = d3; - d3->pair = d0; - - d1->pair = d4; - d4->pair = d1; - - d2->pair = d5; - d5->pair = d2; - - del->rightmost_he = d2; - del->leftmost_he = d0; - } - - return 0; -} - -/* -* remove an edge given a halfedge -*/ -static void del_remove_edge( halfedge_t *d ) -{ - halfedge_t *next, *prev, *pair, *orig_pair; - - orig_pair = d->pair; - - next = d->next; - prev = d->prev; - pair = d->pair; - - assert(next != NULL); - assert(prev != NULL); - - next->prev = prev; - prev->next = next; - - - /* check to see if we have already removed pair */ - if( pair ) - pair->pair = NULL; - - /* check to see if the vertex points to this halfedge */ - if( d->vertex->he == d ) - d->vertex->he = next; - - d->vertex = NULL; - d->next = NULL; - d->prev = NULL; - d->pair = NULL; - - next = orig_pair->next; - prev = orig_pair->prev; - pair = orig_pair->pair; - - assert(next != NULL); - assert(prev != NULL); - - next->prev = prev; - prev->next = next; - - - /* check to see if we have already removed pair */ - if( pair ) - pair->pair = NULL; - - /* check to see if the vertex points to this halfedge */ - if( orig_pair->vertex->he == orig_pair ) - orig_pair->vertex->he = next; - - orig_pair->vertex = NULL; - orig_pair->next = NULL; - orig_pair->prev = NULL; - orig_pair->pair = NULL; - - - /* finally free the halfedges */ - halfedge_free(d); - halfedge_free(orig_pair); -} - -/* -* pass through all the halfedges on the left side and validate them -*/ -static halfedge_t* del_valid_left( halfedge_t* b ) -{ - point2d_t *g, *d, *u, *v; - halfedge_t *c, *du, *dg; - - g = b->vertex; /* base halfedge point */ - dg = b; - - d = b->pair->vertex; /* pair(halfedge) point */ - b = b->next; - - u = b->pair->vertex; /* next(pair(halfedge)) point */ - du = b->pair; - - v = b->next->pair->vertex; /* pair(next(next(halfedge)) point */ - - if( classify_point_seg(g, d, u) == ON_LEFT ) - { - /* 3 points aren't colinear */ - /* as long as the 4 points belong to the same circle, do the cleaning */ - assert( v != u && "1: floating point precision error"); - while( v != d && v != g && in_circle(g, d, u, v) == INSIDE ) - { - c = b->next; - du = b->next->pair; - del_remove_edge(b); - b = c; - u = du->vertex; - v = b->next->pair->vertex; - } - - assert( v != u && "2: floating point precision error"); - if( v != d && v != g && in_circle(g, d, u, v) == ON_CIRCLE ) - { - du = du->prev; - del_remove_edge(b); - } - } else /* treat the case where the 3 points are colinear */ - du = dg; - - assert(du->pair); - return du; -} - -/* -* pass through all the halfedges on the right side and validate them -*/ -static halfedge_t* del_valid_right( halfedge_t *b ) -{ - point2d_t *rv, *lv, *u, *v; - halfedge_t *c, *dd, *du; - - b = b->pair; - rv = b->vertex; - dd = b; - lv = b->pair->vertex; - b = b->prev; - u = b->pair->vertex; - du = b->pair; - - v = b->prev->pair->vertex; - - if( classify_point_seg(lv, rv, u) == ON_LEFT ) - { - assert( v != u && "1: floating point precision error"); - while( v != lv && v != rv && in_circle(lv, rv, u, v) == INSIDE ) - { - c = b->prev; - du = c->pair; - del_remove_edge(b); - b = c; - u = du->vertex; - v = b->prev->pair->vertex; - } - - assert( v != u && "1: floating point precision error"); - if( v != lv && v != rv && in_circle(lv, rv, u, v) == ON_CIRCLE ) - { - du = du->next; - del_remove_edge(b); - } - } else - du = dd; - - assert(du->pair); - return du; -} - - -/* -* validate a link -*/ -static halfedge_t* del_valid_link( halfedge_t *b ) -{ - point2d_t *g, *g_p, *d, *d_p; - halfedge_t *gd, *dd, *new_gd, *new_dd; - int a; - - g = b->vertex; - gd = del_valid_left(b); - g_p = gd->vertex; - - assert(b->pair); - d = b->pair->vertex; - dd = del_valid_right(b); - d_p = dd->vertex; - assert(b->pair); - - if( g != g_p && d != d_p ) { - a = in_circle(g, d, g_p, d_p); - - if( a != ON_CIRCLE ) { - if( a == INSIDE ) { - g_p = g; - gd = b; - } else { - d_p = d; - dd = b->pair; - } - } - } - - /* create the 2 halfedges */ - new_gd = halfedge_alloc(); - new_dd = halfedge_alloc(); - - /* setup new_gd and new_dd */ - - new_gd->vertex = gd->vertex; - new_gd->pair = new_dd; - new_gd->prev = gd; - new_gd->next = gd->next; - gd->next->prev = new_gd; - gd->next = new_gd; - - new_dd->vertex = dd->vertex; - new_dd->pair = new_gd; - new_dd->prev = dd->prev; - dd->prev->next = new_dd; - new_dd->next = dd; - dd->prev = new_dd; - - return new_gd; -} - -/* -* find the lower tangent between the two delaunay, going from left to right (returns the left half edge) -*/ -static halfedge_t* del_get_lower_tangent( delaunay_t *left, delaunay_t *right ) -{ - point2d_t *pl, *pr; - halfedge_t *right_d, *left_d, *new_ld, *new_rd; - int sl, sr; - - left_d = left->rightmost_he; - right_d = right->leftmost_he; - - do { - pl = left_d->prev->pair->vertex; - pr = right_d->pair->vertex; - - if( (sl = classify_point_seg(left_d->vertex, right_d->vertex, pl)) == ON_RIGHT ) { - left_d = left_d->prev->pair; - } - - if( (sr = classify_point_seg(left_d->vertex, right_d->vertex, pr)) == ON_RIGHT ) { - right_d = right_d->pair->next; - } - - } while( sl == ON_RIGHT || sr == ON_RIGHT ); - - /* create the 2 halfedges */ - new_ld = halfedge_alloc(); - new_rd = halfedge_alloc(); - - /* setup new_gd and new_dd */ - new_ld->vertex = left_d->vertex; - new_ld->pair = new_rd; - new_ld->prev = left_d->prev; - left_d->prev->next = new_ld; - new_ld->next = left_d; - left_d->prev = new_ld; - - new_rd->vertex = right_d->vertex; - new_rd->pair = new_ld; - new_rd->prev = right_d->prev; - right_d->prev->next = new_rd; - new_rd->next = right_d; - right_d->prev = new_rd; - - return new_ld; -} - -/* -* link the 2 delaunay together -*/ -static void del_link( delaunay_t *result, delaunay_t *left, delaunay_t *right ) -{ - point2d_t *u, *v, *ml, *mr; - halfedge_t *base; - - assert( left->points == right->points ); - - /* save the most right point and the most left point */ - ml = left->leftmost_he->vertex; - mr = right->rightmost_he->vertex; - - base = del_get_lower_tangent(left, right); - - u = base->next->pair->vertex; - v = base->pair->prev->pair->vertex; - - while( del_classify_point(base, u) == ON_LEFT || - del_classify_point(base, v) == ON_LEFT ) - { - base = del_valid_link(base); - u = base->next->pair->vertex; - v = base->pair->prev->pair->vertex; - } - - right->rightmost_he = mr->he; - left->leftmost_he = ml->he; - - /* TODO: this part is not needed, and can be optimized */ - while( del_classify_point( right->rightmost_he, right->rightmost_he->prev->pair->vertex ) == ON_RIGHT ) - right->rightmost_he = right->rightmost_he->prev; - - while( del_classify_point( left->leftmost_he, left->leftmost_he->prev->pair->vertex ) == ON_RIGHT ) - left->leftmost_he = left->leftmost_he->prev; - - result->leftmost_he = left->leftmost_he; - result->rightmost_he = right->rightmost_he; - result->points = left->points; - result->start_point = left->start_point; - result->end_point = right->end_point; -} - -/* -* divide and conquer delaunay -*/ -void del_divide_and_conquer( delaunay_t *del, int start, int end ) -{ - delaunay_t left, right; - int i, n; - - n = (end - start + 1); - - if( n > 3 ) { - i = (n / 2) + (n & 1); - left.points = del->points; - right.points = del->points; - del_divide_and_conquer( &left, start, start + i - 1 ); - del_divide_and_conquer( &right, start + i, end ); - del_link( del, &left, &right ); - } else { - if( n == 3 ) { - del_init_tri( del, start ); - } else { - if( n == 2 ) { - del_init_seg( del, start ); - } - } - } -} - -static void build_halfedge_face( delaunay_t *del, halfedge_t *d ) -{ - halfedge_t *curr; - - /* test if the halfedge has already a pointing face */ - if( d->face != NULL ) - return; - - /* TODO: optimize this */ - del->faces = (face_t*)realloc(del->faces, (del->num_faces + 1) * sizeof(face_t)); - assert( NULL != del->faces ); - - face_t *f = &(del->faces[del->num_faces]); - curr = d; - f->he = d; - f->num_verts = 0; - do { - curr->face = f; - (f->num_verts)++; - curr = curr->pair->prev; - } while( curr != d ); - - (del->num_faces)++; -} - -/* -* build the faces for all the halfedge -*/ -void del_build_faces( delaunay_t *del ) -{ - unsigned int i; - halfedge_t *curr; - - del->num_faces = 0; - del->faces = NULL; - - /* build external face first */ - build_halfedge_face(del, del->rightmost_he->pair); - - for( i = del->start_point; i <= del->end_point; i++ ) - { - curr = del->points[i].he; - - do { - build_halfedge_face( del, curr ); - curr = curr->next; - } while( curr != del->points[i].he ); - } -} - -/* -*/ -delaunay2d_t* delaunay2d_from(del_point2d_t *points, unsigned int num_points) { - delaunay2d_t* res = NULL; - delaunay_t del; - unsigned int i, j, fbuff_size = 0; - unsigned int* faces = NULL; - - /* allocate the points */ - del.points = (point2d_t*)malloc(num_points * sizeof(point2d_t)); - assert( NULL != del.points ); - memset(del.points, 0, num_points * sizeof(point2d_t)); - - /* copy the points */ - for( i = 0; i < num_points; i++ ) - { - del.points[i].idx = i; - del.points[i].x = points[i].x; - del.points[i].y = points[i].y; - } - - qsort(del.points, num_points, sizeof(point2d_t), cmp_points); - - if( num_points >= 3 ) { - del_divide_and_conquer( &del, 0, num_points - 1 ); - - del_build_faces( &del ); - - fbuff_size = 0; - for( i = 0; i < del.num_faces; i++ ) - fbuff_size += del.faces[i].num_verts + 1; - - faces = (unsigned int*)malloc(sizeof(unsigned int) * fbuff_size); - assert( NULL != faces ); - - j = 0; - for( i = 0; i < del.num_faces; i++ ) - { - halfedge_t *curr; - - faces[j] = del.faces[i].num_verts; - j++; - - curr = del.faces[i].he; - do { - faces[j] = curr->vertex->idx; - j++; - curr = curr->pair->prev; - } while( curr != del.faces[i].he ); - } - - del_free_halfedges( &del ); - - free(del.faces); - free(del.points); - } - - res = (delaunay2d_t*)malloc(sizeof(delaunay2d_t)); - assert( NULL != res ); - res->num_points = num_points; - res->points = (del_point2d_t*)malloc(sizeof(del_point2d_t) * num_points); - assert( NULL != res->points ); - memcpy(res->points, points, sizeof(del_point2d_t) * num_points); - res->num_faces = del.num_faces; - res->faces = faces; - - return res; -} - -void delaunay2d_release(delaunay2d_t *del) { - free(del->faces); - free(del->points); - free(del); -} - - -tri_delaunay2d_t* tri_delaunay2d_from(delaunay2d_t* del) { - unsigned int v_offset = del->faces[0] + 1; /* ignore external face */ - unsigned int dst_offset = 0; - unsigned int i; - - tri_delaunay2d_t* tdel = (tri_delaunay2d_t*)malloc(sizeof(tri_delaunay2d_t)); - assert( NULL != tdel ); - tdel->num_triangles = 0; - - /* count the number of triangles */ - if( 1 == del->num_faces ) { /* degenerate case: only external face exists */ - unsigned int nv = del->faces[0]; - tdel->num_triangles += nv - 2; - } else { - for( i = 1; i < del->num_faces; ++i ) { - unsigned int nv = del->faces[v_offset]; - tdel->num_triangles += nv - 2; - v_offset += nv + 1; - } - } - - /* copy points */ - tdel->num_points = del->num_points; - tdel->points = (del_point2d_t*)malloc(sizeof(del_point2d_t) * del->num_points); - assert( NULL != tdel->points ); - memcpy(tdel->points, del->points, sizeof(del_point2d_t) * del->num_points); - - /* build the triangles */ - tdel->tris = (unsigned int*)malloc(sizeof(unsigned int) * 3 * tdel->num_triangles); - assert( NULL != tdel->tris ); - - v_offset = del->faces[0] + 1; /* ignore external face */ - - if( 1 == del->num_faces ) { - /* handle the degenerated case where only the external face exists */ - unsigned int nv = del->faces[0]; - unsigned int j = 0; - v_offset = 1; - for( ; j < nv - 2; ++j ) { - tdel->tris[dst_offset] = del->faces[v_offset + j]; - tdel->tris[dst_offset + 1] = del->faces[(v_offset + j + 1) % nv]; - tdel->tris[dst_offset + 2] = del->faces[v_offset + j]; - dst_offset += 3; - } - } else { - for( i = 1; i < del->num_faces; ++i ) { - unsigned int nv = del->faces[v_offset]; - unsigned int j = 0; - unsigned int first = del->faces[v_offset + 1]; - - - for( ; j < nv - 2; ++j ) { - tdel->tris[dst_offset] = first; - tdel->tris[dst_offset + 1] = del->faces[v_offset + j + 2]; - tdel->tris[dst_offset + 2] = del->faces[v_offset + j + 3]; - dst_offset += 3; - } - - v_offset += nv + 1; - } - } - - return tdel; -} - - -void tri_delaunay2d_release(tri_delaunay2d_t* tdel) { - free(tdel->tris); - free(tdel->points); - free(tdel); -} - -#endif diff --git a/tools/3rd_mid.h b/tools/3rd_mid.h deleted file mode 100644 index 61b6d64..0000000 --- a/tools/3rd_mid.h +++ /dev/null @@ -1,464 +0,0 @@ -/* ------------------------------------------------------------------------------- - Licensing information can be found at the end of the file. ------------------------------------------------------------------------------- - -mid.h - v0.1 - Midi playback library using the TinySoundFont library. - -Do this: - #define MID_IMPLEMENTATION -before you include this file in *one* C/C++ file to create the implementation. -*/ - -#ifndef mid_h -#define mid_h - -#define _CRT_NONSTDC_NO_DEPRECATE -#define _CRT_SECURE_NO_WARNINGS -#include - -typedef struct mid_t mid_t; -typedef struct tsf tsf; - -mid_t* mid_create( void const* midi_data, size_t midi_size, void* memctx ); -void mid_destroy( mid_t* mid ); - -int mid_render_short( mid_t* mid, short* sample_pairs, int sample_pairs_count, tsf* sound_font ); -int mid_render_float( mid_t* mid, float* sample_pairs, int sample_pairs_count, tsf* sound_font ); - -void mid_skip_leading_silence( mid_t* mid, tsf* sound_font ); - -#endif /* mid_h */ - -#ifdef MID_ENABLE_RAW - -#ifndef mid_raw_h -#define mid_raw_h - -#ifndef MID_U8 - #define MID_U8 unsigned char -#endif - -#ifndef MID_U16 - #define MID_U16 unsigned short -#endif - -#ifndef MID_U32 - #define MID_U32 unsigned int -#endif - -#ifndef MID_U64 - #define MID_U64 unsigned long long -#endif - -typedef struct mid_event_t - { - MID_U32 delay_us; - MID_U8 channel; - MID_U8 type; - union - { - struct { MID_U8 program; } program_change; - struct { MID_U8 note; MID_U8 velocity; } note_on; - struct { MID_U8 note; } note_off; - struct { MID_U8 key; MID_U8 key_pressure; } key_pressure; - struct { MID_U16 value; } pitch_bend; - struct { MID_U8 control, control_value; } control_change; - struct { MID_U8 channel_pressure; } channel_pressure; - } data; - } mid_event_t; - - -typedef struct mid_song_t - { - int event_count; - mid_event_t* events; - } mid_song_t; - - -struct mid_t - { - void* memctx; - mid_song_t song; - int percussion_preset; - MID_U64 playback_accumulated_time_us; - int playback_sample_pos; - int playback_event_pos; - }; - -int mid_init_raw( mid_t* mid, void const* raw_data, size_t raw_size ); - -size_t mid_save_raw( mid_t* mid, void* data, size_t capacity ); - - -#endif /* MID_ENABLE_RAW */ - -#endif /* mid_raw_h */ - -/* ----------------------- - IMPLEMENTATION ----------------------- -*/ - -#ifdef MID_IMPLEMENTATION -#undef MID_IMPLEMENTATION - -#ifndef MID_U8 - #define MID_U8 unsigned char -#endif - -#ifndef MID_U16 - #define MID_U16 unsigned short -#endif - -#ifndef MID_U32 - #define MID_U32 unsigned int -#endif - -#ifndef MID_U64 - #define MID_U64 unsigned long long -#endif - -#ifndef MID_MALLOC - #define _CRT_NONSTDC_NO_DEPRECATE - #define _CRT_SECURE_NO_WARNINGS - #include - #if defined(_cplusplus) - #define MID_MALLOC( ctx, size ) ( ::malloc( size ) ) - #define MID_FREE( ctx, ptr ) ( ::free( ptr ) ) - #else - #define MID_MALLOC( ctx, size ) ( malloc( size ) ) - #define MID_FREE( ctx, ptr ) ( free( ptr ) ) - #endif -#endif -#include -#define MID_LOG(...) (void) __VA_ARGS__ - -#include - -#pragma warning( push ) -#pragma warning( disable: 4242 ) -#pragma warning( disable: 4244 ) -#pragma warning( disable: 4365 ) -#pragma warning( disable: 4668 ) -#pragma warning( disable: 4701 ) -#pragma warning( disable: 4703 ) - -#ifndef MID_NO_TSF_IMPLEMENTATION - #define TSF_NO_STDIO - #define TSF_IMPLEMENTATION -#endif -#include "3rd_tsf.h" - -#pragma warning( disable: 4201 ) - -#ifndef MID_NO_TML_IMPLEMENTATION - #define TML_NO_STDIO - #define TML_IMPLEMENTATION -#endif -#include "3rd_tml.h" - -#pragma warning( pop ) - - - - -#ifndef MID_ENABLE_RAW - -typedef struct mid_event_t - { - MID_U32 delay_us; - MID_U8 channel; - MID_U8 type; - union - { - struct { MID_U8 program; } program_change; - struct { MID_U8 note; MID_U8 velocity; } note_on; - struct { MID_U8 note; } note_off; - struct { MID_U8 key; MID_U8 key_pressure; } key_pressure; - struct { MID_U16 value; } pitch_bend; - struct { MID_U8 control, control_value; } control_change; - struct { MID_U8 channel_pressure; } channel_pressure; - } data; - } mid_event_t; - - -typedef struct mid_song_t - { - int event_count; - mid_event_t* events; - } mid_song_t; - - -struct mid_t - { - void* memctx; - mid_song_t song; - int percussion_preset; - MID_U64 playback_accumulated_time_us; - int playback_sample_pos; - int playback_event_pos; - }; - - -#endif /* MID_ENABLE_RAW */ - - -mid_t* mid_create( void const* midi_data, size_t midi_size, void* memctx ) - { - tml_message* mid_file = tml_load_memory( midi_data, (int) midi_size ); - if( !mid_file ) return NULL; - int count = 0; - tml_message* iter = mid_file; - while( iter ) - { - if( iter->type == TML_PROGRAM_CHANGE || iter->type == TML_NOTE_ON || iter->type == TML_NOTE_OFF || - iter->type == TML_PITCH_BEND || iter->type == TML_CONTROL_CHANGE ) - { - ++count; - } - iter = iter->next; - } - - mid_event_t* events = (mid_event_t*) malloc( sizeof( mid_event_t ) * count ); - int events_count = 0; - unsigned int time = 0; - tml_message* msg = mid_file; - while( msg ) - { - if( msg->type == TML_PROGRAM_CHANGE || msg->type == TML_NOTE_ON || msg->type == TML_NOTE_OFF || - msg->type == TML_PITCH_BEND || msg->type == TML_CONTROL_CHANGE ) - { - mid_event_t* event = &events[ events_count++ ]; - event->delay_us = ( msg->time - time ) * 1000; - time = msg->time; - event->channel = msg->channel; - event->type = msg->type; - switch( msg->type ) - { - case TML_PROGRAM_CHANGE: - event->data.program_change.program = (MID_U8) msg->program; - break; - case TML_NOTE_ON: //play a note - event->data.note_on.note = (MID_U8) msg->key; - event->data.note_on.velocity = (MID_U8) msg->velocity; - break; - case TML_NOTE_OFF: //stop a note - event->data.note_off.note = (MID_U8) msg->key; - break; - case TML_PITCH_BEND: //pitch wheel modification - event->data.pitch_bend.value = (MID_U16) msg->pitch_bend; - break; - case TML_CONTROL_CHANGE: //MIDI controller messages - event->data.control_change.control = (MID_U8) msg->control; - event->data.control_change.control_value = (MID_U8) msg->control_value; - break; - } - } - - msg = msg->next; - } - - tml_free( mid_file ); - - mid_t* mid = (mid_t*) MID_MALLOC( memctx, sizeof( mid_t ) ); - mid->memctx = memctx; - mid->song.event_count = events_count; - mid->song.events = events; - - mid->playback_accumulated_time_us = 0ull; - mid->playback_sample_pos = 0; - mid->playback_event_pos = 0; - - return mid; - } - - -void mid_destroy( mid_t* mid ) - { - if( mid->song.events ) MID_FREE( mid->memctx, mid->song.events ); - MID_FREE( mid->memctx, mid ); - } - - -int mid_init_raw( mid_t* mid, void const* raw_data, size_t raw_size ) - { - int events_count = *(int*)raw_data; - if( sizeof( mid_event_t ) * events_count != raw_size - sizeof( int ) ) return 0; - - mid->memctx = NULL; - - mid->song.event_count = events_count; - mid->song.events = (mid_event_t*)( ( (int*)raw_data ) + 1 ); - - mid->playback_accumulated_time_us = 0ull; - mid->playback_sample_pos = 0; - mid->playback_event_pos = 0; - - return 1; - } - - -size_t mid_save_raw( mid_t* mid, void* data, size_t capacity ) - { - size_t size = sizeof( mid_event_t ) * mid->song.event_count + sizeof( int ); - if( data && capacity >= size ) - { - *(int*)data = mid->song.event_count; - memcpy( ( (int*)data ) + 1, mid->song.events, sizeof( mid_event_t ) * mid->song.event_count ); - } - return size; - } - - -void mid_skip_leading_silence( mid_t* mid, tsf* sound_font ) - { - (void) sound_font; - for( ; ; ) - { - MID_U64 next_event_delay_us = mid->song.events[ mid->playback_event_pos ].delay_us; - MID_U64 playback_time_us = ( mid->playback_sample_pos * 1000000ull ) / 44100ull; - MID_U64 next_event_time_us = mid->playback_accumulated_time_us + next_event_delay_us; - assert( next_event_time_us >= playback_time_us ); - MID_U64 time_until_next_event = next_event_time_us - playback_time_us; - int samples_until_next_event = (int)( ( time_until_next_event * 44100ull ) / 1000000ull ); - mid_event_t* event = &mid->song.events[ mid->playback_event_pos ]; - switch( event->type ) - { - case TML_PROGRAM_CHANGE: - tsf_channel_set_presetnumber( sound_font, event->channel, event->data.program_change.program, ( event->channel == 9 ) ); - break; - case TML_NOTE_ON: - return; - case TML_NOTE_OFF: //stop a note - tsf_channel_note_off( sound_font, event->channel, event->data.note_off.note ); - break; - case TML_PITCH_BEND: //pitch wheel modification - tsf_channel_set_pitchwheel( sound_font, event->channel, event->data.pitch_bend.value ); - break; - case TML_CONTROL_CHANGE: //MIDI controller messages - tsf_channel_midi_control( sound_font, event->channel, event->data.control_change.control, event->data.control_change.control_value ); - break; - } - mid->playback_sample_pos += samples_until_next_event; - mid->playback_accumulated_time_us += next_event_delay_us; - mid->playback_event_pos++; - } - } - - -int mid_render_short( mid_t* mid, short* sample_pairs, int sample_pairs_count, tsf* sound_font ) - { - int samples_rendered = 0; - memset( sample_pairs, 0, sample_pairs_count * sizeof( short ) * 2 ); - while( samples_rendered < sample_pairs_count ) - { - MID_U64 next_event_delay_us = mid->song.events[ mid->playback_event_pos ].delay_us; - MID_U64 playback_time_us = ( mid->playback_sample_pos * 1000000ull ) / 44100ull; - MID_U64 next_event_time_us = mid->playback_accumulated_time_us + next_event_delay_us; - assert( next_event_time_us >= playback_time_us ); - MID_U64 time_until_next_event = next_event_time_us - playback_time_us; - int samples_until_next_event = (int)( ( time_until_next_event * 44100ull ) / 1000000ull ); - int samples_to_render = samples_until_next_event; - if( samples_to_render > sample_pairs_count - samples_rendered ) - { - samples_to_render = sample_pairs_count - samples_rendered; - tsf_render_short( sound_font, sample_pairs + samples_rendered * 2, - samples_to_render, 1 ); - samples_rendered += samples_to_render; - mid->playback_sample_pos += samples_to_render; - return samples_rendered; - } - else - { - tsf_render_short( sound_font, sample_pairs + samples_rendered * 2, - samples_to_render, 1 ); - samples_rendered += samples_to_render; - mid->playback_sample_pos += samples_to_render; - } - - - mid->playback_accumulated_time_us += next_event_delay_us; - mid_event_t* event = &mid->song.events[ mid->playback_event_pos++ ]; - switch( event->type ) - { - case TML_PROGRAM_CHANGE: - tsf_channel_set_presetnumber( sound_font, event->channel, event->data.program_change.program, ( event->channel == 9 ) ); - break; - case TML_NOTE_ON: - tsf_channel_note_on( sound_font, event->channel, event->data.note_on.note, event->data.note_on.velocity / 127.0f ); - break; - case TML_NOTE_OFF: //stop a note - tsf_channel_note_off( sound_font, event->channel, event->data.note_off.note ); - break; - case TML_PITCH_BEND: //pitch wheel modification - tsf_channel_set_pitchwheel( sound_font, event->channel, event->data.pitch_bend.value ); - break; - case TML_CONTROL_CHANGE: //MIDI controller messages - tsf_channel_midi_control( sound_font, event->channel, event->data.control_change.control, event->data.control_change.control_value ); - break; - } - } - - return samples_rendered; - } - - -#endif /* MID_IMPLEMENTATION */ - -/* ------------------------------------------------------------------------------- - -This software is available under 2 licenses - you may choose the one you like. - ------------------------------------------------------------------------------- - -ALTERNATIVE A - MIT License - -Copyright (c) 2016 Mattias Gustavsson - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -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 OR COPYRIGHT HOLDERS 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. - ------------------------------------------------------------------------------- - -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. - ------------------------------------------------------------------------------- -*/ diff --git a/tools/3rd_sproutline.h b/tools/3rd_sproutline.h deleted file mode 100644 index 462cbe8..0000000 --- a/tools/3rd_sproutline.h +++ /dev/null @@ -1,441 +0,0 @@ -/* sproutline - v0.10 - public domain sprite outline detector - http://github.org/ands/sproutline - no warranty implied; use at your own risk - - Do this: - #define S2O_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define S2O_IMPLEMENTATION - #include "sproutline.h" - - You can #define S2O_MALLOC to avoid using malloc - - - QUICK NOTES: - Primarily of interest to game developers. - - Recommended to be used with stb_image. - - Detects outlines in sprite images with alpha channels. - - Extracts outlines as clockwise paths. - - Simplifies outlines based on a distance metric. - - Full documentation under "DOCUMENTATION" below. - - - Revision 0.10 release notes: - - - Initial release of sproutline.h. - - - Added S2O_MALLOC macro for replacing the memory allocator. - Unlike most STB libraries, this macro doesn't support a context parameter, - so if you need to pass a context in to the allocator, you'll have to - store it in a global or a thread-local variable. - - - Revision history: - 0.10 (2015-10-22) initial version - - ============================ Contributors ========================= - - Andreas Mantler (ands) - -License: - This software is in the public domain. Where that dedication is not - recognized, you are granted a perpetual, irrevocable license to copy - and modify this file however you want. - -*/ - -#ifndef S2O_INCLUDE_SPROUTLINE_H -#define S2O_INCLUDE_SPROUTLINE_H - -// DOCUMENTATION -// -// Limitations: -// - currently only works with images that have alpha channels -// -// Basic usage (with stb_image): -// int w, h, n, l; -// unsigned char *rgba = stbi_load(filename, &w, &h, &n, 4); -// unsigned char *alpha = s2o_rgba_to_alpha(rgba, w, h); -// unsigned char *thresholded = s2o_alpha_to_thresholded(alpha, w, h, ALPHA_THRESHOLD); -// unsigned char *outlined = s2o_thresholded_to_outlined(thresholded, w, h); -// s2o_point *outline = s2o_extract_outline_path(outlined, w, h, &l, 0); -// while(l) -// { -// s2o_distance_based_path_simplification(outline, &l, DISTANCE_THRESHOLD); -// // ... process outline here ... -// // ... l = number of points in outline -// // ... ALPHA_THRESHOLD = 1..255 (the min value to be considered solid) -// // ... DISTANCE_THRESHOLD = 0.0f..Inf (~0.5f is a suitable value) -// // ... a greater value results in fewer points in the output -// -// outline = s2o_extract_outline_path(outlined, w, h, &l, outline); -// }; -// free(outline); -// free(outlined); -// free(thresholded); -// free(alpha); -// free(rgba); -// -// s2o_rgba_to_alpha: -// Expects an 'unsigned char *' to memory of w * h 4-byte pixels in 'RGBA' order. -// The return value is an 'unsigned char *' to memory of w * h 1-byte pixel alpha components. -// -// s2o_alpha_to_thresholded: -// Expects an 'unsigned char *' to memory of w * h 1-byte pixel alpha components. -// The return value is an 'unsigned char *' to memory of w * h 1-byte values -// that are 255 if the corresponding input is >= the specified threshold, otherwise 0. -// -// s2o_thresholded_to_outlined: -// Expects an 'unsigned char *' to memory of w * h 1-byte pixels indicating their solidity {0, nonzero}. -// The return value is an 'unsigned char *' to memory of w * h 1-byte pixels that indicate if the -// corresponding input value is part of an outline (= is solid and has a non-solid neighbour). -// -// s2o_extract_outline_path: -// Expects an 'unsigned char *' to memory of w * h 1-byte pixels indicating their outline membership. -// The return value is an 's2o_point *' to memory of l s2o_point values consisting of a short x and y value. -// The procedure scans the input data from top to bottom and starts extracting the first outline it finds. -// The pixels corresponding to the extracted outline are set to 0 in the input, so that a subsequent call to -// s2o_extract_outline_path extracts a different outline. -// The length is set to 0 if no outline was found. -// -// s2o_distance_based_path_simplification: -// Expects an 's2o_point *' to memory of l outline points. -// The procedure throws out points in place that lie on or close to linear sections of the outline. -// The distanceThreshold parameter specifies the min distance value for points to remain in the outline. -// -// =========================================================================== -// -// Philosophy -// -// This library is designed with the stb philosophy in mind. -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small footprint ("easy to maintain") -// - No dependencies ("ease of use") -// - -typedef unsigned char s2o_uc; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef S2O_STATIC -#define S2ODEF static -#else -#define S2ODEF extern -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API -// - -S2ODEF s2o_uc * s2o_rgba_to_alpha (const s2o_uc *data, int w, int h); -S2ODEF s2o_uc * s2o_alpha_to_thresholded (const s2o_uc *data, int w, int h, s2o_uc threshold); -S2ODEF s2o_uc * s2o_thresholded_to_outlined(const s2o_uc *data, int w, int h); - -typedef struct { short x, y; } s2o_point; -S2ODEF s2o_point * s2o_extract_outline_path(s2o_uc *data, int w, int h, int *point_count, s2o_point *reusable_outline); -S2ODEF void s2o_distance_based_path_simplification(s2o_point *outline, int *outline_length, float distance_threshold); - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // S2O_INCLUDE_SPROUTLINE_H - -#ifdef S2O_IMPLEMENTATION - -#include // sqrtf, abs - -#ifndef S2O_MALLOC -#include // malloc -#define S2O_MALLOC(sz) malloc(sz) -#endif - -/////////////////////////////////////////////// -// -// locally used types - -typedef int s2o_bool; - -// 2d point type helpers -#define S2O_POINT_ADD(result, a, b) { (result).x = (a).x + (b).x; (result).y = (a).y + (b).y; } -#define S2O_POINT_SUB(result, a, b) { (result).x = (a).x - (b).x; (result).y = (a).y - (b).y; } -#define S2O_POINT_IS_INSIDE(a, w, h) ((a).x >= 0 && (a).y >= 0 && (a).x < (w) && (a).y < (h)) -#define S2O_POINT_IS_NEXT_TO(a, b) ((a).x - (b).x <= 1 && (a).x - (b).x >= -1 && (a).y - (b).y <= 1 && (a).y - (b).y >= -1) - -// direction type -typedef int s2o_direction; // 8 cw directions: >, _|, v, |_, <, |", ^, "| -#define S2O_DIRECTION_OPPOSITE(dir) ((dir + 4) & 7) -static const s2o_point s2o_direction_to_pixel_offset[] = { {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1} }; - -// image manipulation functions -S2ODEF s2o_uc * s2o_rgba_to_alpha(const s2o_uc *data, int w, int h) -{ - s2o_uc *result = (s2o_uc*)S2O_MALLOC(w * h); - int x, y; - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) - result[y * w + x] = data[(y * w + x) * 4 + 3]; - return result; -} - -S2ODEF s2o_uc * s2o_alpha_to_thresholded(const s2o_uc *data, int w, int h, s2o_uc threshold) -{ - s2o_uc *result = (s2o_uc*)S2O_MALLOC(w * h); - int x, y; - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) - result[y * w + x] = data[y * w + x] >= threshold ? 255 : 0; - return result; -} - -S2ODEF s2o_uc * s2o_dilate_thresholded(const s2o_uc *data, int w, int h) -{ - int x, y, dx, dy, cx, cy; - s2o_uc *result = (s2o_uc*)S2O_MALLOC(w * h); - for (y = 0; y < h; y++) - { - for (x = 0; x < w; x++) - { - result[y * w + x] = 0; - for (dy = -1; dy <= 1; dy++) - { - for (dx = -1; dx <= 1; dx++) - { - cx = x + dx; - cy = y + dy; - if (cx >= 0 && cx < w && cy >= 0 && cy < h) - { - if (data[cy * w + cx]) - { - result[y * w + x] = 255; - dy = 1; - break; - } - } - } - } - } - } - return result; -} - -S2ODEF s2o_uc * s2o_thresholded_to_outlined(const s2o_uc *data, int w, int h) -{ - s2o_uc *result = (s2o_uc*)S2O_MALLOC(w * h); - int x, y; - for (x = 0; x < w; x++) - { - result[x] = data[x]; - result[(h - 1) * w + x] = data[(h - 1) * w + x]; - } - for (y = 1; y < h - 1; y++) - { - result[y * w] = data[y * w]; - for (x = 1; x < w - 1; x++) - { - if (data[y * w + x] && - ( - !data[y * w + x - 1] || - !data[y * w + x + 1] || - !data[y * w + x - w] || - !data[y * w + x + w] - )) - { - result[y * w + x] = 255; - } - else - { - result[y * w + x] = 0; - } - } - result[y * w + w - 1] = data[y * w + w - 1]; - } - return result; -} - -// outline path procedures -static s2o_bool s2o_find_first_filled_pixel(const s2o_uc *data, int w, int h, s2o_point *first) -{ - int x, y; - for (y = 0; y < h; y++) - { - for (x = 0; x < w; x++) - { - if (data[y * w + x]) - { - first->x = (short)x; - first->y = (short)y; - return 1; - } - } - } - return 0; -} - -static s2o_bool s2o_find_next_filled_pixel(const s2o_uc *data, int w, int h, s2o_point current, s2o_direction *dir, s2o_point *next) -{ - // turn around 180°, then make a clockwise scan for a filled pixel - *dir = S2O_DIRECTION_OPPOSITE(*dir); - int i; - for (i = 0; i < 8; i++) - { - S2O_POINT_ADD(*next, current, s2o_direction_to_pixel_offset[*dir]); - - if (S2O_POINT_IS_INSIDE(*next, w, h) && data[next->y * w + next->x]) - return 1; - - // move to next angle (clockwise) - *dir = *dir - 1; - if (*dir < 0) - *dir = 7; - } - return 0; -} - -S2ODEF s2o_point * s2o_extract_outline_path(s2o_uc *data, int w, int h, int *point_count, s2o_point *reusable_outline) -{ - s2o_point *outline = reusable_outline; - if (!outline) - outline = (s2o_point*)S2O_MALLOC(w * h * sizeof(s2o_point)); - - s2o_point current, next; - -restart: - if (!s2o_find_first_filled_pixel(data, w, h, ¤t)) - { - *point_count = 0; - return outline; - } - - int count = 0; - s2o_direction dir = 0; - - while(S2O_POINT_IS_INSIDE(current, w, h) && count < (w*h)) //< @r-lyeh: buffer overflow: add count= 0 && count < (w * h); prev--) //< @r-lyeh: buffer overflow: add count 1; l--) - { - int a, b = l; - for (a = 0; a < length; a++) - { - s2o_point ab; - S2O_POINT_SUB(ab, outline[b], outline[a]); - float lab = sqrtf((float)(ab.x * ab.x + ab.y * ab.y)); - float ilab = 1.0f / lab; - float abnx = ab.x * ilab, abny = ab.y * ilab; - - if (lab != 0.0f) - { - s2o_bool found = 1; - int i = (a + 1) % length; - while (i != b) - { - s2o_point ai; - S2O_POINT_SUB(ai, outline[i], outline[a]); - float t = (abnx * ai.x + abny * ai.y) * ilab; - float distance = -abny * ai.x + abnx * ai.y; - if (t < 0.0f || t > 1.0f || distance > distance_threshold || -distance > distance_threshold) - { - found = 0; - break; - } - - if (++i == length) - i = 0; - } - - if (found) - { - int i; - if (a < b) - { - for (i = 0; i < length - b; i++) - outline[a + i + 1] = outline[b + i]; - length -= b - a - 1; - } - else - { - length = a - b + 1; - for (i = 0; i < length; i++) - outline[i] = outline[b + i]; - } - if (l >= length) - l = length - 1; - } - } - - if (++b >= length) - b = 0; - } - } - *outline_length = length; -} - -#endif // S2O_IMPLEMENTATION diff --git a/tools/ase2ini.c b/tools/ase2ini.c index 1febe7f..a36460f 100644 --- a/tools/ase2ini.c +++ b/tools/ase2ini.c @@ -52,7 +52,9 @@ size_t vlen( void* p ) { return p ? 0[ (size_t*)p - 2 ] : 0; } -char *STRDUP(const char *s) { size_t n = strlen(s)+1; return ((char*)memcpy(ATLAS_REALLOC(0,n), s, n)); } ///- +char *vstrdup(const char *s) { size_t n = strlen(s)+1; return ((char*)memcpy(ATLAS_REALLOC(0,n), s, n)); } ///- + +#define STRDUP vstrdup static unsigned array_c; #define array(t) t* @@ -126,6 +128,7 @@ char* strcatf(char **src_, const char *fmt, ...) { #define ATLASC_IMPLEMENTATION #include "3rd_atlasc.h" + #if defined _WIN32 && defined _MSC_VER __declspec(dllexport) #elif defined _WIN32 && defined __GNUC__ @@ -164,7 +167,6 @@ int main(int argc, char* argv[]) { for( int i = 1; i < argc; ++i) { const char *arg = argv[i]; if( arg[0] != '-' ) { - assert(array_count(files) == 0); if( path_isfile(arg) ) array_push(files, STRDUP(arg)); else @@ -174,7 +176,6 @@ int main(int argc, char* argv[]) { array_push(files, STRDUP(dir_name(d, i))); } } - assert(array_count(files) == 1); } else switch( arg[1] ) { @@ -209,19 +210,23 @@ int main(int argc, char* argv[]) { char *error = g_error_str; - atlas_t* atlas = atlas_loadfiles(files, flags); - if ( atlas ) { - bool r = atlas_save(outfile, atlas, flags); - if( r ) { - // fprintf(stderr, "Written: %d->%d\n", flags.num_files, !!r); - error = 0; + if( !files ) { + error = "No input file(s)"; + } else { + atlas_t* atlas = atlas_loadfiles(files, flags); + if ( atlas ) { + bool r = atlas_save(outfile, atlas, flags); + if( r ) { + // fprintf(stderr, "Written: %d->%d\n", flags.num_files, !!r); + error = 0; + } + atlas_free(atlas); } - atlas_free(atlas); - } - // for( int i = 0; i < array_count(files); ++i) - // ATLAS_REALLOC(files[i], 0); - // array_free(files); + for( int i = 0; i < array_count(files); ++i) + ATLAS_REALLOC(files[i], 0); + array_free(files); + } return error ? fprintf(stderr, "%s\n", error), -1 : 0; } diff --git a/tools/ase2ini.exe b/tools/ase2ini.exe index 86b9cfe..df1f458 100644 Binary files a/tools/ase2ini.exe and b/tools/ase2ini.exe differ