diff --git a/README.md b/README.md index 0bc0844..f7b8dc2 100644 --- a/README.md +++ b/README.md @@ -11,17 +11,17 @@ by Jorge L. "VinoBS" Rodriguez, and stb_sprintf by Jeff Roberts. library | lastest version | category | LoC | description --------------------- | ---- | -------- | --- | -------------------------------- -**[stb_vorbis.c](stb_vorbis.c)** | 1.11 | audio | 5449 | decode ogg vorbis files from file/memory to float/16-bit signed output -**[stb_image.h](stb_image.h)** | 2.16 | graphics | 7187 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC -**[stb_truetype.h](stb_truetype.h)** | 1.17 | graphics | 4566 | parse, decode, and rasterize characters from truetype fonts -**[stb_image_write.h](stb_image_write.h)** | 1.07 | graphics | 1458 | image writing to disk: PNG, TGA, BMP +**[stb_vorbis.c](stb_vorbis.c)** | 1.14 | audio | 5462 | decode ogg vorbis files from file/memory to float/16-bit signed output +**[stb_image.h](stb_image.h)** | 2.19 | graphics | 7462 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC +**[stb_truetype.h](stb_truetype.h)** | 1.19 | graphics | 4853 | parse, decode, and rasterize characters from truetype fonts +**[stb_image_write.h](stb_image_write.h)** | 1.09 | graphics | 1568 | image writing to disk: PNG, TGA, BMP **[stb_image_resize.h](stb_image_resize.h)** | 0.95 | graphics | 2627 | resize images larger/smaller with good quality **[stb_rect_pack.h](stb_rect_pack.h)** | 0.11 | graphics | 624 | simple 2D rectangle packer with decent quality -**[stb_sprintf.h](stb_sprintf.h)** | 1.03 | utility | 1812 | fast sprintf, snprintf for C/C++ -**[stretchy_buffer.h](stretchy_buffer.h)** | 1.02 | utility | 257 | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++ -**[stb_textedit.h](stb_textedit.h)** | 1.11 | user interface | 1393 | guts of a text editor for games etc implementing them from scratch +**[stb_sprintf.h](stb_sprintf.h)** | 1.05 | utility | 1833 | fast sprintf, snprintf for C/C++ +**[stretchy_buffer.h](stretchy_buffer.h)** | 1.03 | utility | 262 | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++ +**[stb_textedit.h](stb_textedit.h)** | 1.12 | user interface | 1404 | guts of a text editor for games etc implementing them from scratch **[stb_voxel_render.h](stb_voxel_render.h)** | 0.85 | 3D graphics | 3803 | Minecraft-esque voxel rendering "engine" with many more features -**[stb_dxt.h](stb_dxt.h)** | 1.07 | 3D graphics | 719 | Fabian "ryg" Giesen's real-time DXT compressor +**[stb_dxt.h](stb_dxt.h)** | 1.08b | 3D graphics | 728 | Fabian "ryg" Giesen's real-time DXT compressor **[stb_perlin.h](stb_perlin.h)** | 0.3 | 3D graphics | 316 | revised Perlin noise (3D input, 1D output) **[stb_easy_font.h](stb_easy_font.h)** | 1.0 | 3D graphics | 303 | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc **[stb_tilemap_editor.h](stb_tilemap_editor.h)** | 0.38 | game dev | 4172 | embeddable tilemap editor @@ -29,11 +29,11 @@ library | lastest version | category | LoC | description **[stb_c_lexer.h](stb_c_lexer.h)** | 0.09 | parsing | 962 | simplify writing parsers for C-like languages **[stb_divide.h](stb_divide.h)** | 0.91 | math | 419 | more useful 32-bit modulus e.g. "euclidean divide" **[stb_connected_comp...](stb_connected_components.h)** | 0.95 | misc | 1045 | incrementally compute reachability on grids -**[stb.h](stb.h)** | 2.30 | misc | 14328 | helper functions for C, mostly redundant in C++; basically author's personal stuff +**[stb.h](stb.h)** | 2.31 | misc | 14405 | helper functions for C, mostly redundant in C++; basically author's personal stuff **[stb_leakcheck.h](stb_leakcheck.h)** | 0.4 | misc | 186 | quick-and-dirty malloc/free leak-checking Total libraries: 20 -Total lines of C code: 52846 +Total lines of C code: 53654 FAQ diff --git a/stb.h b/stb.h index c1523d1..5a4e0de 100644 --- a/stb.h +++ b/stb.h @@ -1,4 +1,4 @@ -/* stb.h - v2.30 - Sean's Tool Box -- public domain -- http://nothings.org/stb.h +/* stb.h - v2.31 - Sean's Tool Box -- public domain -- http://nothings.org/stb.h no warranty is offered or implied; use this code at your own risk This is a single header file with a bunch of useful utilities @@ -25,6 +25,7 @@ Version History + 2.31 stb_ucharcmp 2.30 MinGW fix 2.29 attempt to fix use of swprintf() 2.28 various new functionality @@ -193,6 +194,7 @@ CREDITS Eugene Opalev Tim Sjostrand github:infatum + Dave Butler (Croepha) */ #include @@ -1028,8 +1030,8 @@ stb__wchar *stb__from_utf8(char *str) stb__wchar *stb__from_utf8_alt(char *str) { - static stb__wchar buffer[64]; - return stb_from_utf8(buffer, str, 64); + static stb__wchar buffer[4096]; + return stb_from_utf8(buffer, str, 4096); } char *stb__to_utf8(stb__wchar *str) @@ -1548,7 +1550,7 @@ STB_EXTERN int (*stb_doublecmp(int offset))(const void *a, const void *b); STB_EXTERN int (*stb_charcmp(int offset))(const void *a, const void *b); #ifdef STB_DEFINE -static int stb__intcmpoffset, stb__charcmpoffset, stb__strcmpoffset; +static int stb__intcmpoffset, stb__ucharcmpoffset, stb__strcmpoffset; static int stb__floatcmpoffset, stb__doublecmpoffset; int stb__intcmp(const void *a, const void *b) @@ -1558,10 +1560,10 @@ int stb__intcmp(const void *a, const void *b) return p < q ? -1 : p > q; } -int stb__charcmp(const void *a, const void *b) +int stb__ucharcmp(const void *a, const void *b) { - const int p = *(const unsigned char *) ((const char *) a + stb__charcmpoffset); - const int q = *(const unsigned char *) ((const char *) b + stb__charcmpoffset); + const int p = *(const unsigned char *) ((const char *) a + stb__ucharcmpoffset); + const int q = *(const unsigned char *) ((const char *) b + stb__ucharcmpoffset); return p < q ? -1 : p > q; } @@ -1599,10 +1601,10 @@ int (*stb_intcmp(int offset))(const void *, const void *) return &stb__intcmp; } -int (*stb_charcmp(int offset))(const void *, const void *) +int (*stb_ucharcmp(int offset))(const void *, const void *) { - stb__charcmpoffset = offset; - return &stb__charcmp; + stb__ucharcmpoffset = offset; + return &stb__ucharcmp; } int (*stb_qsort_strcmp(int offset))(const void *, const void *) @@ -1628,7 +1630,6 @@ int (*stb_doublecmp(int offset))(const void *, const void *) stb__doublecmpoffset = offset; return &stb__doublecmp; } - #endif ////////////////////////////////////////////////////////////////////////////// @@ -1795,7 +1796,7 @@ STB_EXTERN int stb_prefix (char *s, char *t); STB_EXTERN char * stb_strichr(char *s, char t); STB_EXTERN char * stb_stristr(char *s, char *t); STB_EXTERN int stb_prefix_count(char *s, char *t); -STB_EXTERN char * stb_plural(int n); // "s" or "" +STB_EXTERN const char * stb_plural(int n); // "s" or "" STB_EXTERN size_t stb_strscpy(char *d, const char *s, size_t n); STB_EXTERN char **stb_tokens(char *src, char *delimit, int *count); @@ -1822,7 +1823,7 @@ size_t stb_strscpy(char *d, const char *s, size_t n) return len + 1; } -char *stb_plural(int n) +const char *stb_plural(int n) { return n == 1 ? "" : "s"; } @@ -3362,7 +3363,7 @@ unsigned int stb_hashlen(char *str, int len) unsigned int stb_hashptr(void *p) { - unsigned int x = (unsigned int) p; + unsigned int x = (unsigned int)(size_t) p; // typically lacking in low bits and high bits x = stb_rehash(x); @@ -3411,7 +3412,7 @@ unsigned int stb_hash_fast(void *p, int len) if (len <= 0 || q == NULL) return 0; /* Main loop */ - if (((int) q & 1) == 0) { + if (((int)(size_t) q & 1) == 0) { for (;len > 3; len -= 4) { unsigned int val; hash += stb__get16(q); @@ -3737,7 +3738,7 @@ int stb_ischar(char c, char *set) static unsigned char (*tables)[256]; static char ** sets = NULL; - int z = stb_perfect_hash(&p, (int) set); + int z = stb_perfect_hash(&p, (int)(size_t) set); if (z < 0) { int i,k,n,j,f; // special code that means free all existing data @@ -3756,7 +3757,7 @@ int stb_ischar(char c, char *set) tables = (unsigned char (*)[256]) realloc(tables, sizeof(*tables) * k); memset(tables, 0, sizeof(*tables) * k); for (i=0; i < stb_arr_len(sets); ++i) { - k = stb_perfect_hash(&p, (int) sets[i]); + k = stb_perfect_hash(&p, (int)(size_t) sets[i]); assert(k >= 0); n = k >> 3; f = bit[k&7]; @@ -3764,7 +3765,7 @@ int stb_ischar(char c, char *set) tables[n][(unsigned char) sets[i][j]] |= f; } } - z = stb_perfect_hash(&p, (int) set); + z = stb_perfect_hash(&p, (int)(size_t) set); } return tables[z >> 3][(unsigned char) c] & bit[z & 7]; } @@ -4245,12 +4246,22 @@ STB_EXTERN void stb_sdict_delete(stb_sdict *); STB_EXTERN void * stb_sdict_change(stb_sdict *, char *str, void *p); STB_EXTERN int stb_sdict_count(stb_sdict *d); +STB_EXTERN int stb_sdict_internal_limit(stb_sdict *d); +STB_EXTERN char * stb_sdict_internal_key(stb_sdict *d, int n); +STB_EXTERN void * stb_sdict_internal_value(stb_sdict *d, int n); + #define stb_sdict_for(d,i,q,z) \ - for(i=0; i < (d)->limit ? q=(d)->table[i].k,z=(d)->table[i].v,1 : 0; ++i) \ + for(i=0; i < stb_sdict_internal_limit(d) ? (q=stb_sdict_internal_key(d,i),z=stb_sdict_internal_value(d,i),1) : 0; ++i) \ if (q==NULL||q==(void *) 1);else // reversed makes macro friendly #ifdef STB_DEFINE +// if in same translation unit, for speed, don't call accessors +#undef stb_sdict_for +#define stb_sdict_for(d,i,q,z) \ + for(i=0; i < (d)->limit ? (q=(d)->table[i].k,z=(d)->table[i].v,1) : 0; ++i) \ + if (q==NULL||q==(void *) 1);else // reversed makes macro friendly + #define STB_DEL ((void *) 1) #define STB_SDEL ((char *) 1) @@ -4270,6 +4281,19 @@ int stb_sdict_count(stb_sdict *a) return a->count; } +int stb_sdict_internal_limit(stb_sdict *a) +{ + return a->limit; +} +char* stb_sdict_internal_key(stb_sdict *a, int n) +{ + return a->table[n].k; +} +void* stb_sdict_internal_value(stb_sdict *a, int n) +{ + return a->table[n].v; +} + stb_sdict * stb_sdict_new(int use_arena) { stb_sdict *d = stb_sdict_create(); @@ -5439,15 +5463,62 @@ typedef struct int errors; } stb__file_data; +static FILE *stb__open_temp_file(char *temp_name, char *src_name, char *mode) +{ + int p; +#ifdef _MSC_VER + int j; +#endif + FILE *f; + // try to generate a temporary file in the same directory + p = strlen(src_name)-1; + while (p > 0 && src_name[p] != '/' && src_name[p] != '\\' + && src_name[p] != ':' && src_name[p] != '~') + --p; + ++p; + + memcpy(temp_name, src_name, p); + + #ifdef _MSC_VER + // try multiple times to make a temp file... just in + // case some other process makes the name first + for (j=0; j < 32; ++j) { + strcpy(temp_name+p, "stmpXXXXXX"); + if (stb_mktemp(temp_name) == NULL) + return 0; + + f = fopen(temp_name, mode); + if (f != NULL) + break; + } + #else + { + strcpy(temp_name+p, "stmpXXXXXX"); + #ifdef __MINGW32__ + int fd = open(mktemp(temp_name), O_RDWR); + #else + int fd = mkstemp(temp_name); + #endif + if (fd == -1) return NULL; + f = fdopen(fd, mode); + if (f == NULL) { + unlink(temp_name); + close(fd); + return NULL; + } + } + #endif + return f; +} + + FILE * stb_fopen(char *filename, char *mode) { FILE *f; char name_full[4096]; char temp_full[sizeof(name_full) + 12]; - int p; -#ifdef _MSC_VER - int j; -#endif + + // @TODO: if the file doesn't exist, we can also use the fastpath here if (mode[0] != 'w' && !strchr(mode, '+')) return stb__fopen(filename, mode); @@ -5458,44 +5529,7 @@ FILE * stb_fopen(char *filename, char *mode) if (stb_fullpath(name_full, sizeof(name_full), filename)==0) return 0; - // try to generate a temporary file in the same directory - p = strlen(name_full)-1; - while (p > 0 && name_full[p] != '/' && name_full[p] != '\\' - && name_full[p] != ':' && name_full[p] != '~') - --p; - ++p; - - memcpy(temp_full, name_full, p); - - #ifdef _MSC_VER - // try multiple times to make a temp file... just in - // case some other process makes the name first - for (j=0; j < 32; ++j) { - strcpy(temp_full+p, "stmpXXXXXX"); - if (stb_mktemp(temp_full) == NULL) - return 0; - - f = fopen(temp_full, mode); - if (f != NULL) - break; - } - #else - { - strcpy(temp_full+p, "stmpXXXXXX"); - #ifdef __MINGW32__ - int fd = open(mktemp(temp_full), O_RDWR); - #else - int fd = mkstemp(temp_full); - #endif - if (fd == -1) return NULL; - f = fdopen(fd, mode); - if (f == NULL) { - unlink(temp_full); - close(fd); - return NULL; - } - } - #endif + f = stb__open_temp_file(temp_full, name_full, mode); if (f != NULL) { stb__file_data *d = (stb__file_data *) malloc(sizeof(*d)); if (!d) { assert(0); /* NOTREACHED */fclose(f); return NULL; } @@ -5538,21 +5572,63 @@ int stb_fclose(FILE *f, int keep) } } - if (keep != stb_keep_no) { - if (stb_fexists(d->name) && remove(d->name)) { - // failed to delete old, so don't keep new - keep = stb_keep_no; + if (keep == stb_keep_no) { + remove(d->temp_name); + } else { + if (!stb_fexists(d->name)) { + // old file doesn't exist, so just move the new file over it + stb_rename(d->temp_name, d->name); } else { - if (!stb_rename(d->temp_name, d->name)) - ok = STB_TRUE; - else - keep=stb_keep_no; + // don't delete the old file yet in case there are troubles! First rename it! + char preserved_old_file[4096]; + + // generate a temp filename in the same directory (also creates it, which we don't need) + FILE *dummy = stb__open_temp_file(preserved_old_file, d->name, "wb"); + if (dummy != NULL) { + // we don't actually want the open file + fclose(dummy); + + // discard what we just created + remove(preserved_old_file); // if this fails, there's nothing we can do, and following logic handles it as best as possible anyway + + // move the existing file to the preserved name + if (0 != stb_rename(d->name, preserved_old_file)) { // 0 on success + // failed, state is: + // filename -> old file + // tempname -> new file + // keep tempname around so we don't lose data + } else { + // state is: + // preserved -> old file + // tempname -> new file + // move the new file to the old name + if (0 == stb_rename(d->temp_name, d->name)) { + // state is: + // preserved -> old file + // filename -> new file + ok = STB_TRUE; + + // 'filename -> new file' has always been the goal, so clean up + remove(preserved_old_file); // nothing to be done if it fails + } else { + // couldn't rename, so try renaming preserved file back + + // state is: + // preserved -> old file + // tempname -> new file + stb_rename(preserved_old_file, d->name); + // if the rename failed, there's nothing more we can do + } + } + } else { + // we couldn't get a temp filename. do this the naive way; the worst case failure here + // leaves the filename pointing to nothing and the new file as a tempfile + remove(d->name); + stb_rename(d->temp_name, d->name); + } } } - if (keep == stb_keep_no) - remove(d->temp_name); - free(d->temp_name); free(d->name); free(d); @@ -7631,14 +7707,14 @@ static stb_ps_hash *stb_ps_makehash(int size, int old_size, void **old_data) h->any_offset = 0; memset(h->table, 0, size * sizeof(h->table[0])); for (i=0; i < old_size; ++i) - if (!stb_ps_empty(old_data[i])) + if (!stb_ps_empty((size_t)old_data[i])) stb_ps_add(EncodeHash(h), old_data[i]); return h; } void stb_ps_delete(stb_ps *ps) { - switch (3 & (int) ps) { + switch (3 & (int)(size_t) ps) { case STB_ps_direct: break; case STB_ps_bucket: stb_bucket_free(GetBucket(ps)); break; case STB_ps_array : free(GetArray(ps)); break; @@ -7650,7 +7726,7 @@ stb_ps *stb_ps_copy(stb_ps *ps) { int i; // not a switch: order based on expected performance/power-law distribution - switch (3 & (int) ps) { + switch (3 & (int)(size_t) ps) { case STB_ps_direct: return ps; case STB_ps_bucket: { stb_ps_bucket *n = (stb_ps_bucket *) malloc(sizeof(*n)); @@ -7677,8 +7753,8 @@ stb_ps *stb_ps_copy(stb_ps *ps) int stb_ps_find(stb_ps *ps, void *value) { - int i, code = 3 & (int) ps; - assert((3 & (int) value) == STB_ps_direct); + int i, code = 3 & (int)(size_t) ps; + assert((3 & (int)(size_t) value) == STB_ps_direct); assert(stb_ps_fastlist_valid(value)); // not a switch: order based on expected performance/power-law distribution if (code == STB_ps_direct) @@ -7719,11 +7795,11 @@ stb_ps * stb_ps_add (stb_ps *ps, void *value) assert(!stb_ps_find(ps,value)); #endif if (value == NULL) return ps; // ignore NULL adds to avoid bad breakage - assert((3 & (int) value) == STB_ps_direct); + assert((3 & (int)(size_t) value) == STB_ps_direct); assert(stb_ps_fastlist_valid(value)); assert(value != STB_DEL); // STB_DEL is less likely - switch (3 & (int) ps) { + switch (3 & (int)(size_t) ps) { case STB_ps_direct: if (ps == NULL) return (stb_ps *) value; return EncodeBucket(stb_bucket_create2(ps,value)); @@ -7772,11 +7848,11 @@ stb_ps * stb_ps_add (stb_ps *ps, void *value) stb_uint32 n = hash & h->mask; void **t = h->table; // find first NULL or STB_DEL entry - if (!stb_ps_empty(t[n])) { + if (!stb_ps_empty((size_t)t[n])) { stb_uint32 s = stb_rehash(hash) | 1; do { n = (n + s) & h->mask; - } while (!stb_ps_empty(t[n])); + } while (!stb_ps_empty((size_t)t[n])); } if (t[n] == STB_DEL) -- h->count_deletes; @@ -7803,9 +7879,9 @@ stb_ps *stb_ps_remove(stb_ps *ps, void *value) #ifdef STB_DEBUG assert(stb_ps_find(ps, value)); #endif - assert((3 & (int) value) == STB_ps_direct); + assert((3 & (int)(size_t) value) == STB_ps_direct); if (value == NULL) return ps; // ignore NULL removes to avoid bad breakage - switch (3 & (int) ps) { + switch (3 & (int)(size_t) ps) { case STB_ps_direct: return ps == value ? NULL : ps; case STB_ps_bucket: { @@ -7864,7 +7940,7 @@ stb_ps *stb_ps_remove(stb_ps *ps, void *value) stb_ps_array *a = (stb_ps_array *) malloc(sizeof(*a) + (n-1) * sizeof(a->p[0])); int i,j=0; for (i=0; i < h->size; ++i) - if (!stb_ps_empty(t[i])) + if (!stb_ps_empty((size_t)t[i])) a->p[j++] = t[i]; assert(j == h->count); a->count = j; @@ -7886,7 +7962,7 @@ stb_ps *stb_ps_remove(stb_ps *ps, void *value) stb_ps *stb_ps_remove_any(stb_ps *ps, void **value) { assert(ps != NULL); - switch (3 & (int) ps) { + switch (3 & (int)(size_t) ps) { case STB_ps_direct: *value = ps; return NULL; @@ -7919,7 +7995,7 @@ stb_ps *stb_ps_remove_any(stb_ps *ps, void **value) stb_ps_hash *h = GetHash(ps); void **t = h->table; stb_uint32 n = h->any_offset; - while (stb_ps_empty(t[n])) + while (stb_ps_empty((size_t)t[n])) n = (n + 1) & h->mask; *value = t[n]; h->any_offset = (n+1) & h->mask; @@ -7940,7 +8016,7 @@ void ** stb_ps_getlist(stb_ps *ps, int *count) { int i,n=0; void **p = NULL; - switch (3 & (int) ps) { + switch (3 & (int)(size_t) ps) { case STB_ps_direct: if (ps == NULL) { *count = 0; return NULL; } p = (void **) malloc(sizeof(*p) * 1); @@ -7966,7 +8042,7 @@ void ** stb_ps_getlist(stb_ps *ps, int *count) stb_ps_hash *h = GetHash(ps); p = (void **) malloc(sizeof(*p) * h->count); for (i=0; i < h->size; ++i) - if (!stb_ps_empty(h->table[i])) + if (!stb_ps_empty((size_t)h->table[i])) p[n++] = h->table[i]; break; } @@ -7978,7 +8054,7 @@ void ** stb_ps_getlist(stb_ps *ps, int *count) int stb_ps_writelist(stb_ps *ps, void **list, int size ) { int i,n=0; - switch (3 & (int) ps) { + switch (3 & (int)(size_t) ps) { case STB_ps_direct: if (ps == NULL || size <= 0) return 0; list[0] = ps; @@ -8000,7 +8076,7 @@ int stb_ps_writelist(stb_ps *ps, void **list, int size ) stb_ps_hash *h = GetHash(ps); if (size <= 0) return 0; for (i=0; i < h->count; ++i) { - if (!stb_ps_empty(h->table[i])) { + if (!stb_ps_empty((size_t)h->table[i])) { list[n++] = h->table[i]; if (n == size) break; } @@ -8014,7 +8090,7 @@ int stb_ps_writelist(stb_ps *ps, void **list, int size ) int stb_ps_enum(stb_ps *ps, void *data, int (*func)(void *value, void *data)) { int i; - switch (3 & (int) ps) { + switch (3 & (int)(size_t) ps) { case STB_ps_direct: if (ps == NULL) return STB_TRUE; return func(ps, data); @@ -8036,7 +8112,7 @@ int stb_ps_enum(stb_ps *ps, void *data, int (*func)(void *value, void *data)) case STB_ps_hash: { stb_ps_hash *h = GetHash(ps); for (i=0; i < h->count; ++i) - if (!stb_ps_empty(h->table[i])) + if (!stb_ps_empty((size_t)h->table[i])) if (!func(h->table[i], data)) return STB_FALSE; return STB_TRUE; @@ -8047,7 +8123,7 @@ int stb_ps_enum(stb_ps *ps, void *data, int (*func)(void *value, void *data)) int stb_ps_count (stb_ps *ps) { - switch (3 & (int) ps) { + switch (3 & (int)(size_t) ps) { case STB_ps_direct: return ps != NULL; case STB_ps_bucket: { @@ -8071,7 +8147,7 @@ void ** stb_ps_fastlist(stb_ps *ps, int *count) { static void *storage; - switch (3 & (int) ps) { + switch (3 & (int)(size_t) ps) { case STB_ps_direct: if (ps == NULL) { *count = 0; return NULL; } storage = ps; @@ -9118,7 +9194,7 @@ static void stb__add_epsilon(stb_matcher *matcher, int from, int to) static void stb__add_edge(stb_matcher *matcher, int from, int to, int type) { - stb_nfa_edge z = { type, to }; + stb_nfa_edge z = { (stb_int16)type, (stb_uint16)to }; if (matcher->nodes[from].out == NULL) stb_arr_malloc((void **) &matcher->nodes[from].out, matcher); stb_arr_push(matcher->nodes[from].out, z); @@ -9831,7 +9907,7 @@ int stb_regex(char *regex, char *str) static char ** regexps; static char ** regexp_cache; static unsigned short *mapping; - int z = stb_perfect_hash(&p, (int) regex); + int z = stb_perfect_hash(&p, (int)(size_t) regex); if (z >= 0) { if (strcmp(regex, regexp_cache[(int) mapping[z]])) { int i = mapping[z]; @@ -9862,8 +9938,8 @@ int stb_regex(char *regex, char *str) n = stb_perfect_create(&p, (unsigned int *) (char **) regexps, stb_arr_len(regexps)); mapping = (unsigned short *) realloc(mapping, n * sizeof(*mapping)); for (i=0; i < stb_arr_len(regexps); ++i) - mapping[stb_perfect_hash(&p, (int) regexps[i])] = i; - z = stb_perfect_hash(&p, (int) regex); + mapping[stb_perfect_hash(&p, (int)(size_t) regexps[i])] = i; + z = stb_perfect_hash(&p, (int)(size_t) regex); } return stb_matcher_find(matchers[(int) mapping[z]], str); } @@ -10357,7 +10433,7 @@ static void stb__write(unsigned char v) ++stb__outbytes; } -#define stb_out(v) (stb__out ? *stb__out++ = (stb_uchar) (v) : stb__write((stb_uchar) (v))) +#define stb_out(v) (stb__out ? (void)(*stb__out++ = (stb_uchar) (v)) : stb__write((stb_uchar) (v))) static void stb_out2(stb_uint v) { @@ -10620,7 +10696,8 @@ static size_t stb_out_backpatch_id(void) static void stb_out_backpatch(size_t id, stb_uint value) { - stb_uchar data[4] = { value >> 24, value >> 16, value >> 8, value }; + + stb_uchar data[4] = { (stb_uchar)(value >> 24), (stb_uchar)(value >> 16), (stb_uchar)(value >> 8), (stb_uchar)(value) }; if (stb__out) { memcpy((void *) id, data, 4); } else { diff --git a/stb_dxt.h b/stb_dxt.h index b53aca7..2fa84fa 100644 --- a/stb_dxt.h +++ b/stb_dxt.h @@ -1,4 +1,4 @@ -// stb_dxt.h - v1.07 - DXT1/DXT5 compressor - public domain +// stb_dxt.h - v1.08b - DXT1/DXT5 compressor - public domain // original by fabian "ryg" giesen - ported to C by stb // use '#define STB_DXT_IMPLEMENTATION' before including to create the implementation // @@ -9,7 +9,8 @@ // and "high quality" using mode. // // version history: -// v1.07 - bc4; allow not using libc; add STB_DXT_STATIC +// v1.08 - (sbt) fix bug in dxt-with-alpha block +// v1.07 - (stb) bc4; allow not using libc; add STB_DXT_STATIC // v1.06 - (stb) fix to known-broken 1.05 // v1.05 - (stb) support bc5/3dc (Arvids Kokins), use extern "C" in C++ (Pavel Krajcevski) // v1.04 - (ryg) default to no rounding bias for lerped colors (as per S3TC/DX10 spec); @@ -31,11 +32,6 @@ #ifndef STB_INCLUDE_STB_DXT_H #define STB_INCLUDE_STB_DXT_H -// compression mode (bitflags) -#define STB_DXT_NORMAL 0 -#define STB_DXT_DITHER 1 // use dithering. dubious win. never use for normal maps and the like! -#define STB_DXT_HIGHQUAL 2 // high quality mode, does two refinement steps instead of 1. ~30-40% slower. - #ifdef __cplusplus extern "C" { #endif @@ -46,15 +42,21 @@ extern "C" { #define STBDDEF extern #endif +// compression mode (bitflags) +#define STB_DXT_NORMAL 0 +#define STB_DXT_DITHER 1 // use dithering. dubious win. never use for normal maps and the like! +#define STB_DXT_HIGHQUAL 2 // high quality mode, does two refinement steps instead of 1. ~30-40% slower. + STBDDEF void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src_rgba_four_bytes_per_pixel, int alpha, int mode); STBDDEF void stb_compress_bc4_block(unsigned char *dest, const unsigned char *src_r_one_byte_per_pixel); STBDDEF void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src_rg_two_byte_per_pixel); +#define STB_COMPRESS_DXT_BLOCK + #ifdef __cplusplus } #endif - -#define STB_COMPRESS_DXT_BLOCK +#endif // STB_INCLUDE_STB_DXT_H #ifdef STB_DXT_IMPLEMENTATION @@ -88,7 +90,7 @@ STBDDEF void stb_compress_bc5_block(unsigned char *dest, const unsigned char *sr #ifndef STBD_MEMSET #include -#define STBD_MEMSET(x) memset(x) +#define STBD_MEMSET memset #endif static unsigned char stb__Expand5[32]; @@ -649,6 +651,7 @@ static void stb__InitDXT() void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode) { + unsigned char data[16][4]; static int init=1; if (init) { stb__InitDXT(); @@ -656,8 +659,15 @@ void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int a } if (alpha) { + int i; stb__CompressAlphaBlock(dest,(unsigned char*) src+3, 4); dest += 8; + // make a new copy of the data in which alpha is opaque, + // because code uses a fast test for color constancy + memcpy(data, src, 4*16); + for (i=0; i < 16; ++i) + data[i][3] = 255; + src = &data[0][0]; } stb__CompressColorBlock(dest,(unsigned char*) src,mode); @@ -674,7 +684,6 @@ void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src) stb__CompressAlphaBlock(dest + 8,(unsigned char*) src+1,2); } #endif // STB_DXT_IMPLEMENTATION -#endif // STB_INCLUDE_STB_DXT_H /* ------------------------------------------------------------------------------ diff --git a/stb_image.h b/stb_image.h index a056138..d9c21bc 100644 --- a/stb_image.h +++ b/stb_image.h @@ -1,5 +1,5 @@ -/* stb_image - v2.16 - public domain image loader - http://nothings.org/stb_image.h - no warranty implied; use at your own risk +/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk Do this: #define STB_IMAGE_IMPLEMENTATION @@ -48,6 +48,9 @@ LICENSE RECENT REVISION HISTORY: + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs @@ -74,11 +77,11 @@ RECENT REVISION HISTORY: Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) github:urraka (animated gif) Junggon Kim (PNM comments) - Daniel Gibson (16-bit TGA) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) socks-the-fox (16-bit PNG) Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes - Fabian "ryg" Giesen + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) Arseny Kapoulkine John-Mark Allen @@ -87,16 +90,17 @@ RECENT REVISION HISTORY: Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan Dave Moore Roy Eltham Hayaki Saito Nathan Reed Won Chun Luke Graham Johan Duparc Nick Verigakis - the Horde3D community Thomas Ruf Ronny Chevalier Baldur Karlsson - Janez Zemva John Bartholomew Michal Cichon github:rlyeh - Jonathan Blow Ken Hamada Tero Hanninen github:romigrou - Laurent Gomila Cort Stratton Sergio Gonzalez github:svdijk - Aruelien Pocheville Thibault Reuille Cass Everitt github:snagar - Ryamond Barbiero Paul Du Bois Engin Manap github:Zelex - Michaelangel007@github Philipp Wiesemann Dale Weiler github:grim210 - Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:sammyhw - Blazej Dariusz Roszkowski Gregory Mullen github:phprus - Christian Floisand Kevin Schmidt github:poppolopoppo + the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar + Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex + Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 + Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw + Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus + Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo + Christian Floisand Kevin Schmidt github:darealshinji + Blazej Dariusz Roszkowski github:Michaelangel007 */ #ifndef STBI_INCLUDE_STB_IMAGE_H @@ -105,10 +109,8 @@ RECENT REVISION HISTORY: // DOCUMENTATION // // Limitations: -// - no 16-bit-per-channel PNG // - no 12-bit-per-channel JPEG // - no JPEGs with arithmetic coding -// - no 1-bit BMP // - GIF always returns *comp=4 // // Basic usage (see HDR discussion below for HDR usage): @@ -353,6 +355,10 @@ typedef struct STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + #ifndef STBI_NO_STDIO STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); @@ -416,11 +422,14 @@ STBIDEF void stbi_image_free (void *retval_from_stbi_load); // get image dimensions & components without fully decoding STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); #ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); - +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); #endif @@ -504,7 +513,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #include #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp +#include // ldexp, pow #endif #ifndef STBI_NO_STDIO @@ -784,6 +793,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__png_test(stbi__context *s); static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); #endif #ifndef STBI_NO_BMP @@ -802,6 +812,7 @@ static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__psd_test(stbi__context *s); static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); #endif #ifndef STBI_NO_HDR @@ -819,6 +830,7 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); #ifndef STBI_NO_GIF static int stbi__gif_test(stbi__context *s); static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif @@ -893,11 +905,13 @@ static int stbi__mad3sizes_valid(int a, int b, int c, int add) } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); } +#endif // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) @@ -912,11 +926,13 @@ static void *stbi__malloc_mad3(int a, int b, int c, int add) return stbi__malloc(a*b*c + add); } +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; return stbi__malloc(a*b*c*d + add); } +#endif // stbi__err - error // stbi__errpf - error returning pointer to float @@ -1054,6 +1070,18 @@ static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) } } +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} + static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__result_info ri; @@ -1103,7 +1131,7 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, return (stbi__uint16 *) result; } -#ifndef STBI_NO_HDR +#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { @@ -1205,6 +1233,22 @@ STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *u return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + #ifndef STBI_NO_LINEAR static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) { @@ -1288,12 +1332,16 @@ STBIDEF int stbi_is_hdr (char const *filename) return result; } -STBIDEF int stbi_is_hdr_from_file(FILE *f) +STBIDEF int stbi_is_hdr_from_file(FILE *f) { #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; stbi__context s; stbi__start_file(&s,f); - return stbi__hdr_test(&s); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; #else STBI_NOTUSED(f); return 0; @@ -1705,7 +1753,8 @@ typedef struct static int stbi__build_huffman(stbi__huffman *h, int *count) { - int i,j,k=0,code; + int i,j,k=0; + unsigned int code; // build size list for each symbol (from JPEG spec) for (i=0; i < 16; ++i) for (j=0; j < count[i]; ++j) @@ -1721,7 +1770,7 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) if (h->size[k] == j) { while (h->size[k] == j) h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); } // compute largest code + 1 for this size, preshifted as needed later h->maxcode[j] = code << (16-j); @@ -1765,7 +1814,7 @@ static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) if (k < m) k += (~0U << magbits) + 1; // if the result is small enough, we can fit it in fast_ac table if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); } } } @@ -1774,7 +1823,7 @@ static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { do { - int b = j->nomore ? 0 : stbi__get8(j->s); + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); if (b == 0xff) { int c = stbi__get8(j->s); while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes @@ -1790,7 +1839,7 @@ static void stbi__grow_buffer_unsafe(stbi__jpeg *j) } // (1 << n) - 1 -static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; // decode a jpeg huffman value from the bitstream stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) @@ -1843,7 +1892,7 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) } // bias[n] = (-1<rgb = 0; for (i=0; i < s->img_n; ++i) { - static unsigned char rgb[3] = { 'R', 'G', 'B' }; + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; z->img_comp[i].id = stbi__get8(s); if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) ++z->rgb; @@ -3093,8 +3142,8 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) stbi__err("bad DNL height", "Corrupt JPEG"); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); } else { if (!stbi__process_marker(j, m)) return 0; } @@ -3912,18 +3961,18 @@ static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room return 1; } -static int stbi__zlength_base[31] = { +static const int stbi__zlength_base[31] = { 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 }; -static int stbi__zlength_extra[31]= +static const int stbi__zlength_extra[31]= { 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 }; -static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +static const int stbi__zdist_base[32] = { 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}; -static int stbi__zdist_extra[32] = +static const int stbi__zdist_extra[32] = { 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}; static int stbi__parse_huffman_block(stbi__zbuf *a) @@ -3970,7 +4019,7 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) static int stbi__compute_huffman_codes(stbi__zbuf *a) { - static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; stbi__zhuffman z_codelength; stbi_uc lencodes[286+32+137];//padding for maximum single op stbi_uc codelength_sizes[19]; @@ -4229,7 +4278,7 @@ static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) static int stbi__check_png_header(stbi__context *s) { - static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; int i; for (i=0; i < 8; ++i) if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); @@ -4275,7 +4324,7 @@ static int stbi__paeth(int a, int b, int c) return c; } -static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) @@ -4295,8 +4344,10 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); img_len = (img_width_bytes + 1) * y; + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), // so just check for raw_len < img_len always. @@ -4675,7 +4726,7 @@ static void stbi__de_iphone(stbi__png *z) } } -#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { @@ -4912,6 +4963,19 @@ static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) p.s = s; return stbi__png_info_raw(&p, x, y, comp); } + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} #endif // Microsoft/Windows BMP image @@ -4963,21 +5027,27 @@ static int stbi__bitcount(unsigned int a) return a & 0xff; } +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. static int stbi__shiftsigned(int v, int shift, int bits) { - int result; - int z=0; - - if (shift < 0) v <<= -shift; - else v >>= shift; - result = v; - - z = bits; - while (z < 8) { - result += v >> z; - z += bits; - } - return result; + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v >= 0 && v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; } typedef struct @@ -5007,7 +5077,6 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) } if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); info->bpp = stbi__get16le(s); - if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); if (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); @@ -5125,29 +5194,47 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req pal[i][3] = 255; } stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 4) width = (s->img_x + 1) >> 1; + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.bpp == 8) width = s->img_x; else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } pad = (-width)&3; - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); } - stbi__skip(s, pad); } } else { int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; @@ -5188,7 +5275,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req int bpp = info.bpp; for (i=0; i < (int) s->img_x; ++i) { stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - int a; + unsigned int a; out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); @@ -5236,14 +5323,14 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) { // only RGB or RGBA (incl. 16bit) or grey allowed - if(is_rgb16) *is_rgb16 = 0; + if (is_rgb16) *is_rgb16 = 0; switch(bits_per_pixel) { case 8: return STBI_grey; case 16: if(is_grey) return STBI_grey_alpha; - // else: fall-through + // fallthrough case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fall-through + return STBI_rgb; + case 24: // fallthrough case 32: return bits_per_pixel/8; default: return 0; } @@ -6038,11 +6125,13 @@ typedef struct typedef struct { int w,h; - stbi_uc *out, *old_out; // output buffer (always 4 components) - int flags, bgindex, ratio, transparent, eflags, delay; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; - stbi__gif_lzw codes[4096]; + stbi__gif_lzw codes[8192]; stbi_uc *color_table; int parse, step; int lflags; @@ -6050,6 +6139,7 @@ typedef struct int max_x, max_y; int cur_x, cur_y; int line_size; + int delay; } stbi__gif; static int stbi__gif_test_raw(stbi__context *s) @@ -6125,6 +6215,7 @@ static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; + int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty @@ -6133,10 +6224,12 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) if (g->cur_y >= g->max_y) return; - p = &g->out[g->cur_x + g->cur_y]; - c = &g->color_table[g->codes[code].suffix * 4]; + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; - if (c[3] >= 128) { + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; @@ -6210,11 +6303,16 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) stbi__skip(s,len); return g->out; } else if (code <= avail) { - if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } if (oldcode >= 0) { p = &g->codes[avail++]; - if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + p->prefix = (stbi__int16) oldcode; p->first = g->codes[oldcode].first; p->suffix = (code == avail) ? p->first : g->codes[code].first; @@ -6236,62 +6334,72 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) } } -static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) -{ - int x, y; - stbi_uc *c = g->pal[g->bgindex]; - for (y = y0; y < y1; y += 4 * g->w) { - for (x = x0; x < x1; x += 4) { - stbi_uc *p = &g->out[y + x]; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = 0; - } - } -} - // this function is designed to support animated gifs, although stb_image doesn't support it -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { - int i; - stbi_uc *prev_out = 0; + int dispose; + int first_frame; + int pi; + int pcount; - if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) - return 0; // stbi__g_failure_reason set by stbi__gif_header + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + g->history = (stbi_uc *) stbi__malloc(g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - if (!stbi__mad3sizes_valid(g->w, g->h, 4, 0)) - return stbi__errpuc("too large", "GIF too large"); + // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to teh color that was there the previous frame. + memset( g->out, 0x00, 4 * g->w * g->h ); + memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispoase of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; - prev_out = g->out; - g->out = (stbi_uc *) stbi__malloc_mad3(4, g->w, g->h, 0); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } - switch ((g->eflags & 0x1C) >> 2) { - case 0: // unspecified (also always used on 1st frame) - stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); - break; - case 1: // do not dispose - if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); - g->old_out = prev_out; - break; - case 2: // dispose to background - if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); - stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); - break; - case 3: // dispose to previous - if (g->old_out) { - for (i = g->start_y; i < g->max_y; i += 4 * g->w) - memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } } - break; + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); } + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + for (;;) { - switch (stbi__get8(s)) { + int tag = stbi__get8(s); + switch (tag) { case 0x2C: /* Image Descriptor */ { - int prev_trans = -1; stbi__int32 x, y, w, h; stbi_uc *o; @@ -6324,19 +6432,24 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); g->color_table = (stbi_uc *) g->lpal; } else if (g->flags & 0x80) { - if (g->transparent >= 0 && (g->eflags & 0x01)) { - prev_trans = g->pal[g->transparent][3]; - g->pal[g->transparent][3] = 0; - } g->color_table = (stbi_uc *) g->pal; } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - + return stbi__errpuc("missing color table", "Corrupt GIF"); + o = stbi__process_gif_raster(s, g); if (o == NULL) return NULL; - if (prev_trans != -1) - g->pal[g->transparent][3] = (stbi_uc) prev_trans; + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } return o; } @@ -6344,19 +6457,35 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i case 0x21: // Comment Extension. { int len; - if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { g->eflags = stbi__get8(s); - g->delay = stbi__get16le(s); - g->transparent = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } } else { stbi__skip(s, len); break; } - } - while ((len = stbi__get8(s)) != 0) + } + while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); + } break; } @@ -6367,28 +6496,92 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i return stbi__errpuc("unknown code", "Corrupt GIF"); } } +} - STBI_NOTUSED(req_comp); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + if (delays) { + *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } } static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *u = 0; - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - memset(g, 0, sizeof(*g)); - STBI_NOTUSED(ri); + stbi__gif g; + memset(&g, 0, sizeof(g)); - u = stbi__gif_load_next(s, g, comp, req_comp); + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { - *x = g->w; - *y = g->h; + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g->w, g->h); + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); } - else if (g->out) - STBI_FREE(g->out); - STBI_FREE(g); + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + return u; } @@ -6667,7 +6860,7 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_PSD static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { - int channelCount, dummy; + int channelCount, dummy, depth; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; @@ -6687,7 +6880,8 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) } *y = stbi__get32be(s); *x = stbi__get32be(s); - if (stbi__get16be(s) != 8) { + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { stbi__rewind( s ); return 0; } @@ -6698,6 +6892,33 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) *comp = 4; return 1; } + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + (void) stbi__get32be(s); + (void) stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} #endif #ifndef STBI_NO_PIC @@ -6928,6 +7149,19 @@ static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) return stbi__err("unknown image type", "Image not of any known type, or corrupt"); } +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + return 0; +} + #ifndef STBI_NO_STDIO STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) { @@ -6949,6 +7183,27 @@ STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) fseek(f,pos,SEEK_SET); return r; } + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} #endif // !STBI_NO_STDIO STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) @@ -6965,10 +7220,30 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int return stbi__info_main(&s,x,y,comp); } +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + #endif // STB_IMAGE_IMPLEMENTATION /* revision history: + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings 2.16 (2017-07-23) all functions have 16-bit variants; STBI_NO_STDIO works again; compilation fixes; diff --git a/stb_image_write.h b/stb_image_write.h index 9d553e0..c05e958 100644 --- a/stb_image_write.h +++ b/stb_image_write.h @@ -1,4 +1,4 @@ -/* stb_image_write - v1.07 - public domain - http://nothings.org/stb/stb_image_write.h +/* stb_image_write - v1.09 - public domain - http://nothings.org/stb/stb_image_write.h writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk @@ -10,34 +10,47 @@ Will probably not work correctly with strict-aliasing optimizations. + If using a modern Microsoft Compiler, non-safe versions of CRT calls may cause + compilation warnings or even errors. To avoid this, also before #including, + + #define STBI_MSC_SECURE_CRT + ABOUT: This header file is a library for writing images to C stdio. It could be adapted to write to memory or a general streaming interface; let me know. The PNG output is not optimal; it is 20-50% larger than the file - written by a decent optimizing implementation. This library is designed - for source code compactness and simplicity, not optimal image file size - or run-time performance. + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. BUILDING: You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace malloc,realloc,free. - You can define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), USAGE: - There are four functions, one for each image file format: + There are five functions, one for each image file format: int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); - int stbi_write_jpg(char const *filename, int w, int h, int comp, const float *data); - There are also four equivalent functions that use an arbitrary write function. You are + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are expected to open/close your file-equivalent before and after calling these: int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); @@ -49,6 +62,12 @@ USAGE: where the callback is: void stbi_write_func(void *context, void *data, int size); + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these functions, so the library will not use stdio.h at all. However, this will also disable HDR writing, because it requires stdio for formatted output. @@ -75,6 +94,9 @@ USAGE: writer, both because it is in BGR order and because it may have padding at the end of the line.) + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + HDR expects linear float data. Since the format is always 32-bit rgb(e) data, alpha (if provided) is discarded, and for monochrome data it is replicated across all three channels. @@ -88,21 +110,17 @@ USAGE: CREDITS: - PNG/BMP/TGA - Sean Barrett - HDR - Baldur Karlsson - TGA monochrome: - Jean-Sebastien Guay - misc enhancements: - Tim Kelsey - TGA RLE - Alan Hickman - initial file IO callback implementation - Emmanuel Julien - JPEG - Jon Olick (original jo_jpeg.cpp code) - Daniel Gibson + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + bugfixes: github:Chribba Guillaume Chereau @@ -114,7 +132,13 @@ CREDITS: Thatcher Ulrich github:poppolopoppo Patrick Boettcher - + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + LICENSE See end of file for license information. @@ -124,15 +148,23 @@ LICENSE #ifndef INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else #ifdef __cplusplus -extern "C" { +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif #endif -#ifdef STB_IMAGE_WRITE_STATIC -#define STBIWDEF static -#else -#define STBIWDEF extern +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations extern int stbi_write_tga_with_rle; +extern int stbi_write_png_compression_level; +extern int stbi_write_force_png_filter; #endif #ifndef STBI_WRITE_NO_STDIO @@ -151,9 +183,7 @@ STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); -#ifdef __cplusplus -} -#endif +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); #endif//INCLUDE_STB_IMAGE_WRITE_H @@ -208,6 +238,23 @@ STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi__flip_vertically_on_write=0; +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi__flip_vertically_on_write=0; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + typedef struct { stbi_write_func *func; @@ -230,7 +277,13 @@ static void stbi__stdio_write(void *context, void *data, int size) static int stbi__start_write_file(stbi__write_context *s, const char *filename) { - FILE *f = fopen(filename, "wb"); + FILE *f; +#ifdef STBI_MSC_SECURE_CRT + if (fopen_s(&f, filename, "wb")) + f = NULL; +#else + f = fopen(filename, "wb"); +#endif stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); return f != NULL; } @@ -245,12 +298,6 @@ static void stbi__end_write_file(stbi__write_context *s) typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; -#ifdef STB_IMAGE_WRITE_STATIC -static int stbi_write_tga_with_rle = 1; -#else -int stbi_write_tga_with_rle = 1; -#endif - static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { while (*fmt) { @@ -341,6 +388,9 @@ static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, i if (y <= 0) return; + if (stbi__flip_vertically_on_write) + vdir *= -1; + if (vdir < 0) j_end = -1, j = y-1; else @@ -412,11 +462,21 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, v "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); } else { int i,j,k; + int jend, jdir; stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); - for (j = y - 1; j >= 0; --j) { - unsigned char *row = (unsigned char *) data + j * x * comp; + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; int len; for (i = 0; i < x; i += len) { @@ -626,11 +686,15 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; s->func(s->context, header, sizeof(header)-1); +#ifdef STBI_MSC_SECURE_CRT + len = sprintf_s(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif s->func(s->context, buffer, len); for(i=0; i < y; i++) - stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)*x); STBIW_FREE(scratch); return 1; } @@ -662,6 +726,7 @@ STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const // PNG writer // +#ifndef STBIW_ZLIB_COMPRESS // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() #define stbiw__sbraw(a) ((int *) (a) - 2) #define stbiw__sbm(a) stbiw__sbraw(a)[0] @@ -742,8 +807,14 @@ static unsigned int stbiw__zhash(unsigned char *data) #define stbiw__ZHASH 16384 +#endif // STBIW_ZLIB_COMPRESS + unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin static unsigned short lengthc[] = { 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, 259 }; static unsigned char lengtheb[]= { 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 }; static unsigned short distc[] = { 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, 32768 }; @@ -752,6 +823,8 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l int i,j, bitcount=0; unsigned char *out = NULL; unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + if (hash_table == NULL) + return NULL; if (quality < 5) quality = 5; stbiw__sbpush(out, 0x78); // DEFLATE 32K window @@ -845,6 +918,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l // make returned pointer freeable STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS } static unsigned int stbiw__crc32(unsigned char *buffer, int len) @@ -911,61 +985,88 @@ static unsigned char stbiw__paeth(int a, int b, int c) } // @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + for (i = 0; i < n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + for (i=n; i < width*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } +} + unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) { + int force_filter = stbi_write_force_png_filter; int ctype[5] = { -1, 0, 4, 2, 6 }; unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; unsigned char *out,*o, *filt, *zlib; signed char *line_buffer; - int i,j,k,p,zlen; + int j,zlen; if (stride_bytes == 0) stride_bytes = x * n; + if (force_filter >= 5) { + force_filter = -1; + } + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } for (j=0; j < y; ++j) { - static int mapping[] = { 0,1,2,3,4 }; - static int firstmap[] = { 0,1,0,5,6 }; - int *mymap = (j != 0) ? mapping : firstmap; - int best = 0, bestval = 0x7fffffff; - for (p=0; p < 2; ++p) { - for (k= p?best:0; k < 5; ++k) { // @TODO: clarity: rewrite this to go 0..5, and 'continue' the unwanted ones during 2nd pass - int type = mymap[k],est=0; - unsigned char *z = pixels + stride_bytes*j; - for (i=0; i < n; ++i) - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i]; break; - case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; - case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; - case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; - case 5: line_buffer[i] = z[i]; break; - case 6: line_buffer[i] = z[i]; break; - } - for (i=n; i < x*n; ++i) { - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i] - z[i-n]; break; - case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; - case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; - case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; - case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; - case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; - } - } - if (p) break; - for (i=0; i < x*n; ++i) + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { est += abs((signed char) line_buffer[i]); - if (est < bestval) { bestval = est; best = k; } + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; } } - // when we get here, best contains the filter type, and line_buffer contains the data - filt[j*(x*n+1)] = (unsigned char) best; + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); } STBIW_FREE(line_buffer); - zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); STBIW_FREE(filt); if (!zlib) return 0; @@ -1010,7 +1111,12 @@ STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const int len; unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; +#ifdef STBI_MSC_SECURE_CRT + if (fopen_s(&f, filename, "wb")) + f = NULL; +#else f = fopen(filename, "wb"); +#endif if (!f) { STBIW_FREE(png); return 0; } fwrite(png, 1, len, f); fclose(f); @@ -1318,7 +1424,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, in float YDU[64], UDU[64], VDU[64]; for(row = y, pos = 0; row < y+8; ++row) { for(col = x; col < x+8; ++col, ++pos) { - int p = row*width*comp + col*comp; + int p = (stbi__flip_vertically_on_write ? height-1-row : row)*width*comp + col*comp; float r, g, b; if(row >= height) { p -= width*comp*(row+1 - height); @@ -1377,6 +1483,10 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter 1.07 (2017-07-24) doc fix 1.06 (2017-07-23) diff --git a/stb_sprintf.h b/stb_sprintf.h index 3828445..0ad26b2 100644 --- a/stb_sprintf.h +++ b/stb_sprintf.h @@ -1,4 +1,4 @@ -// stb_sprintf - v1.03 - public domain snprintf() implementation +// stb_sprintf - v1.05 - public domain snprintf() implementation // originally by Jeff Roberts / RAD Game Tools, 2015/10/20 // http://github.com/nothings/stb // @@ -12,6 +12,9 @@ // github:d26435 // github:trex78 // Jari Komppa (SI suffixes) +// Rohit Nirmal +// Marcin Wojdyr +// Leonard Ritter // // LICENSE: // @@ -927,7 +930,7 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; pr = sizeof(void *) * 2; fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros - // drop through to X + // fall through - to X case 'X': // upper hex case 'x': // lower hex @@ -1025,11 +1028,11 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, n64 = 0; } if ((fl & STBSP__TRIPLET_COMMA) == 0) { - while (n) { + do { s -= 2; *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair[(n % 100) * 2]; n /= 100; - } + } while (n); } while (n) { if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { @@ -1259,7 +1262,7 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, s = num + STBSP__NUMSZ - 1; *s = f[0]; l = 1; - fw = pr = fl = 0; + fw = fl = 0; lead[0] = 0; tail[0] = 0; pr = 0; @@ -1341,24 +1344,42 @@ static char *stbsp__clamp_callback(char *buf, void *user, int len) return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can } -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va) +static char * stbsp__count_clamp_callback( char * buf, void * user, int len ) +{ + stbsp__context * c = (stbsp__context*)user; + + c->count += len; + return c->tmp; // go direct into buffer if you can +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) { stbsp__context c; int l; - if (count == 0) - return 0; + if ( (count == 0) && !buf ) + { + c.count = 0; - c.buf = buf; - c.count = count; + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); + l = c.count; + } + else + { + if ( count == 0 ) + return 0; - STB_SPRINTF_DECORATE(vsprintfcb)(stbsp__clamp_callback, &c, stbsp__clamp_callback(0, &c, 0), fmt, va); + c.buf = buf; + c.count = count; - // zero-terminate - l = (int)(c.buf - buf); - if (l >= count) // should never be greater, only equal (or less) than count - l = count - 1; - buf[l] = 0; + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); + + // zero-terminate + l = (int)( c.buf - buf ); + if ( l >= count ) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } return l; } diff --git a/stb_textedit.h b/stb_textedit.h index 2fb1179..91a52da 100644 --- a/stb_textedit.h +++ b/stb_textedit.h @@ -1,4 +1,4 @@ -// stb_textedit.h - v1.11 - public domain - Sean Barrett +// stb_textedit.h - v1.12 - public domain - Sean Barrett // Development of this library was sponsored by RAD Game Tools // // This C header file implements the guts of a multi-line text-editing @@ -29,6 +29,7 @@ // // VERSION HISTORY // +// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash // 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield // 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual // 1.9 (2016-08-27) customizable move-by-word @@ -198,7 +199,7 @@ // void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) // int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) // int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) -// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) +// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key) // // Each of these functions potentially updates the string and updates the // state. @@ -232,7 +233,9 @@ // inputs, set a high bit to distinguish the two; then you can define the // various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit // set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is -// clear. +// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to +// anything other type you wante before including. +// // // When rendering, you can read the cursor position and selection state from // the STB_TexteditState. @@ -711,8 +714,12 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta return 0; } +#ifndef STB_TEXTEDIT_KEYTYPE +#define STB_TEXTEDIT_KEYTYPE int +#endif + // API key: process a keyboard input -static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) +static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) { retry: switch (key) { @@ -1112,15 +1119,18 @@ static void stb_textedit_discard_redo(StbUndoState *state) // if the k'th undo state has characters, clean those up if (state->undo_rec[k].char_storage >= 0) { int n = state->undo_rec[k].insert_length, i; - // delete n characters from all other records + // move the remaining redo character data to the end of the buffer state->redo_char_point = state->redo_char_point + (short) n; // vsnet05 - STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); + STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); + // adjust the position of all the other records to account for above memmove for (i=state->redo_point; i < k; ++i) if (state->undo_rec[i].char_storage >= 0) - state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short) n; // vsnet05 + state->undo_rec[i].char_storage += (short) n; // vsnet05 } + // now move all the redo records towards the end of the buffer; the first one is at 'redo_point' + STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); + // now move redo_point to point to the new one ++state->redo_point; - STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point-1, state->undo_rec + state->redo_point, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); } } @@ -1203,11 +1213,11 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) // there's definitely room to store the characters eventually while (s->undo_char_point + u.delete_length > s->redo_char_point) { - // there's currently not enough room, so discard a redo record - stb_textedit_discard_redo(s); // should never happen: if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) return; + // there's currently not enough room, so discard a redo record + stb_textedit_discard_redo(s); } r = &s->undo_rec[s->redo_point-1]; @@ -1278,6 +1288,7 @@ static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) if (r.insert_length) { // easy case: need to insert n characters STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); + s->redo_char_point += r.insert_length; } state->cursor = r.where + r.insert_length; diff --git a/stb_tilemap_editor.h b/stb_tilemap_editor.h index 94cff66..352042b 100644 --- a/stb_tilemap_editor.h +++ b/stb_tilemap_editor.h @@ -1847,7 +1847,7 @@ static int stbte__minibutton(int colormode, int x, int y, int ch, int id) int x0 = x, y0 = y, x1 = x+8, y1 = y+7; int over = stbte__hittest(x0,y0,x1,y1,id); if (stbte__ui.event == STBTE__paint) { - char str[2] = { ch,0 }; + char str[2] = { (char)ch, 0 }; stbte__draw_textbox(x0,y0,x1,y1, str,1,0,colormode, STBTE__INDEX_FOR_ID(id,0,0)); } return stbte__button_core(id); @@ -1858,7 +1858,7 @@ static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int dis int x0 = x, y0 = y, x1 = x+10, y1 = y+11; int over = !disabled && stbte__hittest(x0,y0,x1,y1,id); if (stbte__ui.event == STBTE__paint) { - char str[2] = { ch,0 }; + char str[2] = { (char)ch, 0 }; int off = (9-stbte__get_char_width(ch))/2; stbte__draw_textbox(x0,y0,x1,y1, str, off+1,2, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); } diff --git a/stb_truetype.h b/stb_truetype.h index cec2425..a08e929 100644 --- a/stb_truetype.h +++ b/stb_truetype.h @@ -1,4 +1,4 @@ -// stb_truetype.h - v1.17 - public domain +// stb_truetype.h - v1.19 - public domain // authored from 2009-2016 by Sean Barrett / RAD Game Tools // // This library processes TrueType files: @@ -22,41 +22,35 @@ // Mikko Mononen: compound shape support, more cmap formats // Tor Andersson: kerning, subpixel rendering // Dougall Johnson: OpenType / Type 2 font handling +// Daniel Ribeiro Maciel: basic GPOS-based kerning // // Misc other: // Ryan Gordon // Simon Glass // github:IntellectualKitty // Imanol Celaya +// Daniel Ribeiro Maciel // // Bug/warning reports/fixes: -// "Zer" on mollyrocket -// Cass Everitt -// stoiko (Haemimont Games) -// Brian Hook -// Walter van Niftrik -// David Gow -// David Given -// Ivan-Assen Ivanov -// Anthony Pesch -// Johan Duparc -// Hou Qiming -// Fabian "ryg" Giesen -// Martins Mozeiko -// Cap Petschulat -// Omar Cornut -// github:aloucks -// Peter LaValle -// Sergey Popov -// Giumo X. Clanjor -// Higor Euripedes -// Thomas Fields -// Derek Vinyard -// Cort Stratton -// github:oyvindjam -// +// "Zer" on mollyrocket Fabian "ryg" Giesen +// Cass Everitt Martins Mozeiko +// stoiko (Haemimont Games) Cap Petschulat +// Brian Hook Omar Cornut +// Walter van Niftrik github:aloucks +// David Gow Peter LaValle +// David Given Sergey Popov +// Ivan-Assen Ivanov Giumo X. Clanjor +// Anthony Pesch Higor Euripedes +// Johan Duparc Thomas Fields +// Hou Qiming Derek Vinyard +// Rob Loach Cort Stratton +// Kenney Phillis Jr. github:oyvindjam +// Brian Costabile github:vassvik +// // VERSION HISTORY // +// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod +// 1.18 (2018-01-29) add missing function // 1.17 (2017-07-23) make more arguments const; doc fix // 1.16 (2017-07-12) SDF support // 1.15 (2017-03-03) make more arguments const @@ -171,7 +165,7 @@ // measurement for describing font size, defined as 72 points per inch. // stb_truetype provides a point API for compatibility. However, true // "per inch" conventions don't make much sense on computer displays -// since they different monitors have different number of pixels per +// since different monitors have different number of pixels per // inch. For example, Windows traditionally uses a convention that // there are 96 pixels per inch, thus making 'inch' measurements have // nothing to do with inches, and thus effectively defining a point to @@ -181,6 +175,39 @@ // for non-commercial fonts, thus making fonts scaled in points // according to the TrueType spec incoherently sized in practice. // +// DETAILED USAGE: +// +// Scale: +// Select how high you want the font to be, in points or pixels. +// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute +// a scale factor SF that will be used by all other functions. +// +// Baseline: +// You need to select a y-coordinate that is the baseline of where +// your text will appear. Call GetFontBoundingBox to get the baseline-relative +// bounding box for all characters. SF*-y0 will be the distance in pixels +// that the worst-case character could extend above the baseline, so if +// you want the top edge of characters to appear at the top of the +// screen where y=0, then you would set the baseline to SF*-y0. +// +// Current point: +// Set the current point where the first character will appear. The +// first character could extend left of the current point; this is font +// dependent. You can either choose a current point that is the leftmost +// point and hope, or add some padding, or check the bounding box or +// left-side-bearing of the first character to be displayed and set +// the current point based on that. +// +// Displaying a character: +// Compute the bounding box of the character. It will contain signed values +// relative to . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to #define STBTT_ifloor(x) ((int) floor(x)) @@ -416,6 +444,11 @@ int main(int arg, char **argv) #define STBTT_pow(x,y) pow(x,y) #endif + #ifndef STBTT_fmod + #include + #define STBTT_fmod(x,y) fmod(x,y) + #endif + #ifndef STBTT_cos #include #define STBTT_cos(x) cos(x) @@ -427,11 +460,6 @@ int main(int arg, char **argv) #define STBTT_fabs(x) fabs(x) #endif - #ifndef STBTT_fabs - #include - #define STBTT_fabs(x) fabs(x) - #endif - // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h #ifndef STBTT_malloc #include @@ -676,7 +704,7 @@ struct stbtt_fontinfo int numGlyphs; // number of glyphs, needed for range checking - int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf + int loca,head,glyf,hhea,hmtx,kern,gpos; // table locations as offset from start of .ttf int index_map; // a cmap mapping for our chosen character encoding int indexToLocFormat; // format needed to map from glyph index to glyph @@ -1319,6 +1347,7 @@ static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, in info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required if (!cmap || !info->head || !info->hhea || !info->hmtx) return 0; @@ -2172,7 +2201,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st // push immediate if (b0 == 255) { - f = (float)stbtt__buf_get32(&b) / 0x10000; + f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; } else { stbtt__buf_skip(&b, -1); f = (float)(stbtt_int16)stbtt__cff_int(&b); @@ -2210,12 +2239,10 @@ static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, in { stbtt__csctx c = STBTT__CSCTX_INIT(1); int r = stbtt__run_charstring(info, glyph_index, &c); - if (x0) { - *x0 = r ? c.min_x : 0; - *y0 = r ? c.min_y : 0; - *x1 = r ? c.max_x : 0; - *y1 = r ? c.max_y : 0; - } + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; return r ? c.num_vertices : 0; } @@ -2239,7 +2266,7 @@ STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_inde } } -STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) { stbtt_uint8 *data = info->data + info->kern; stbtt_uint32 needle, straw; @@ -2269,9 +2296,260 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, return 0; } +static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) +{ + stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); + switch(coverageFormat) { + case 1: { + stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); + + // Binary search. + stbtt_int32 l=0, r=glyphCount-1, m; + int straw, needle=glyph; + while (l <= r) { + stbtt_uint8 *glyphArray = coverageTable + 4; + stbtt_uint16 glyphID; + m = (l + r) >> 1; + glyphID = ttUSHORT(glyphArray + 2 * m); + straw = glyphID; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + return m; + } + } + } break; + + case 2: { + stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); + stbtt_uint8 *rangeArray = coverageTable + 4; + + // Binary search. + stbtt_int32 l=0, r=rangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *rangeRecord; + m = (l + r) >> 1; + rangeRecord = rangeArray + 6 * m; + strawStart = ttUSHORT(rangeRecord); + strawEnd = ttUSHORT(rangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else { + stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); + return startCoverageIndex + glyph - strawStart; + } + } + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + } break; + } + + return -1; +} + +static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) +{ + stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); + switch(classDefFormat) + { + case 1: { + stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); + stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); + stbtt_uint8 *classDef1ValueArray = classDefTable + 6; + + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); + + classDefTable = classDef1ValueArray + 2 * glyphCount; + } break; + + case 2: { + stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); + stbtt_uint8 *classRangeRecords = classDefTable + 4; + + // Binary search. + stbtt_int32 l=0, r=classRangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *classRangeRecord; + m = (l + r) >> 1; + classRangeRecord = classRangeRecords + 6 * m; + strawStart = ttUSHORT(classRangeRecord); + strawEnd = ttUSHORT(classRangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else + return (stbtt_int32)ttUSHORT(classRangeRecord + 4); + } + + classDefTable = classRangeRecords + 6 * classRangeCount; + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + } break; + } + + return -1; +} + +// Define to STBTT_assert(x) if you want to break on unimplemented formats. +#define STBTT_GPOS_TODO_assert(x) + +static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint16 lookupListOffset; + stbtt_uint8 *lookupList; + stbtt_uint16 lookupCount; + stbtt_uint8 *data; + stbtt_int32 i; + + if (!info->gpos) return 0; + + data = info->data + info->gpos; + + if (ttUSHORT(data+0) != 1) return 0; // Major version 1 + if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 + + lookupListOffset = ttUSHORT(data+8); + lookupList = data + lookupListOffset; + lookupCount = ttUSHORT(lookupList); + + for (i=0; i> 1; + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = ttUSHORT(pairValue); + straw = secondGlyph; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + stbtt_int16 xAdvance = ttSHORT(pairValue + 2); + return xAdvance; + } + } + } break; + + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + + stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); + stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); + int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); + int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); + + stbtt_uint16 class1Count = ttUSHORT(table + 12); + stbtt_uint16 class2Count = ttUSHORT(table + 14); + STBTT_assert(glyph1class < class1Count); + STBTT_assert(glyph2class < class2Count); + + // TODO: Support more formats. + STBTT_GPOS_TODO_assert(valueFormat1 == 4); + if (valueFormat1 != 4) return 0; + STBTT_GPOS_TODO_assert(valueFormat2 == 0); + if (valueFormat2 != 0) return 0; + + if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) { + stbtt_uint8 *class1Records = table + 16; + stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count); + stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + break; + }; + } + } + break; + }; + + default: + // TODO: Implement other stuff. + break; + } + } + + return 0; +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) +{ + int xAdvance = 0; + + if (info->gpos) + xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); + + if (info->kern) + xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) { - if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs + if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs return 0; return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); } @@ -2395,7 +2673,7 @@ static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) hh->num_remaining_in_head_chunk = count; } --hh->num_remaining_in_head_chunk; - return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; } } @@ -3229,8 +3507,9 @@ error: STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) { - float scale = scale_x > scale_y ? scale_y : scale_x; - int winding_count, *winding_lengths; + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); if (windings) { stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); @@ -3318,6 +3597,11 @@ STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo * return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); } +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); +} + STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) { stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); @@ -3909,7 +4193,7 @@ static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float discr = b*b - a*c; if (discr > 0.0) { float rcpna = -1 / a; - float d = (float) sqrt(discr); + float d = (float) STBTT_sqrt(discr); s0 = (b+d) * rcpna; s1 = (b-d) * rcpna; if (s0 >= 0.0 && s0 <= 1.0) @@ -3971,7 +4255,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex orig[1] = y; // make sure y never passes through a vertex of the shape - y_frac = (float) fmod(y, 1.0f); + y_frac = (float) STBTT_fmod(y, 1.0f); if (y_frac < 0.01f) y += 0.01f; else if (y_frac > 0.99f) @@ -4471,6 +4755,9 @@ STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const // FULL VERSION HISTORY // +// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix // 1.16 (2017-07-12) SDF support // 1.15 (2017-03-03) make more arguments const // 1.14 (2017-01-16) num-fonts-in-TTC function diff --git a/stb_vorbis.c b/stb_vorbis.c index 14cebbf..dea5e1d 100644 --- a/stb_vorbis.c +++ b/stb_vorbis.c @@ -1,11 +1,11 @@ -// Ogg Vorbis audio decoder - v1.11 - public domain +// Ogg Vorbis audio decoder - v1.14 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. // -// Originally sponsored by RAD Game Tools. Seeking sponsored -// by Phillip Bennefall, Marc Andersen, Aaron Baker, Elias Software, -// Aras Pranckevicius, and Sean Barrett. +// Originally sponsored by RAD Game Tools. Seeking implementation +// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, +// Elias Software, Aras Pranckevicius, and Sean Barrett. // // LICENSE // @@ -30,22 +30,26 @@ // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart // manxorist@github saga musix github:infatum +// Timur Gagiev // // Partial history: -// 1.11 - 2017/07/23 - fix MinGW compilation -// 1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory -// 1.09 - 2016/04/04 - back out 'truncation of last frame' fix from previous version -// 1.08 - 2016/04/02 - warnings; setup memory leaks; truncation of last frame -// 1.07 - 2015/01/16 - fixes for crashes on invalid files; warning fixes; const -// 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) +// 1.14 - 2018-02-11 - delete bogus dealloca usage +// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) +// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files +// 1.11 - 2017-07-23 - fix MinGW compilation +// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory +// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) // some crash fixes when out of memory or with corrupt files // fix some inappropriately signed shifts -// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant -// 1.04 - 2014/08/27 - fix missing const-correct case in API -// 1.03 - 2014/08/07 - warning fixes -// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows -// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct) -// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant +// 1.04 - 2014-08-27 - fix missing const-correct case in API +// 1.03 - 2014-08-07 - warning fixes +// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel; // (API change) report sample rate for decode-full-file funcs // // See end of file for full version history. @@ -880,11 +884,7 @@ static int error(vorb *f, enum STBVorbisError e) #define array_size_required(count,size) (count*(sizeof(void *)+(size))) #define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) -#ifdef dealloca -#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : dealloca(size)) -#else #define temp_free(f,p) 0 -#endif #define temp_alloc_save(f) ((f)->temp_offset) #define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) @@ -2042,6 +2042,8 @@ static int residue_decode(vorb *f, Codebook *book, float *target, int offset, in return TRUE; } +// n is 1/2 of the blocksize -- +// specification: "Correct per-vector decode length is [n]/2" static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) { int i,j,pass; @@ -2049,7 +2051,10 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int int rtype = f->residue_types[rn]; int c = r->classbook; int classwords = f->codebooks[c].dimensions; - int n_read = r->end - r->begin; + unsigned int actual_size = rtype == 2 ? n*2 : n; + unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); + unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); + int n_read = limit_r_end - limit_r_begin; int part_read = n_read / r->part_size; int temp_alloc_point = temp_alloc_save(f); #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE @@ -3391,7 +3396,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, if (f->last_seg_which == f->end_seg_with_known_loc) { // if we have a valid current loc, and this is final: if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { - uint32 current_end = f->known_loc_for_packet - (n-right_end); + uint32 current_end = f->known_loc_for_packet; // then let's infer the size of the (probably) short final frame if (current_end < f->current_loc + (right_end-left_start)) { if (current_end < f->current_loc) { @@ -3400,7 +3405,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, } else { *len = current_end - f->current_loc; } - *len += left_start; + *len += left_start; // this doesn't seem right, but has no ill effect on my test files if (*len > right_end) *len = right_end; // this should never happen f->current_loc += *len; return TRUE; @@ -4050,6 +4055,7 @@ static int start_decoder(vorb *f) f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); + memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); #ifdef STB_VORBIS_NO_DEFER_FLOOR f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); @@ -4077,7 +4083,10 @@ static int start_decoder(vorb *f) int i,max_part_read=0; for (i=0; i < f->residue_count; ++i) { Residue *r = f->residue_config + i; - int n_read = r->end - r->begin; + unsigned int actual_size = f->blocksize_1 / 2; + unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; + unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; + int n_read = limit_r_end - limit_r_begin; int part_read = n_read / r->part_size; if (part_read > max_part_read) max_part_read = part_read; @@ -4088,6 +4097,8 @@ static int start_decoder(vorb *f) classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); #endif + // maximum reasonable partition size is f->blocksize_1 + f->temp_memory_required = classify_mem; if (imdct_mem > f->temp_memory_required) f->temp_memory_required = imdct_mem; @@ -5351,20 +5362,22 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in #endif // STB_VORBIS_NO_PULLDATA_API /* Version history - 1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory - 1.09 - 2016/04/04 - back out 'avoid discarding last frame' fix from previous version - 1.08 - 2016/04/02 - fixed multiple warnings; fix setup memory leaks; + 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files + 1.11 - 2017-07-23 - fix MinGW compilation + 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory + 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; avoid discarding last frame of audio data - 1.07 - 2015/01/16 - fixed some warnings, fix mingw, const-correct API + 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API some more crash fixes when out of memory or with corrupt files - 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) + 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) some crash fixes when out of memory or with corrupt files - 1.05 - 2015/04/19 - don't define __forceinline if it's redundant - 1.04 - 2014/08/27 - fix missing const-correct case in API - 1.03 - 2014/08/07 - Warning fixes - 1.02 - 2014/07/09 - Declare qsort compare function _cdecl on windows - 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float - 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in multichannel + 1.05 - 2015-04-19 - don't define __forceinline if it's redundant + 1.04 - 2014-08-27 - fix missing const-correct case in API + 1.03 - 2014-08-07 - Warning fixes + 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float + 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel (API change) report sample rate for decode-full-file funcs 0.99996 - bracket #include for macintosh compilation by Laurent Gomila 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem diff --git a/stretchy_buffer.h b/stretchy_buffer.h index bb711f6..cbd48a3 100644 --- a/stretchy_buffer.h +++ b/stretchy_buffer.h @@ -1,7 +1,8 @@ -// stretchy_buffer.h - v1.02 - public domain - nothings.org/stb +// stretchy_buffer.h - v1.03 - public domain - nothings.org/stb // a vector<>-like dynamic array for C // // version history: +// 1.03 - compile as C++ maybe // 1.02 - tweaks to syntax for no good reason // 1.01 - added a "common uses" documentation section // 1.0 - fixed bug in the version I posted prematurely @@ -162,6 +163,10 @@ // the main trick is in realizing in the first place that it's // possible to do this in a generic, type-safe way in C. // +// Contributors: +// +// Timothy Wright (github:ZenToad) +// // LICENSE // // See end of file for license information. @@ -189,7 +194,7 @@ #define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a)) #define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0) -#define stb__sbgrow(a,n) ((a) = stb__sbgrowf((a), (n), sizeof(*(a)))) +#define stb__sbgrow(a,n) (*((void **)&(a)) = stb__sbgrowf((a), (n), sizeof(*(a)))) #include diff --git a/tests/Makefile b/tests/Makefile index 055ffaf..53e2485 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -3,5 +3,5 @@ CFLAGS = -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast -DSTB_DIVIDE_TEST CPPFLAGS = -Wno-write-strings -DSTB_DIVIDE_TEST all: - $(CC) $(INCLUDES) $(CFLAGS) ../stb_vorbis.c test_c_compilation.c -lm - $(CC) $(INCLUDES) $(CPPFLAGS) test_cpp_compilation.cpp -lm + $(CC) $(INCLUDES) $(CFLAGS) ../stb_vorbis.c test_c_compilation.c -lm + $(CC) $(INCLUDES) $(CPPFLAGS) test_cpp_compilation.cpp -lm -lstdc++ diff --git a/tests/caveview/cave_mesher.c b/tests/caveview/cave_mesher.c index 1fac297..1f76c89 100644 --- a/tests/caveview/cave_mesher.c +++ b/tests/caveview/cave_mesher.c @@ -37,7 +37,8 @@ extern void ods(char *fmt, ...); #define FAST_CHUNK #define IN_PLACE -#define SKIP_TERRAIN 48 // use to avoid building underground stuff +#define SKIP_TERRAIN 0 +//#define SKIP_TERRAIN 48 // use to avoid building underground stuff // allows you to see what perf would be like if underground was efficiently culled, // or if you were making a game without underground @@ -131,7 +132,8 @@ unsigned char minecraft_info[256][7] = { C_solid, 35,35,35,35,4,4, }, // 48 - { C_solid, 36,36,36,36,36,36 }, + //{ C_solid, 36,36,36,36,36,36 }, + { C_force, 36,36,36,36,36,36 }, { C_solid, 37,37,37,37,37,37 }, { C_cross, 80,80,80,80,80,80 }, // torch { C_empty }, // fire @@ -142,6 +144,7 @@ unsigned char minecraft_info[256][7] = // 56 { C_solid, 50,50,50,50,50,50 }, + //{ C_force, 50,50,50,50,50,50 }, { C_solid, 26,26,26,26,26,26 }, { C_solid, 60,59,59,59,43,43 }, { C_cross, 95,95,95,95 }, @@ -831,6 +834,8 @@ void mesh_init(void) minecraft_color_for_blocktype[161][i] = 37 | 64; // green minecraft_color_for_blocktype[10][i] = 63; // emissive lava minecraft_color_for_blocktype[11][i] = 63; // emissive + //minecraft_color_for_blocktype[56][i] = 63; // emissive diamond + minecraft_color_for_blocktype[48][i] = 63; // emissive dungeon } #ifdef VHEIGHT_TEST diff --git a/tests/prerelease/stb_lib.h b/tests/prerelease/stb_lib.h new file mode 100644 index 0000000..f4445c6 --- /dev/null +++ b/tests/prerelease/stb_lib.h @@ -0,0 +1,3305 @@ +/* stb_lib.h - v1.00 - http://nothings.org/stb + no warranty is offered or implied; use this code at your own risk + + ============================================================================ + You MUST + + #define STB_LIB_IMPLEMENTATION + + in EXACTLY _one_ C or C++ file that includes this header, BEFORE the + include, like this: + + #define STB_LIB_IMPLEMENTATION + #include "stblib_files.h" + + All other files should just #include "stblib_files.h" without the #define. + ============================================================================ + +LICENSE + + See end of file for license information. + +CREDITS + + Written by Sean Barrett. + + Fixes: + Philipp Wiesemann Robert Nix + r-lyeh blackpawn + github:Mojofreem Ryan Whitworth + Vincent Isambart Mike Sartain + Eugene Opalev Tim Sjostrand + github:infatum Dave Butler +*/ + +#ifndef STB_INCLUDE_STB_LIB_H + +#include + +#if defined(_WIN32) && !defined(__MINGW32__) + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif + #ifndef _CRT_NON_CONFORMING_SWPRINTFS + #define _CRT_NON_CONFORMING_SWPRINTFS + #endif + #if !defined(_MSC_VER) || _MSC_VER > 1700 + #include // _BitScanReverse + #endif +#endif + +#include // stdlib could have min/max +#include // need FILE +#include // stb_define_hash needs memcpy/memset +#include // stb_dirtree + +typedef unsigned char stb_uchar; +typedef unsigned char stb_uint8; +typedef unsigned int stb_uint; +typedef unsigned short stb_uint16; +typedef short stb_int16; +typedef signed char stb_int8; +#if defined(STB_USE_LONG_FOR_32_BIT_INT) || defined(STB_LONG32) + typedef unsigned long stb_uint32; + typedef long stb_int32; +#else + typedef unsigned int stb_uint32; + typedef int stb_int32; +#endif +typedef char stb__testsize2_16[sizeof(stb_uint16)==2 ? 1 : -1]; +typedef char stb__testsize2_32[sizeof(stb_uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER + typedef unsigned __int64 stb_uint64; + typedef __int64 stb_int64; + #define STB_IMM_UINT64(literalui64) (literalui64##ui64) +#else + // ?? + typedef unsigned long long stb_uint64; + typedef long long stb_int64; + #define STB_IMM_UINT64(literalui64) (literalui64##ULL) +#endif +typedef char stb__testsize2_64[sizeof(stb_uint64)==8 ? 1 : -1]; + +#ifdef __cplusplus + #define STB_EXTERN extern "C" +#else + #define STB_EXTERN extern +#endif + +// check for well-known debug defines +#if defined(DEBUG) || defined(_DEBUG) || defined(DBG) + #ifndef NDEBUG + #define STB_DEBUG + #endif +#endif + +#ifdef STB_DEBUG + #include +#endif +#endif // STB_INCLUDE_STB_LIB_H + +#ifdef STB_LIB_IMPLEMENTATION + #include + #include + #include + #include + #include + #ifndef _WIN32 + #include + #else + #include // _mktemp + #include // _rmdir + #endif + #include // stat()/_stat() + #include // stat()/_stat() +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Miscellany +// + +#ifdef _WIN32 + #define stb_stricmp(a,b) stricmp(a,b) + #define stb_strnicmp(a,b,n) strnicmp(a,b,n) +#else + #define stb_stricmp(a,b) strcasecmp(a,b) + #define stb_strnicmp(a,b,n) strncasecmp(a,b,n) +#endif + +#ifndef STB_INCLUDE_STB_LIB_H +STB_EXTERN void stb_fatal(char *fmt, ...); +STB_EXTERN void stb_swap(void *p, void *q, size_t sz); +STB_EXTERN double stb_linear_remap(double x, double x_min, double x_max, + double out_min, double out_max); + +#define stb_arrcount(x) (sizeof(x)/sizeof((x)[0])) +#define stb_lerp(t,a,b) ( (a) + (t) * (float) ((b)-(a)) ) +#define stb_unlerp(t,a,b) ( ((t) - (a)) / (float) ((b) - (a)) ) + +#endif + + +#ifdef STB_LIB_IMPLEMENTATION +void stb_fatal(char *s, ...) +{ + va_list a; + va_start(a,s); + fputs("Fatal error: ", stderr); + vfprintf(stderr, s, a); + va_end(a); + fputs("\n", stderr); + #ifdef STB_DEBUG + #ifdef _MSC_VER + #ifndef _WIN64 + __asm int 3; // trap to debugger! + #else + __debugbreak(); + #endif + #else + __builtin_trap(); + #endif + #endif + exit(1); +} + +typedef struct { char d[4]; } stb__4; +typedef struct { char d[8]; } stb__8; + +// optimize the small cases, though you shouldn't be calling this for those! +void stb_swap(void *p, void *q, size_t sz) +{ + char buffer[256]; + if (p == q) return; + if (sz == 4) { + stb__4 temp = * ( stb__4 *) p; + * (stb__4 *) p = * ( stb__4 *) q; + * (stb__4 *) q = temp; + return; + } else if (sz == 8) { + stb__8 temp = * ( stb__8 *) p; + * (stb__8 *) p = * ( stb__8 *) q; + * (stb__8 *) q = temp; + return; + } + + while (sz > sizeof(buffer)) { + stb_swap(p, q, sizeof(buffer)); + p = (char *) p + sizeof(buffer); + q = (char *) q + sizeof(buffer); + sz -= sizeof(buffer); + } + + memcpy(buffer, p , sz); + memcpy(p , q , sz); + memcpy(q , buffer, sz); +} + +#ifdef stb_linear_remap +#undef stb_linear_remap +#endif + +double stb_linear_remap(double x, double x_min, double x_max, + double out_min, double out_max) +{ + return stb_lerp(stb_unlerp(x,x_min,x_max),out_min,out_max); +} + +#define stb_linear_remap(t,a,b,c,d) stb_lerp(stb_unlerp(t,a,b),c,d) +#endif // STB_LIB_IMPLEMENTATION + +#ifndef STB_INCLUDE_STB_LIB_H +// avoid unnecessary function call, but define function so its address can be taken +#ifndef stb_linear_remap +#define stb_linear_remap(t,a,b,c,d) stb_lerp(stb_unlerp(t,a,b),c,d) +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// cross-platform snprintf because they keep changing that, +// and with old compilers without vararg macros we can't write +// a macro wrapper to fix it up + +#ifndef STB_INCLUDE_STB_LIB_H +STB_EXTERN int stb_snprintf(char *s, size_t n, const char *fmt, ...); +STB_EXTERN int stb_vsnprintf(char *s, size_t n, const char *fmt, va_list v); +STB_EXTERN char *stb_sprintf(const char *fmt, ...); +#endif + +#ifdef STB_LIB_IMPLEMENTATION + +int stb_vsnprintf(char *s, size_t n, const char *fmt, va_list v) +{ + int res; + #ifdef _WIN32 + // Could use "_vsnprintf_s(s, n, _TRUNCATE, fmt, v)" ? + res = _vsnprintf(s,n,fmt,v); + #else + res = vsnprintf(s,n,fmt,v); + #endif + if (n) s[n-1] = 0; + // Unix returns length output would require, Windows returns negative when truncated. + return (res >= (int) n || res < 0) ? -1 : res; +} + +int stb_snprintf(char *s, size_t n, const char *fmt, ...) +{ + int res; + va_list v; + va_start(v,fmt); + res = stb_vsnprintf(s, n, fmt, v); + va_end(v); + return res; +} + +char *stb_sprintf(const char *fmt, ...) +{ + static char buffer[1024]; + va_list v; + va_start(v,fmt); + stb_vsnprintf(buffer,1024,fmt,v); + va_end(v); + return buffer; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Windows UTF8 filename handling +// +// Windows stupidly treats 8-bit filenames as some dopey code page, +// rather than utf-8. If we want to use utf8 filenames, we have to +// convert them to WCHAR explicitly and call WCHAR versions of the +// file functions. So, ok, we do. + + +#ifndef STB_INCLUDE_STB_LIB_H +#ifdef _WIN32 + #define stb__fopen(x,y) _wfopen((const wchar_t *)stb__from_utf8(x), (const wchar_t *)stb__from_utf8_alt(y)) + #define stb__windows(x,y) x +#else + #define stb__fopen(x,y) fopen(x,y) + #define stb__windows(x,y) y +#endif + + +typedef unsigned short stb__wchar; + +STB_EXTERN stb__wchar * stb_from_utf8(stb__wchar *buffer, char *str, int n); +STB_EXTERN char * stb_to_utf8 (char *buffer, stb__wchar *str, int n); + +STB_EXTERN stb__wchar *stb__from_utf8(char *str); +STB_EXTERN stb__wchar *stb__from_utf8_alt(char *str); +STB_EXTERN char *stb__to_utf8(stb__wchar *str); +#endif + +#ifdef STB_LIB_IMPLEMENTATION +stb__wchar * stb_from_utf8(stb__wchar *buffer, char *ostr, int n) +{ + unsigned char *str = (unsigned char *) ostr; + stb_uint32 c; + int i=0; + --n; + while (*str) { + if (i >= n) + return NULL; + if (!(*str & 0x80)) + buffer[i++] = *str++; + else if ((*str & 0xe0) == 0xc0) { + if (*str < 0xc2) return NULL; + c = (*str++ & 0x1f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + buffer[i++] = c + (*str++ & 0x3f); + } else if ((*str & 0xf0) == 0xe0) { + if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return NULL; + if (*str == 0xed && str[1] > 0x9f) return NULL; // str[1] < 0x80 is checked below + c = (*str++ & 0x0f) << 12; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + buffer[i++] = c + (*str++ & 0x3f); + } else if ((*str & 0xf8) == 0xf0) { + if (*str > 0xf4) return NULL; + if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return NULL; + if (*str == 0xf4 && str[1] > 0x8f) return NULL; // str[1] < 0x80 is checked below + c = (*str++ & 0x07) << 18; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 12; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f); + // utf-8 encodings of values used in surrogate pairs are invalid + if ((c & 0xFFFFF800) == 0xD800) return NULL; + if (c >= 0x10000) { + c -= 0x10000; + if (i + 2 > n) return NULL; + buffer[i++] = 0xD800 | (0x3ff & (c >> 10)); + buffer[i++] = 0xDC00 | (0x3ff & (c )); + } + } else + return NULL; + } + buffer[i] = 0; + return buffer; +} + +char * stb_to_utf8(char *buffer, stb__wchar *str, int n) +{ + int i=0; + --n; + while (*str) { + if (*str < 0x80) { + if (i+1 > n) return NULL; + buffer[i++] = (char) *str++; + } else if (*str < 0x800) { + if (i+2 > n) return NULL; + buffer[i++] = 0xc0 + (*str >> 6); + buffer[i++] = 0x80 + (*str & 0x3f); + str += 1; + } else if (*str >= 0xd800 && *str < 0xdc00) { + stb_uint32 c; + if (i+4 > n) return NULL; + c = ((str[0] - 0xd800) << 10) + ((str[1]) - 0xdc00) + 0x10000; + buffer[i++] = 0xf0 + (c >> 18); + buffer[i++] = 0x80 + ((c >> 12) & 0x3f); + buffer[i++] = 0x80 + ((c >> 6) & 0x3f); + buffer[i++] = 0x80 + ((c ) & 0x3f); + str += 2; + } else if (*str >= 0xdc00 && *str < 0xe000) { + return NULL; + } else { + if (i+3 > n) return NULL; + buffer[i++] = 0xe0 + (*str >> 12); + buffer[i++] = 0x80 + ((*str >> 6) & 0x3f); + buffer[i++] = 0x80 + ((*str ) & 0x3f); + str += 1; + } + } + buffer[i] = 0; + return buffer; +} + +stb__wchar *stb__from_utf8(char *str) +{ + static stb__wchar buffer[4096]; + return stb_from_utf8(buffer, str, 4096); +} + +stb__wchar *stb__from_utf8_alt(char *str) +{ + static stb__wchar buffer[4096]; + return stb_from_utf8(buffer, str, 4096); +} + +char *stb__to_utf8(stb__wchar *str) +{ + static char buffer[4096]; + return stb_to_utf8(buffer, str, 4096); +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// qsort Compare Routines +// NOT THREAD SAFE + +#ifndef STB_INCLUDE_STB_LIB_H +STB_EXTERN int (*stb_intcmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_qsort_strcmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_qsort_stricmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_floatcmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_doublecmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_ucharcmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_charcmp(int offset))(const void *a, const void *b); +#endif + +#ifdef STB_LIB_IMPLEMENTATION +static int stb__intcmpoffset, stb__ucharcmpoffset, stb__strcmpoffset; +static int stb__floatcmpoffset, stb__doublecmpoffset, stb__charcmpoffset; + +int stb__intcmp(const void *a, const void *b) +{ + const int p = *(const int *) ((const char *) a + stb__intcmpoffset); + const int q = *(const int *) ((const char *) b + stb__intcmpoffset); + return p < q ? -1 : p > q; +} + +int stb__ucharcmp(const void *a, const void *b) +{ + const int p = *(const unsigned char *) ((const char *) a + stb__ucharcmpoffset); + const int q = *(const unsigned char *) ((const char *) b + stb__ucharcmpoffset); + return p < q ? -1 : p > q; +} + +int stb__charcmp(const void *a, const void *b) +{ + const int p = *(const char *) ((const char *) a + stb__ucharcmpoffset); + const int q = *(const char *) ((const char *) b + stb__ucharcmpoffset); + return p < q ? -1 : p > q; +} + +int stb__floatcmp(const void *a, const void *b) +{ + const float p = *(const float *) ((const char *) a + stb__floatcmpoffset); + const float q = *(const float *) ((const char *) b + stb__floatcmpoffset); + return p < q ? -1 : p > q; +} + +int stb__doublecmp(const void *a, const void *b) +{ + const double p = *(const double *) ((const char *) a + stb__doublecmpoffset); + const double q = *(const double *) ((const char *) b + stb__doublecmpoffset); + return p < q ? -1 : p > q; +} + +int stb__qsort_strcmp(const void *a, const void *b) +{ + const char *p = *(const char **) ((const char *) a + stb__strcmpoffset); + const char *q = *(const char **) ((const char *) b + stb__strcmpoffset); + return strcmp(p,q); +} + +int stb__qsort_stricmp(const void *a, const void *b) +{ + const char *p = *(const char **) ((const char *) a + stb__strcmpoffset); + const char *q = *(const char **) ((const char *) b + stb__strcmpoffset); + return stb_stricmp(p,q); +} + +int (*stb_intcmp(int offset))(const void *, const void *) +{ + stb__intcmpoffset = offset; + return &stb__intcmp; +} + +int (*stb_ucharcmp(int offset))(const void *, const void *) +{ + stb__ucharcmpoffset = offset; + return &stb__ucharcmp; +} + +int (*stb_charcmp(int offset))(const void *, const void *) +{ + stb__charcmpoffset = offset; + return &stb__ucharcmp; +} + +int (*stb_qsort_strcmp(int offset))(const void *, const void *) +{ + stb__strcmpoffset = offset; + return &stb__qsort_strcmp; +} + +int (*stb_qsort_stricmp(int offset))(const void *, const void *) +{ + stb__strcmpoffset = offset; + return &stb__qsort_stricmp; +} + +int (*stb_floatcmp(int offset))(const void *, const void *) +{ + stb__floatcmpoffset = offset; + return &stb__floatcmp; +} + +int (*stb_doublecmp(int offset))(const void *, const void *) +{ + stb__doublecmpoffset = offset; + return &stb__doublecmp; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// String Processing +// + +#ifndef STB_INCLUDE_STB_LIB_H +#define stb_prefixi(s,t) (0==stb_strnicmp((s),(t),strlen(t))) + +enum stb_splitpath_flag +{ + STB_PATH = 1, + STB_FILE = 2, + STB_EXT = 4, + STB_PATH_FILE = STB_PATH + STB_FILE, + STB_FILE_EXT = STB_FILE + STB_EXT, + STB_EXT_NO_PERIOD = 8, +}; + +STB_EXTERN char * stb_skipwhite(char *s); +STB_EXTERN char * stb_trimwhite(char *s); +STB_EXTERN char * stb_skipnewline(char *s); +STB_EXTERN char * stb_strncpy(char *s, char *t, int n); +STB_EXTERN char * stb_substr(char *t, int n); +STB_EXTERN char * stb_duplower(char *s); +STB_EXTERN void stb_tolower (char *s); +STB_EXTERN char * stb_strchr2 (char *s, char p1, char p2); +STB_EXTERN char * stb_strrchr2(char *s, char p1, char p2); +STB_EXTERN char * stb_strtok(char *output, char *src, char *delimit); +STB_EXTERN char * stb_strtok_keep(char *output, char *src, char *delimit); +STB_EXTERN char * stb_strtok_invert(char *output, char *src, char *allowed); +STB_EXTERN char * stb_dupreplace(char *s, char *find, char *replace); +STB_EXTERN void stb_replaceinplace(char *s, char *find, char *replace); +STB_EXTERN char * stb_splitpath(char *output, char *src, int flag); +STB_EXTERN char * stb_splitpathdup(char *src, int flag); +STB_EXTERN char * stb_replacedir(char *output, char *src, char *dir); +STB_EXTERN char * stb_replaceext(char *output, char *src, char *ext); +STB_EXTERN void stb_fixpath(char *path); +STB_EXTERN char * stb_shorten_path_readable(char *path, int max_len); +STB_EXTERN int stb_suffix (char *s, char *t); +STB_EXTERN int stb_suffixi(char *s, char *t); +STB_EXTERN int stb_prefix (char *s, char *t); +STB_EXTERN char * stb_strichr(char *s, char t); +STB_EXTERN char * stb_stristr(char *s, char *t); +STB_EXTERN int stb_prefix_count(char *s, char *t); +STB_EXTERN const char * stb_plural(int n); // "s" or "" +STB_EXTERN size_t stb_strscpy(char *d, const char *s, size_t n); + +STB_EXTERN char **stb_tokens(char *src, char *delimit, int *count); +STB_EXTERN char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out); +STB_EXTERN char **stb_tokens_nested_empty(char *src, char *delimit, int *count, char *nest_in, char *nest_out); +STB_EXTERN char **stb_tokens_allowempty(char *src, char *delimit, int *count); +STB_EXTERN char **stb_tokens_stripwhite(char *src, char *delimit, int *count); +STB_EXTERN char **stb_tokens_withdelim(char *src, char *delimit, int *count); +STB_EXTERN char **stb_tokens_quoted(char *src, char *delimit, int *count); +// with 'quoted', allow delimiters to appear inside quotation marks, and don't +// strip whitespace inside them (and we delete the quotation marks unless they +// appear back to back, in which case they're considered escaped) +#endif // STB_INCLUDE_STB_LIB_H + +#ifdef STB_LIB_IMPLEMENTATION +#include + +size_t stb_strscpy(char *d, const char *s, size_t n) +{ + size_t len = strlen(s); + if (len >= n) { + if (n) d[0] = 0; + return 0; + } + strcpy(d,s); + return len + 1; +} + +const char *stb_plural(int n) +{ + return n == 1 ? "" : "s"; +} + +int stb_prefix(char *s, char *t) +{ + while (*t) + if (*s++ != *t++) + return 0; + return 1; +} + +int stb_prefix_count(char *s, char *t) +{ + int c=0; + while (*t) { + if (*s++ != *t++) + break; + ++c; + } + return c; +} + +int stb_suffix(char *s, char *t) +{ + size_t n = strlen(s); + size_t m = strlen(t); + if (m <= n) + return 0 == strcmp(s+n-m, t); + else + return 0; +} + +int stb_suffixi(char *s, char *t) +{ + size_t n = strlen(s); + size_t m = strlen(t); + if (m <= n) + return 0 == stb_stricmp(s+n-m, t); + else + return 0; +} + +// originally I was using this table so that I could create known sentinel +// values--e.g. change whitetable[0] to be true if I was scanning for whitespace, +// and false if I was scanning for nonwhite. I don't appear to be using that +// functionality anymore (I do for tokentable, though), so just replace it +// with isspace() +char *stb_skipwhite(char *s) +{ + while (isspace((unsigned char) *s)) ++s; + return s; +} + +char *stb_skipnewline(char *s) +{ + if (s[0] == '\r' || s[0] == '\n') { + if (s[0]+s[1] == '\r' + '\n') ++s; + ++s; + } + return s; +} + +char *stb_trimwhite(char *s) +{ + int i,n; + s = stb_skipwhite(s); + n = (int) strlen(s); + for (i=n-1; i >= 0; --i) + if (!isspace(s[i])) + break; + s[i+1] = 0; + return s; +} + +char *stb_strncpy(char *s, char *t, int n) +{ + strncpy(s,t,n); + s[n-1] = 0; + return s; +} + +char *stb_substr(char *t, int n) +{ + char *a; + int z = (int) strlen(t); + if (z < n) n = z; + a = (char *) malloc(n+1); + strncpy(a,t,n); + a[n] = 0; + return a; +} + +char *stb_duplower(char *s) +{ + char *p = strdup(s), *q = p; + while (*q) { + *q = tolower(*q); + ++q; + } + return p; +} + +void stb_tolower(char *s) +{ + while (*s) { + *s = tolower(*s); + ++s; + } +} + +char *stb_strchr2(char *s, char x, char y) +{ + for(; *s; ++s) + if (*s == x || *s == y) + return s; + return NULL; +} + +char *stb_strrchr2(char *s, char x, char y) +{ + char *r = NULL; + for(; *s; ++s) + if (*s == x || *s == y) + r = s; + return r; +} + +char *stb_strichr(char *s, char t) +{ + if (tolower(t) == toupper(t)) + return strchr(s,t); + return stb_strchr2(s, (char) tolower(t), (char) toupper(t)); +} + +char *stb_stristr(char *s, char *t) +{ + size_t n = strlen(t); + char *z; + if (n==0) return s; + while ((z = stb_strichr(s, *t)) != NULL) { + if (0==stb_strnicmp(z, t, n)) + return z; + s = z+1; + } + return NULL; +} + +static char *stb_strtok_raw(char *output, char *src, char *delimit, int keep, int invert) +{ + if (invert) { + while (*src && strchr(delimit, *src) != NULL) { + *output++ = *src++; + } + } else { + while (*src && strchr(delimit, *src) == NULL) { + *output++ = *src++; + } + } + *output = 0; + if (keep) + return src; + else + return *src ? src+1 : src; +} + +char *stb_strtok(char *output, char *src, char *delimit) +{ + return stb_strtok_raw(output, src, delimit, 0, 0); +} + +char *stb_strtok_keep(char *output, char *src, char *delimit) +{ + return stb_strtok_raw(output, src, delimit, 1, 0); +} + +char *stb_strtok_invert(char *output, char *src, char *delimit) +{ + return stb_strtok_raw(output, src, delimit, 1,1); +} + +static char **stb_tokens_raw(char *src_, char *delimit, int *count, + int stripwhite, int allow_empty, char *start, char *end) +{ + int nested = 0; + unsigned char *src = (unsigned char *) src_; + static char stb_tokentable[256]; // rely on static initializion to 0 + static char stable[256],etable[256]; + char *out; + char **result; + int num=0; + unsigned char *s; + + s = (unsigned char *) delimit; while (*s) stb_tokentable[*s++] = 1; + if (start) { + s = (unsigned char *) start; while (*s) stable[*s++] = 1; + s = (unsigned char *) end; if (s) while (*s) stable[*s++] = 1; + s = (unsigned char *) end; if (s) while (*s) etable[*s++] = 1; + } + stable[0] = 1; + + // two passes through: the first time, counting how many + s = (unsigned char *) src; + while (*s) { + // state: just found delimiter + // skip further delimiters + if (!allow_empty) { + stb_tokentable[0] = 0; + while (stb_tokentable[*s]) + ++s; + if (!*s) break; + } + ++num; + // skip further non-delimiters + stb_tokentable[0] = 1; + if (stripwhite == 2) { // quoted strings + while (!stb_tokentable[*s]) { + if (*s != '"') + ++s; + else { + ++s; + if (*s == '"') + ++s; // "" -> ", not start a string + else { + // begin a string + while (*s) { + if (s[0] == '"') { + if (s[1] == '"') s += 2; // "" -> " + else { ++s; break; } // terminating " + } else + ++s; + } + } + } + } + } else + while (nested || !stb_tokentable[*s]) { + if (stable[*s]) { + if (!*s) break; + if (end ? etable[*s] : nested) + --nested; + else + ++nested; + } + ++s; + } + if (allow_empty) { + if (*s) ++s; + } + } + // now num has the actual count... malloc our output structure + // need space for all the strings: strings won't be any longer than + // original input, since for every '\0' there's at least one delimiter + result = (char **) malloc(sizeof(*result) * (num+1) + (s-src+1)); + if (result == NULL) return result; + out = (char *) (result + (num+1)); + // second pass: copy out the data + s = (unsigned char *) src; + num = 0; + nested = 0; + while (*s) { + char *last_nonwhite; + // state: just found delimiter + // skip further delimiters + if (!allow_empty) { + stb_tokentable[0] = 0; + if (stripwhite) + while (stb_tokentable[*s] || isspace(*s)) + ++s; + else + while (stb_tokentable[*s]) + ++s; + } else if (stripwhite) { + while (isspace(*s)) ++s; + } + if (!*s) break; + // we're past any leading delimiters and whitespace + result[num] = out; + ++num; + // copy non-delimiters + stb_tokentable[0] = 1; + last_nonwhite = out-1; + if (stripwhite == 2) { + while (!stb_tokentable[*s]) { + if (*s != '"') { + if (!isspace(*s)) last_nonwhite = out; + *out++ = *s++; + } else { + ++s; + if (*s == '"') { + if (!isspace(*s)) last_nonwhite = out; + *out++ = *s++; // "" -> ", not start string + } else { + // begin a quoted string + while (*s) { + if (s[0] == '"') { + if (s[1] == '"') { *out++ = *s; s += 2; } + else { ++s; break; } // terminating " + } else + *out++ = *s++; + } + last_nonwhite = out-1; // all in quotes counts as non-white + } + } + } + } else { + while (nested || !stb_tokentable[*s]) { + if (!isspace(*s)) last_nonwhite = out; + if (stable[*s]) { + if (!*s) break; + if (end ? etable[*s] : nested) + --nested; + else + ++nested; + } + *out++ = *s++; + } + } + + if (stripwhite) // rewind to last non-whitespace char + out = last_nonwhite+1; + *out++ = '\0'; + + if (*s) ++s; // skip delimiter + } + s = (unsigned char *) delimit; while (*s) stb_tokentable[*s++] = 0; + if (start) { + s = (unsigned char *) start; while (*s) stable[*s++] = 1; + s = (unsigned char *) end; if (s) while (*s) stable[*s++] = 1; + s = (unsigned char *) end; if (s) while (*s) etable[*s++] = 1; + } + if (count != NULL) *count = num; + result[num] = 0; + return result; +} + +char **stb_tokens(char *src, char *delimit, int *count) +{ + return stb_tokens_raw(src,delimit,count,0,0,0,0); +} + +char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out) +{ + return stb_tokens_raw(src,delimit,count,0,0,nest_in,nest_out); +} + +char **stb_tokens_nested_empty(char *src, char *delimit, int *count, char *nest_in, char *nest_out) +{ + return stb_tokens_raw(src,delimit,count,0,1,nest_in,nest_out); +} + +char **stb_tokens_allowempty(char *src, char *delimit, int *count) +{ + return stb_tokens_raw(src,delimit,count,0,1,0,0); +} + +char **stb_tokens_stripwhite(char *src, char *delimit, int *count) +{ + return stb_tokens_raw(src,delimit,count,1,1,0,0); +} + +char **stb_tokens_quoted(char *src, char *delimit, int *count) +{ + return stb_tokens_raw(src,delimit,count,2,1,0,0); +} + +char *stb_dupreplace(char *src, char *find, char *replace) +{ + size_t len_find = strlen(find); + size_t len_replace = strlen(replace); + int count = 0; + + char *s,*p,*q; + + s = strstr(src, find); + if (s == NULL) return strdup(src); + do { + ++count; + s = strstr(s + len_find, find); + } while (s != NULL); + + p = (char *) malloc(strlen(src) + count * (len_replace - len_find) + 1); + if (p == NULL) return p; + q = p; + s = src; + for (;;) { + char *t = strstr(s, find); + if (t == NULL) { + strcpy(q,s); + assert(strlen(p) == strlen(src) + count*(len_replace-len_find)); + return p; + } + memcpy(q, s, t-s); + q += t-s; + memcpy(q, replace, len_replace); + q += len_replace; + s = t + len_find; + } +} + +void stb_replaceinplace(char *src, char *find, char *replace) +{ + size_t len_find = strlen(find); + size_t len_replace = strlen(replace); + int delta; + + char *s,*p,*q; + + delta = len_replace - len_find; + assert(delta <= 0); + if (delta > 0) return; + + p = strstr(src, find); + if (p == NULL) return; + + s = q = p; + while (*s) { + memcpy(q, replace, len_replace); + p += len_find; + q += len_replace; + s = strstr(p, find); + if (s == NULL) s = p + strlen(p); + memmove(q, p, s-p); + q += s-p; + p = s; + } + *q = 0; +} + +void stb_fixpath(char *path) +{ + for(; *path; ++path) + if (*path == '\\') + *path = '/'; +} + +void stb__add_section(char *buffer, char *data, int curlen, int newlen) +{ + if (newlen < curlen) { + int z1 = newlen >> 1, z2 = newlen-z1; + memcpy(buffer, data, z1-1); + buffer[z1-1] = '.'; + buffer[z1-0] = '.'; + memcpy(buffer+z1+1, data+curlen-z2+1, z2-1); + } else + memcpy(buffer, data, curlen); +} + +char * stb_shorten_path_readable(char *path, int len) +{ + static char buffer[1024]; + int n = strlen(path),n1,n2,r1,r2; + char *s; + if (n <= len) return path; + if (len > 1024) return path; + s = stb_strrchr2(path, '/', '\\'); + if (s) { + n1 = s - path + 1; + n2 = n - n1; + ++s; + } else { + n1 = 0; + n2 = n; + s = path; + } + // now we need to reduce r1 and r2 so that they fit in len + if (n1 < len>>1) { + r1 = n1; + r2 = len - r1; + } else if (n2 < len >> 1) { + r2 = n2; + r1 = len - r2; + } else { + r1 = n1 * len / n; + r2 = n2 * len / n; + if (r1 < len>>2) r1 = len>>2, r2 = len-r1; + if (r2 < len>>2) r2 = len>>2, r1 = len-r2; + } + assert(r1 <= n1 && r2 <= n2); + if (n1) + stb__add_section(buffer, path, n1, r1); + stb__add_section(buffer+r1, s, n2, r2); + buffer[len] = 0; + return buffer; +} + +static char *stb__splitpath_raw(char *buffer, char *path, int flag) +{ + int len=0,x,y, n = (int) strlen(path), f1,f2; + char *s = stb_strrchr2(path, '/', '\\'); + char *t = strrchr(path, '.'); + if (s && t && t < s) t = NULL; + if (s) ++s; + + if (flag == STB_EXT_NO_PERIOD) + flag |= STB_EXT; + + if (!(flag & (STB_PATH | STB_FILE | STB_EXT))) return NULL; + + f1 = s == NULL ? 0 : s-path; // start of filename + f2 = t == NULL ? n : t-path; // just past end of filename + + if (flag & STB_PATH) { + x = 0; if (f1 == 0 && flag == STB_PATH) len=2; + } else if (flag & STB_FILE) { + x = f1; + } else { + x = f2; + if (flag & STB_EXT_NO_PERIOD) + if (buffer[x] == '.') + ++x; + } + + if (flag & STB_EXT) + y = n; + else if (flag & STB_FILE) + y = f2; + else + y = f1; + + if (buffer == NULL) { + buffer = (char *) malloc(y-x + len + 1); + if (!buffer) return NULL; + } + + if (len) { strcpy(buffer, "./"); return buffer; } + strncpy(buffer, path+x, y-x); + buffer[y-x] = 0; + return buffer; +} + +char *stb_splitpath(char *output, char *src, int flag) +{ + return stb__splitpath_raw(output, src, flag); +} + +char *stb_splitpathdup(char *src, int flag) +{ + return stb__splitpath_raw(NULL, src, flag); +} + +char *stb_replacedir(char *output, char *src, char *dir) +{ + char buffer[4096]; + stb_splitpath(buffer, src, STB_FILE | STB_EXT); + if (dir) + sprintf(output, "%s/%s", dir, buffer); + else + strcpy(output, buffer); + return output; +} + +char *stb_replaceext(char *output, char *src, char *ext) +{ + char buffer[4096]; + stb_splitpath(buffer, src, STB_PATH | STB_FILE); + if (ext) + sprintf(output, "%s.%s", buffer, ext[0] == '.' ? ext+1 : ext); + else + strcpy(output, buffer); + return output; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_arr +// +// An stb_arr is directly useable as a pointer (use the actual type in your +// definition), but when it resizes, it returns a new pointer and you can't +// use the old one, so you have to be careful to copy-in-out as necessary. +// +// Use a NULL pointer as a 0-length array. +// +// float *my_array = NULL, *temp; +// +// // add elements on the end one at a time +// stb_arr_push(my_array, 0.0f); +// stb_arr_push(my_array, 1.0f); +// stb_arr_push(my_array, 2.0f); +// +// assert(my_array[1] == 2.0f); +// +// // add an uninitialized element at the end, then assign it +// *stb_arr_add(my_array) = 3.0f; +// +// // add three uninitialized elements at the end +// temp = stb_arr_addn(my_array,3); +// temp[0] = 4.0f; +// temp[1] = 5.0f; +// temp[2] = 6.0f; +// +// assert(my_array[5] == 5.0f); +// +// // remove the last one +// stb_arr_pop(my_array); +// +// assert(stb_arr_len(my_array) == 6); + + +#ifndef STB_INCLUDE_STB_LIB_H + +// simple functions written on top of other functions +#define stb_arr_empty(a) ( stb_arr_len(a) == 0 ) +#define stb_arr_add(a) ( stb_arr_addn((a),1) ) +#define stb_arr_push(a,v) ( *stb_arr_add(a)=(v) ) + +typedef struct +{ + int len, limit; + unsigned int signature; + unsigned int padding; // make it a multiple of 16 so preserve alignment mod 16 +} stb__arr; + +#define stb_arr_signature 0x51bada7b // ends with 0123 in decimal + +// access the header block stored before the data +#define stb_arrhead(a) /*lint --e(826)*/ (((stb__arr *) (a)) - 1) +#define stb_arrhead2(a) /*lint --e(826)*/ (((stb__arr *) (a)) - 1) + +#ifdef STB_DEBUG +#define stb_arr_check(a) assert(!a || stb_arrhead(a)->signature == stb_arr_signature) +#define stb_arr_check2(a) assert(!a || stb_arrhead2(a)->signature == stb_arr_signature) +#else +#define stb_arr_check(a) ((void) 0) +#define stb_arr_check2(a) ((void) 0) +#endif + +// ARRAY LENGTH + +// get the array length; special case if pointer is NULL +#define stb_arr_len(a) (a ? stb_arrhead(a)->len : 0) +#define stb_arr_len2(a) ((stb__arr *) (a) ? stb_arrhead2(a)->len : 0) +#define stb_arr_lastn(a) (stb_arr_len(a)-1) + +// check whether a given index is valid -- tests 0 <= i < stb_arr_len(a) +#define stb_arr_valid(a,i) (a ? (int) (i) < stb_arrhead(a)->len : 0) + +// change the array length so is is exactly N entries long, creating +// uninitialized entries as needed +#define stb_arr_setlen(a,n) \ + (stb__arr_setlen((void **) &(a), sizeof(a[0]), (n))) + +// change the array length so that N is a valid index (that is, so +// it is at least N entries long), creating uninitialized entries as needed +#define stb_arr_makevalid(a,n) \ + (stb_arr_len(a) < (n)+1 ? stb_arr_setlen(a,(n)+1),(a) : (a)) + +// remove the last element of the array, returning it +#define stb_arr_pop(a) ((stb_arr_check(a), (a))[--stb_arrhead(a)->len]) + +// access the last element in the array +#define stb_arr_last(a) ((stb_arr_check(a), (a))[stb_arr_len(a)-1]) + +// is iterator at end of list? +#define stb_arr_end(a,i) ((i) >= &(a)[stb_arr_len(a)]) + +// (internal) change the allocated length of the array +#define stb_arr__grow(a,n) (stb_arr_check(a), stb_arrhead(a)->len += (n)) + +// add N new unitialized elements to the end of the array +#define stb_arr__addn(a,n) /*lint --e(826)*/ \ + ((stb_arr_len(a)+(n) > stb_arrcurmax(a)) \ + ? (stb__arr_addlen((void **) &(a),sizeof(*a),(n)),0) \ + : ((stb_arr__grow(a,n), 0))) + +// add N new unitialized elements to the end of the array, and return +// a pointer to the first new one +#define stb_arr_addn(a,n) (stb_arr__addn((a),n),(a)+stb_arr_len(a)-(n)) + +// add N new uninitialized elements starting at index 'i' +#define stb_arr_insertn(a,i,n) (stb__arr_insertn((void **) &(a), sizeof(*a), i, n)) + +// insert an element at i +#define stb_arr_insert(a,i,v) (stb__arr_insertn((void **) &(a), sizeof(*a), i, 1), ((a)[i] = v)) + +// delete N elements from the middle starting at index 'i' +#define stb_arr_deleten(a,i,n) (stb__arr_deleten((void **) &(a), sizeof(*a), i, n)) + +// delete the i'th element +#define stb_arr_delete(a,i) stb_arr_deleten(a,i,1) + +// delete the i'th element, swapping down from the end +#define stb_arr_fastdelete(a,i) \ + (stb_swap(&a[i], &a[stb_arrhead(a)->len-1], sizeof(*a)), stb_arr_pop(a)) + + +// ARRAY STORAGE + +// get the array maximum storage; special case if NULL +#define stb_arrcurmax(a) (a ? stb_arrhead(a)->limit : 0) +#define stb_arrcurmax2(a) (a ? stb_arrhead2(a)->limit : 0) + +// set the maxlength of the array to n in anticipation of further growth +#define stb_arr_setsize(a,n) (stb_arr_check(a), stb__arr_setsize((void **) &(a),sizeof((a)[0]),n)) + +// make sure maxlength is large enough for at least N new allocations +#define stb_arr_atleast(a,n) (stb_arr_len(a)+(n) > stb_arrcurmax(a) \ + ? stb_arr_setsize((a), (n)) : 0) + +// make a copy of a given array (copies contents via 'memcpy'!) +#define stb_arr_copy(a) stb__arr_copy(a, sizeof((a)[0])) + +// compute the storage needed to store all the elements of the array +#define stb_arr_storage(a) (stb_arr_len(a) * sizeof((a)[0])) + +#define stb_arr_for(v,arr) for((v)=(arr); (v) < (arr)+stb_arr_len(arr); ++(v)) + +// IMPLEMENTATION + +STB_EXTERN void stb_arr_free_(void **p); +STB_EXTERN void *stb__arr_copy_(void *p, int elem_size); +STB_EXTERN void stb__arr_setsize_(void **p, int size, int limit); +STB_EXTERN void stb__arr_setlen_(void **p, int size, int newlen); +STB_EXTERN void stb__arr_addlen_(void **p, int size, int addlen); +STB_EXTERN void stb__arr_deleten_(void **p, int size, int loc, int n); +STB_EXTERN void stb__arr_insertn_(void **p, int size, int loc, int n); + +#define stb_arr_free(p) stb_arr_free_((void **) &(p)) + +#ifndef STBLIB_MALLOC_WRAPPER // @Todo + #define stb__arr_setsize stb__arr_setsize_ + #define stb__arr_setlen stb__arr_setlen_ + #define stb__arr_addlen stb__arr_addlen_ + #define stb__arr_deleten stb__arr_deleten_ + #define stb__arr_insertn stb__arr_insertn_ + #define stb__arr_copy stb__arr_copy_ +#else + #define stb__arr_addlen(p,s,n) stb__arr_addlen_(p,s,n,__FILE__,__LINE__) + #define stb__arr_setlen(p,s,n) stb__arr_setlen_(p,s,n,__FILE__,__LINE__) + #define stb__arr_setsize(p,s,n) stb__arr_setsize_(p,s,n,__FILE__,__LINE__) + #define stb__arr_deleten(p,s,i,n) stb__arr_deleten_(p,s,i,n,__FILE__,__LINE__) + #define stb__arr_insertn(p,s,i,n) stb__arr_insertn_(p,s,i,n,__FILE__,__LINE__) + #define stb__arr_copy(p,s) stb__arr_copy_(p,s,__FILE__,__LINE__) +#endif +#endif // STB_INCLUDE_STB_LIB_H + +#ifdef STB_LIB_IMPLEMENTATION +void stb_arr_malloc(void **target, void *context) +{ + stb__arr *q = (stb__arr *) malloc(sizeof(*q)); + q->len = q->limit = 0; + q->signature = stb_arr_signature; + *target = (void *) (q+1); +} + +static void * stb__arr_malloc(int size) +{ + return malloc(size); +} + +void * stb__arr_copy_(void *p, int elem_size) +{ + stb__arr *q; + if (p == NULL) return p; + q = (stb__arr *) malloc(sizeof(*q) + elem_size * stb_arrhead2(p)->limit); + stb_arr_check2(p); + memcpy(q, stb_arrhead2(p), sizeof(*q) + elem_size * stb_arrhead2(p)->len); + return q+1; +} + +void stb_arr_free_(void **pp) +{ + void *p = *pp; + stb_arr_check2(p); + if (p) { + stb__arr *q = stb_arrhead2(p); + free(q); + } + *pp = NULL; +} + +static void stb__arrsize_(void **pp, int size, int limit, int len) +{ + void *p = *pp; + stb__arr *a; + stb_arr_check2(p); + if (p == NULL) { + if (len == 0 && size == 0) return; + a = (stb__arr *) stb__arr_malloc(sizeof(*a) + size*limit); + a->limit = limit; + a->len = len; + a->signature = stb_arr_signature; + } else { + a = stb_arrhead2(p); + a->len = len; + if (a->limit < limit) { + void *p; + if (a->limit >= 4 && limit < a->limit * 2) + limit = a->limit * 2; + p = realloc(a, sizeof(*a) + limit*size); + if (p) { + a = (stb__arr *) p; + a->limit = limit; + } else { + // throw an error! + } + } + } + a->len = a->len < a->limit ? a->len : a->limit; + *pp = a+1; +} + +void stb__arr_setsize_(void **pp, int size, int limit) +{ + void *p = *pp; + stb_arr_check2(p); + stb__arrsize_(pp, size, limit, stb_arr_len2(p)); +} + +void stb__arr_setlen_(void **pp, int size, int newlen) +{ + void *p = *pp; + stb_arr_check2(p); + if (stb_arrcurmax2(p) < newlen || p == NULL) { + stb__arrsize_(pp, size, newlen, newlen); + } else { + stb_arrhead2(p)->len = newlen; + } +} + +void stb__arr_addlen_(void **p, int size, int addlen) +{ + stb__arr_setlen_(p, size, stb_arr_len2(*p) + addlen); +} + +void stb__arr_insertn_(void **pp, int size, int i, int n) +{ + void *p = *pp; + if (n) { + int z; + + if (p == NULL) { + stb__arr_addlen_(pp, size, n); + return; + } + + z = stb_arr_len2(p); + stb__arr_addlen_(&p, size, n); + memmove((char *) p + (i+n)*size, (char *) p + i*size, size * (z-i)); + } + *pp = p; +} + +void stb__arr_deleten_(void **pp, int size, int i, int n) +{ + void *p = *pp; + if (n) { + memmove((char *) p + i*size, (char *) p + (i+n)*size, size * (stb_arr_len2(p)-(i+n))); + stb_arrhead2(p)->len -= n; + } + *pp = p; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Hashing +// +// typical use for this is to make a power-of-two hash table. +// +// let N = size of table (2^n) +// let H = stb_hash(str) +// let S = stb_rehash(H) | 1 +// +// then hash probe sequence P(i) for i=0..N-1 +// P(i) = (H + S*i) & (N-1) +// +// the idea is that H has 32 bits of hash information, but the +// table has only, say, 2^20 entries so only uses 20 of the bits. +// then by rehashing the original H we get 2^12 different probe +// sequences for a given initial probe location. (So it's optimal +// for 64K tables and its optimality decreases past that.) +// +// ok, so I've added something that generates _two separate_ +// 32-bit hashes simultaneously which should scale better to +// very large tables. + +#ifndef STB_INCLUDE_STB_LIB_H +STB_EXTERN unsigned int stb_hash(char *str); +STB_EXTERN unsigned int stb_hashptr(void *p); +STB_EXTERN unsigned int stb_hashlen(char *str, int len); +STB_EXTERN unsigned int stb_rehash_improved(unsigned int v); +STB_EXTERN unsigned int stb_hash_fast(void *p, int len); +STB_EXTERN unsigned int stb_hash2(char *str, unsigned int *hash2_ptr); +STB_EXTERN unsigned int stb_hash_number(unsigned int hash); + +#define stb_rehash(x) ((x) + ((x) >> 6) + ((x) >> 19)) +#endif // STB_INCLUDE_STB_LIB_H + +#ifdef STB_LIB_IMPLEMENTATION +unsigned int stb_hash(char *str) +{ + unsigned int hash = 0; + while (*str) + hash = (hash << 7) + (hash >> 25) + *str++; + return hash + (hash >> 16); +} + +unsigned int stb_hashlen(char *str, int len) +{ + unsigned int hash = 0; + while (len-- > 0 && *str) + hash = (hash << 7) + (hash >> 25) + *str++; + return hash + (hash >> 16); +} + +unsigned int stb_hashptr(void *p) +{ + unsigned int x = (unsigned int)(size_t) p; + + // typically lacking in low bits and high bits + x = stb_rehash(x); + x += x << 16; + + // pearson's shuffle + x ^= x << 3; + x += x >> 5; + x ^= x << 2; + x += x >> 15; + x ^= x << 10; + return stb_rehash(x); +} + +unsigned int stb_rehash_improved(unsigned int v) +{ + return stb_hashptr((void *)(size_t) v); +} + +unsigned int stb_hash2(char *str, unsigned int *hash2_ptr) +{ + unsigned int hash1 = 0x3141592c; + unsigned int hash2 = 0x77f044ed; + while (*str) { + hash1 = (hash1 << 7) + (hash1 >> 25) + *str; + hash2 = (hash2 << 11) + (hash2 >> 21) + *str; + ++str; + } + *hash2_ptr = hash2 + (hash1 >> 16); + return hash1 + (hash2 >> 16); +} + +// Paul Hsieh hash +#define stb__get16_slow(p) ((p)[0] + ((p)[1] << 8)) +#if defined(_MSC_VER) + #define stb__get16(p) (*((unsigned short *) (p))) +#else + #define stb__get16(p) stb__get16_slow(p) +#endif + +unsigned int stb_hash_fast(void *p, int len) +{ + unsigned char *q = (unsigned char *) p; + unsigned int hash = len; + + if (len <= 0 || q == NULL) return 0; + + /* Main loop */ + if (((int)(size_t) q & 1) == 0) { + for (;len > 3; len -= 4) { + unsigned int val; + hash += stb__get16(q); + val = (stb__get16(q+2) << 11); + hash = (hash << 16) ^ hash ^ val; + q += 4; + hash += hash >> 11; + } + } else { + for (;len > 3; len -= 4) { + unsigned int val; + hash += stb__get16_slow(q); + val = (stb__get16_slow(q+2) << 11); + hash = (hash << 16) ^ hash ^ val; + q += 4; + hash += hash >> 11; + } + } + + /* Handle end cases */ + switch (len) { + case 3: hash += stb__get16_slow(q); + hash ^= hash << 16; + hash ^= q[2] << 18; + hash += hash >> 11; + break; + case 2: hash += stb__get16_slow(q); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += q[0]; + hash ^= hash << 10; + hash += hash >> 1; + break; + case 0: break; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; +} + +unsigned int stb_hash_number(unsigned int hash) +{ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Instantiated data structures +// +// This is an attempt to implement a templated data structure. +// +// Hash table: call stb_define_hash(TYPE,N,KEY,K1,K2,HASH,VALUE) +// TYPE -- will define a structure type containing the hash table +// N -- the name, will prefix functions named: +// N create +// N destroy +// N get +// N set, N add, N update, +// N remove +// KEY -- the type of the key. 'x == y' must be valid +// K1,K2 -- keys never used by the app, used as flags in the hashtable +// HASH -- a piece of code ending with 'return' that hashes key 'k' +// VALUE -- the type of the value. 'x = y' must be valid +// +// Note that stb_define_hash_base can be used to define more sophisticated +// hash tables, e.g. those that make copies of the key or use special +// comparisons (e.g. strcmp). + +#define STB_(prefix,name) stb__##prefix##name +#define STB__(prefix,name) prefix##name +#define STB__use(x) x +#define STB__skip(x) + +#define stb_declare_hash(PREFIX,TYPE,N,KEY,VALUE) \ + typedef struct stb__st_##TYPE TYPE;\ + PREFIX int STB__(N, init)(TYPE *h, int count);\ + PREFIX int STB__(N, memory_usage)(TYPE *h);\ + PREFIX TYPE * STB__(N, create)(void);\ + PREFIX TYPE * STB__(N, copy)(TYPE *h);\ + PREFIX void STB__(N, destroy)(TYPE *h);\ + PREFIX int STB__(N,get_flag)(TYPE *a, KEY k, VALUE *v);\ + PREFIX VALUE STB__(N,get)(TYPE *a, KEY k);\ + PREFIX int STB__(N, set)(TYPE *a, KEY k, VALUE v);\ + PREFIX int STB__(N, add)(TYPE *a, KEY k, VALUE v);\ + PREFIX int STB__(N, update)(TYPE*a,KEY k,VALUE v);\ + PREFIX int STB__(N, remove)(TYPE *a, KEY k, VALUE *v); + +#define STB_nocopy(x) (x) +#define STB_nodelete(x) 0 +#define STB_nofields +#define STB_nonullvalue(x) +#define STB_nullvalue(x) x +#define STB_safecompare(x) x +#define STB_nosafe(x) +#define STB_noprefix + +#ifdef __GNUC__ +#define STB__nogcc(x) +#else +#define STB__nogcc(x) x +#endif + +#define stb_define_hash_base(PREFIX,TYPE,FIELDS,N,NC,LOAD_FACTOR, \ + KEY,EMPTY,DEL,COPY,DISPOSE,SAFE, \ + VCOMPARE,CCOMPARE,HASH, \ + VALUE,HASVNULL,VNULL) \ + \ +typedef struct \ +{ \ + KEY k; \ + VALUE v; \ +} STB_(N,_hashpair); \ + \ +STB__nogcc( typedef struct stb__st_##TYPE TYPE; ) \ +struct stb__st_##TYPE { \ + FIELDS \ + STB_(N,_hashpair) *table; \ + unsigned int mask; \ + int count, limit; \ + int deleted; \ + \ + int delete_threshhold; \ + int grow_threshhold; \ + int shrink_threshhold; \ + unsigned char alloced, has_empty, has_del; \ + VALUE ev; VALUE dv; \ +}; \ + \ +static unsigned int STB_(N, hash)(KEY k) \ +{ \ + HASH \ +} \ + \ +PREFIX int STB__(N, init)(TYPE *h, int count) \ +{ \ + int i; \ + if (count < 4) count = 4; \ + h->limit = count; \ + h->count = 0; \ + h->mask = count-1; \ + h->deleted = 0; \ + h->grow_threshhold = (int) (count * LOAD_FACTOR); \ + h->has_empty = h->has_del = 0; \ + h->alloced = 0; \ + if (count <= 64) \ + h->shrink_threshhold = 0; \ + else \ + h->shrink_threshhold = (int) (count * (LOAD_FACTOR/2.25)); \ + h->delete_threshhold = (int) (count * (1-LOAD_FACTOR)/2); \ + h->table = (STB_(N,_hashpair)*) malloc(sizeof(h->table[0]) * count); \ + if (h->table == NULL) return 0; \ + /* ideally this gets turned into a memset32 automatically */ \ + for (i=0; i < count; ++i) \ + h->table[i].k = EMPTY; \ + return 1; \ +} \ + \ +PREFIX int STB__(N, memory_usage)(TYPE *h) \ +{ \ + return sizeof(*h) + h->limit * sizeof(h->table[0]); \ +} \ + \ +PREFIX TYPE * STB__(N, create)(void) \ +{ \ + TYPE *h = (TYPE *) malloc(sizeof(*h)); \ + if (h) { \ + if (STB__(N, init)(h, 16)) \ + h->alloced = 1; \ + else { free(h); h=NULL; } \ + } \ + return h; \ +} \ + \ +PREFIX void STB__(N, destroy)(TYPE *a) \ +{ \ + int i; \ + for (i=0; i < a->limit; ++i) \ + if (!CCOMPARE(a->table[i].k,EMPTY) && !CCOMPARE(a->table[i].k, DEL)) \ + DISPOSE(a->table[i].k); \ + free(a->table); \ + if (a->alloced) \ + free(a); \ +} \ + \ +static void STB_(N, rehash)(TYPE *a, int count); \ + \ +PREFIX int STB__(N,get_flag)(TYPE *a, KEY k, VALUE *v) \ +{ \ + unsigned int h = STB_(N, hash)(k); \ + unsigned int n = h & a->mask, s; \ + if (CCOMPARE(k,EMPTY)){ if (a->has_empty) *v = a->ev; return a->has_empty;}\ + if (CCOMPARE(k,DEL)) { if (a->has_del ) *v = a->dv; return a->has_del; }\ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (!CCOMPARE(a->table[n].k,DEL))) \ + if (VCOMPARE(a->table[n].k,k)) { *v = a->table[n].v; return 1; } \ + s = stb_rehash(h) | 1; \ + for(;;) { \ + n = (n + s) & a->mask; \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (CCOMPARE(a->table[n].k,DEL)) continue;) \ + if (VCOMPARE(a->table[n].k,k)) \ + { *v = a->table[n].v; return 1; } \ + } \ +} \ + \ +HASVNULL( \ + PREFIX VALUE STB__(N,get)(TYPE *a, KEY k) \ + { \ + VALUE v; \ + if (STB__(N,get_flag)(a,k,&v)) return v; \ + else return VNULL; \ + } \ +) \ + \ +PREFIX int STB__(N,getkey)(TYPE *a, KEY k, KEY *kout) \ +{ \ + unsigned int h = STB_(N, hash)(k); \ + unsigned int n = h & a->mask, s; \ + if (CCOMPARE(k,EMPTY)||CCOMPARE(k,DEL)) return 0; \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (!CCOMPARE(a->table[n].k,DEL))) \ + if (VCOMPARE(a->table[n].k,k)) { *kout = a->table[n].k; return 1; } \ + s = stb_rehash(h) | 1; \ + for(;;) { \ + n = (n + s) & a->mask; \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (CCOMPARE(a->table[n].k,DEL)) continue;) \ + if (VCOMPARE(a->table[n].k,k)) \ + { *kout = a->table[n].k; return 1; } \ + } \ +} \ + \ +static int STB_(N,addset)(TYPE *a, KEY k, VALUE v, \ + int allow_new, int allow_old, int copy) \ +{ \ + unsigned int h = STB_(N, hash)(k); \ + unsigned int n = h & a->mask; \ + int b = -1; \ + if (CCOMPARE(k,EMPTY)) { \ + if (a->has_empty ? allow_old : allow_new) { \ + n=a->has_empty; a->ev = v; a->has_empty = 1; return !n; \ + } else return 0; \ + } \ + if (CCOMPARE(k,DEL)) { \ + if (a->has_del ? allow_old : allow_new) { \ + n=a->has_del; a->dv = v; a->has_del = 1; return !n; \ + } else return 0; \ + } \ + if (!CCOMPARE(a->table[n].k, EMPTY)) { \ + unsigned int s; \ + if (CCOMPARE(a->table[n].k, DEL)) \ + b = n; \ + else if (VCOMPARE(a->table[n].k,k)) { \ + if (allow_old) \ + a->table[n].v = v; \ + return !allow_new; \ + } \ + s = stb_rehash(h) | 1; \ + for(;;) { \ + n = (n + s) & a->mask; \ + if (CCOMPARE(a->table[n].k, EMPTY)) break; \ + if (CCOMPARE(a->table[n].k, DEL)) { \ + if (b < 0) b = n; \ + } else if (VCOMPARE(a->table[n].k,k)) { \ + if (allow_old) \ + a->table[n].v = v; \ + return !allow_new; \ + } \ + } \ + } \ + if (!allow_new) return 0; \ + if (b < 0) b = n; else --a->deleted; \ + a->table[b].k = copy ? COPY(k) : k; \ + a->table[b].v = v; \ + ++a->count; \ + if (a->count > a->grow_threshhold) \ + STB_(N,rehash)(a, a->limit*2); \ + return 1; \ +} \ + \ +PREFIX int STB__(N, set)(TYPE *a, KEY k, VALUE v){return STB_(N,addset)(a,k,v,1,1,1);}\ +PREFIX int STB__(N, add)(TYPE *a, KEY k, VALUE v){return STB_(N,addset)(a,k,v,1,0,1);}\ +PREFIX int STB__(N, update)(TYPE*a,KEY k,VALUE v){return STB_(N,addset)(a,k,v,0,1,1);}\ + \ +PREFIX int STB__(N, remove)(TYPE *a, KEY k, VALUE *v) \ +{ \ + unsigned int h = STB_(N, hash)(k); \ + unsigned int n = h & a->mask, s; \ + if (CCOMPARE(k,EMPTY)) { if (a->has_empty) { if(v)*v = a->ev; a->has_empty=0; return 1; } return 0; } \ + if (CCOMPARE(k,DEL)) { if (a->has_del ) { if(v)*v = a->dv; a->has_del =0; return 1; } return 0; } \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + if (SAFE(CCOMPARE(a->table[n].k,DEL) || ) !VCOMPARE(a->table[n].k,k)) { \ + s = stb_rehash(h) | 1; \ + for(;;) { \ + n = (n + s) & a->mask; \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (CCOMPARE(a->table[n].k, DEL)) continue;) \ + if (VCOMPARE(a->table[n].k,k)) break; \ + } \ + } \ + DISPOSE(a->table[n].k); \ + a->table[n].k = DEL; \ + --a->count; \ + ++a->deleted; \ + if (v != NULL) \ + *v = a->table[n].v; \ + if (a->count < a->shrink_threshhold) \ + STB_(N, rehash)(a, a->limit >> 1); \ + else if (a->deleted > a->delete_threshhold) \ + STB_(N, rehash)(a, a->limit); \ + return 1; \ +} \ + \ +PREFIX TYPE * STB__(NC, copy)(TYPE *a) \ +{ \ + int i; \ + TYPE *h = (TYPE *) malloc(sizeof(*h)); \ + if (!h) return NULL; \ + if (!STB__(N, init)(h, a->limit)) { free(h); return NULL; } \ + h->count = a->count; \ + h->deleted = a->deleted; \ + h->alloced = 1; \ + h->ev = a->ev; h->dv = a->dv; \ + h->has_empty = a->has_empty; h->has_del = a->has_del; \ + memcpy(h->table, a->table, h->limit * sizeof(h->table[0])); \ + for (i=0; i < a->limit; ++i) \ + if (!CCOMPARE(h->table[i].k,EMPTY) && !CCOMPARE(h->table[i].k,DEL)) \ + h->table[i].k = COPY(h->table[i].k); \ + return h; \ +} \ + \ +static void STB_(N, rehash)(TYPE *a, int count) \ +{ \ + int i; \ + TYPE b; \ + STB__(N, init)(&b, count); \ + for (i=0; i < a->limit; ++i) \ + if (!CCOMPARE(a->table[i].k,EMPTY) && !CCOMPARE(a->table[i].k,DEL)) \ + STB_(N,addset)(&b, a->table[i].k, a->table[i].v,1,1,0); \ + free(a->table); \ + a->table = b.table; \ + a->mask = b.mask; \ + a->count = b.count; \ + a->limit = b.limit; \ + a->deleted = b.deleted; \ + a->delete_threshhold = b.delete_threshhold; \ + a->grow_threshhold = b.grow_threshhold; \ + a->shrink_threshhold = b.shrink_threshhold; \ +} + +#define STB_equal(a,b) ((a) == (b)) + +#define stb_define_hash(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE) \ + stb_define_hash_base(STB_noprefix, TYPE,STB_nofields,N,NC,0.85f, \ + KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe, \ + STB_equal,STB_equal,HASH, \ + VALUE,STB_nonullvalue,0) + +#define stb_define_hash_vnull(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE,VNULL) \ + stb_define_hash_base(STB_noprefix, TYPE,STB_nofields,N,NC,0.85f, \ + KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe, \ + STB_equal,STB_equal,HASH, \ + VALUE,STB_nullvalue,VNULL) + +////////////////////////////////////////////////////////////////////////////// +// +// stb_ptrmap +// +// An stb_ptrmap data structure is an O(1) hash table between pointers. One +// application is to let you store "extra" data associated with pointers, +// which is why it was originally called stb_extra. + +#ifndef STB_INCLUDE_STB_LIB_H +stb_declare_hash(STB_EXTERN, stb_ptrmap, stb_ptrmap_, void *, void *) +stb_declare_hash(STB_EXTERN, stb_idict, stb_idict_, stb_int32, stb_int32) + +STB_EXTERN void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *)); +STB_EXTERN stb_ptrmap *stb_ptrmap_new(void); + +STB_EXTERN stb_idict * stb_idict_new_size(unsigned int size); +STB_EXTERN void stb_idict_remove_all(stb_idict *e); +#endif // STB_INCLUDE_STB_LIB_H + +#ifdef STB_LIB_IMPLEMENTATION + +#define STB_EMPTY ((void *) 2) +#define STB_EDEL ((void *) 6) + +stb_define_hash_base(STB_noprefix,stb_ptrmap, STB_nofields, stb_ptrmap_,stb_ptrmap_,0.85f, + void *,STB_EMPTY,STB_EDEL,STB_nocopy,STB_nodelete,STB_nosafe, + STB_equal,STB_equal,return stb_hashptr(k);, + void *,STB_nullvalue,NULL) + +stb_ptrmap *stb_ptrmap_new(void) +{ + return stb_ptrmap_create(); +} + +void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *)) +{ + int i; + if (free_func) + for (i=0; i < e->limit; ++i) + if (e->table[i].k != STB_EMPTY && e->table[i].k != STB_EDEL) { + if (free_func == free) + free(e->table[i].v); // allow STB_MALLOC_WRAPPER to operate + else + free_func(e->table[i].v); + } + stb_ptrmap_destroy(e); +} + +// extra fields needed for stua_dict +#define STB_IEMPTY ((int) 1) +#define STB_IDEL ((int) 3) +stb_define_hash_base(STB_noprefix, stb_idict, STB_nofields, stb_idict_,stb_idict_,0.85f, + stb_int32,STB_IEMPTY,STB_IDEL,STB_nocopy,STB_nodelete,STB_nosafe, + STB_equal,STB_equal, + return stb_rehash_improved(k);,stb_int32,STB_nonullvalue,0) + +stb_idict * stb_idict_new_size(unsigned int size) +{ + stb_idict *e = (stb_idict *) malloc(sizeof(*e)); + if (e) { + // round up to power of 2 + while ((size & (size-1)) != 0) // while more than 1 bit is set + size += (size & ~(size-1)); // add the lowest set bit + stb_idict_init(e, size); + e->alloced = 1; + } + return e; +} + +void stb_idict_remove_all(stb_idict *e) +{ + int n; + for (n=0; n < e->limit; ++n) + e->table[n].k = STB_IEMPTY; + e->has_empty = e->has_del = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// SDICT: Hash Table for Strings (symbol table) +// +// if "use_arena=1", then strings will be copied +// into blocks and never freed until the sdict is freed; +// otherwise they're malloc()ed and free()d on the fly. +// (specify use_arena=1 if you never stb_sdict_remove) + +#ifndef STB_INCLUDE_STB_LIB_H +stb_declare_hash(STB_EXTERN, stb_sdict, stb_sdict_, char *, void *) + +STB_EXTERN stb_sdict * stb_sdict_new(void); +STB_EXTERN stb_sdict * stb_sdict_copy(stb_sdict*); +STB_EXTERN void stb_sdict_delete(stb_sdict *); +STB_EXTERN void * stb_sdict_change(stb_sdict *, char *str, void *p); +STB_EXTERN int stb_sdict_count(stb_sdict *d); + +STB_EXTERN int stb_sdict_internal_limit(stb_sdict *d); +STB_EXTERN char * stb_sdict_internal_key(stb_sdict *d, int n); +STB_EXTERN void * stb_sdict_internal_value(stb_sdict *d, int n); + +#define stb_sdict_for(d,i,q,z) \ + for(i=0; i < stb_sdict_internal_limit(d) ? (q=stb_sdict_internal_key(d,i),z=stb_sdict_internal_value(d,i),1) : 0; ++i) \ + if (q==NULL||q==(void *) 1);else // reversed makes macro friendly +#endif // STB_INCLUDE_STB_LIB_H + +#ifdef STB_LIB_IMPLEMENTATION + +// if in same translation unit, for speed, don't call accessors +#undef stb_sdict_for +#define stb_sdict_for(d,i,q,z) \ + for(i=0; i < (d)->limit ? (q=(d)->table[i].k,z=(d)->table[i].v,1) : 0; ++i) \ + if (q==NULL||q==(void *) 1);else // reversed makes macro friendly + +//#define STB_DEL ((void *) 1) +#define STB_SDEL ((char *) 1) + +stb_define_hash_base(STB_noprefix, stb_sdict, STB_nofields, stb_sdict_,stb_sdictinternal_, 0.85f, + char *, NULL, STB_SDEL, strdup, free, + STB_safecompare, !strcmp, STB_equal, return stb_hash(k);, + void *, STB_nullvalue, NULL) + +int stb_sdict_count(stb_sdict *a) +{ + return a->count; +} + +int stb_sdict_internal_limit(stb_sdict *a) +{ + return a->limit; +} +char* stb_sdict_internal_key(stb_sdict *a, int n) +{ + return a->table[n].k; +} +void* stb_sdict_internal_value(stb_sdict *a, int n) +{ + return a->table[n].v; +} + +stb_sdict * stb_sdict_new(void) +{ + stb_sdict *d = stb_sdict_create(); + if (d == NULL) return NULL; + return d; +} + +stb_sdict* stb_sdict_copy(stb_sdict *old) +{ + return stb_sdictinternal_copy(old); +} + +void stb_sdict_delete(stb_sdict *d) +{ + stb_sdict_destroy(d); +} + +void * stb_sdict_change(stb_sdict *d, char *str, void *p) +{ + void *q = stb_sdict_get(d, str); + stb_sdict_set(d, str, p); + return q; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// File Processing +// + +#ifndef STB_INCLUDE_STB_LIB_H +#ifdef _MSC_VER + #define stb_rename(x,y) _wrename((const wchar_t *)stb__from_utf8(x), (const wchar_t *)stb__from_utf8_alt(y)) + #define stb_mktemp _mktemp +#else + #define stb_mktemp mktemp + #define stb_rename rename +#endif + +#define stb_filec (char *) stb_file +#define stb_fileu (unsigned char *) stb_file +STB_EXTERN void * stb_file(char *filename, size_t *length); +STB_EXTERN size_t stb_filelen(FILE *f); +STB_EXTERN int stb_filewrite(char *filename, void *data, size_t length); +STB_EXTERN int stb_filewritestr(char *filename, char *data); +STB_EXTERN char ** stb_stringfile(char *filename, int *len); +STB_EXTERN char * stb_fgets(char *buffer, int buflen, FILE *f); +STB_EXTERN char * stb_fgets_malloc(FILE *f); +STB_EXTERN int stb_fexists(char *filename); +STB_EXTERN int stb_fcmp(char *s1, char *s2); +STB_EXTERN int stb_feq(char *s1, char *s2); +STB_EXTERN time_t stb_ftimestamp(char *filename); +STB_EXTERN int stb_fullpath(char *abs, int abs_size, char *rel); + +STB_EXTERN int stb_copyfile(char *src, char *dest); +STB_EXTERN int stb_fread(void *data, size_t len, size_t count, void *f); +STB_EXTERN int stb_fwrite(void *data, size_t len, size_t count, void *f); +#endif // STB_INCLUDE_STB_LIB_H + +#ifdef STB_LIB_IMPLEMENTATION +#if defined(_MSC_VER) || defined(__MINGW32__) + #define stb__stat _stat +#else + #define stb__stat stat +#endif + +int stb_fexists(char *filename) +{ + struct stb__stat buf; + return stb__windows( + _wstat((const wchar_t *)stb__from_utf8(filename), &buf), + stat(filename,&buf) + ) == 0; +} + +time_t stb_ftimestamp(char *filename) +{ + struct stb__stat buf; + if (stb__windows( + _wstat((const wchar_t *)stb__from_utf8(filename), &buf), + stat(filename,&buf) + ) == 0) + { + return buf.st_mtime; + } else { + return 0; + } +} + +size_t stb_filelen(FILE *f) +{ + size_t len, pos; + pos = ftell(f); + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, pos, SEEK_SET); + return len; +} + +void *stb_file(char *filename, size_t *length) +{ + FILE *f = stb__fopen(filename, "rb"); + char *buffer; + size_t len, len2; + if (!f) return NULL; + len = stb_filelen(f); + buffer = (char *) malloc(len+2); // nul + extra + len2 = fread(buffer, 1, len, f); + if (len2 == len) { + if (length) *length = len; + buffer[len] = 0; + } else { + free(buffer); + buffer = NULL; + } + fclose(f); + return buffer; +} + +int stb_filewrite(char *filename, void *data, size_t length) +{ + FILE *f = stb__fopen(filename, "wb"); + if (f) { + unsigned char *data_ptr = (unsigned char *) data; + size_t remaining = length; + while (remaining > 0) { + size_t len2 = remaining > 65536 ? 65536 : remaining; + size_t len3 = fwrite(data_ptr, 1, len2, f); + if (len2 != len3) { + fprintf(stderr, "Failed while writing %s\n", filename); + break; + } + remaining -= len2; + data_ptr += len2; + } + fclose(f); + } + return f != NULL; +} + +int stb_filewritestr(char *filename, char *data) +{ + return stb_filewrite(filename, data, strlen(data)); +} + +char ** stb_stringfile(char *filename, int *plen) +{ + FILE *f = stb__fopen(filename, "rb"); + char *buffer, **list=NULL, *s; + size_t len, count, i; + + if (!f) return NULL; + len = stb_filelen(f); + buffer = (char *) malloc(len+1); + len = fread(buffer, 1, len, f); + buffer[len] = 0; + fclose(f); + + // two passes through: first time count lines, second time set them + for (i=0; i < 2; ++i) { + s = buffer; + if (i == 1) + list[0] = s; + count = 1; + while (*s) { + if (*s == '\n' || *s == '\r') { + // detect if both cr & lf are together + int crlf = (s[0] + s[1]) == ('\n' + '\r'); + if (i == 1) *s = 0; + if (crlf) ++s; + if (s[1]) { // it's not over yet + if (i == 1) list[count] = s+1; + ++count; + } + } + ++s; + } + if (i == 0) { + list = (char **) malloc(sizeof(*list) * (count+1) + len+1); + if (!list) return NULL; + list[count] = 0; + // recopy the file so there's just a single allocation to free + memcpy(&list[count+1], buffer, len+1); + free(buffer); + buffer = (char *) &list[count+1]; + if (plen) *plen = count; + } + } + return list; +} + +char * stb_fgets(char *buffer, int buflen, FILE *f) +{ + char *p; + buffer[0] = 0; + p = fgets(buffer, buflen, f); + if (p) { + int n = strlen(p)-1; + if (n >= 0) + if (p[n] == '\n') + p[n] = 0; + } + return p; +} + +char * stb_fgets_malloc(FILE *f) +{ + // avoid reallocing for small strings + char quick_buffer[800]; + quick_buffer[sizeof(quick_buffer)-2] = 0; + if (!fgets(quick_buffer, sizeof(quick_buffer), f)) + return NULL; + + if (quick_buffer[sizeof(quick_buffer)-2] == 0) { + int n = strlen(quick_buffer); + if (n > 0 && quick_buffer[n-1] == '\n') + quick_buffer[n-1] = 0; + return strdup(quick_buffer); + } else { + char *p; + char *a = strdup(quick_buffer); + int len = sizeof(quick_buffer)-1; + + while (!feof(f)) { + if (a[len-1] == '\n') break; + a = (char *) realloc(a, len*2); + p = &a[len]; + p[len-2] = 0; + if (!fgets(p, len, f)) + break; + if (p[len-2] == 0) { + len += strlen(p); + break; + } + len = len + (len-1); + } + if (a[len-1] == '\n') + a[len-1] = 0; + return a; + } +} + +int stb_fullpath(char *abs, int abs_size, char *rel) +{ + #ifdef _MSC_VER + return _fullpath(abs, rel, abs_size) != NULL; + #else + if (rel[0] == '/' || rel[0] == '~') { + if ((int) strlen(rel) >= abs_size) + return 0; + strcpy(abs,rel); + return 1; + } else { + int n; + getcwd(abs, abs_size); + n = strlen(abs); + if (n+(int) strlen(rel)+2 <= abs_size) { + abs[n] = '/'; + strcpy(abs+n+1, rel); + return 1; + } else { + return 0; + } + } + #endif +} + +static int stb_fcmp_core(FILE *f, FILE *g) +{ + char buf1[1024],buf2[1024]; + int n1,n2, res=0; + + while (1) { + n1 = fread(buf1, 1, sizeof(buf1), f); + n2 = fread(buf2, 1, sizeof(buf2), g); + res = memcmp(buf1,buf2,n1 < n2 ? n1 : n2); + if (res) + break; + if (n1 != n2) { + res = n1 < n2 ? -1 : 1; + break; + } + if (n1 == 0) + break; + } + + fclose(f); + fclose(g); + return res; +} + +int stb_fcmp(char *s1, char *s2) +{ + FILE *f = stb__fopen(s1, "rb"); + FILE *g = stb__fopen(s2, "rb"); + + if (f == NULL || g == NULL) { + if (f) fclose(f); + if (g) { + fclose(g); + return 1; + } + return f != NULL; + } + + return stb_fcmp_core(f,g); +} + +int stb_feq(char *s1, char *s2) +{ + FILE *f = stb__fopen(s1, "rb"); + FILE *g = stb__fopen(s2, "rb"); + + if (f == NULL || g == NULL) { + if (f) fclose(f); + if (g) fclose(g); + return f == g; + } + + // feq is faster because it shortcuts if they're different length + if (stb_filelen(f) != stb_filelen(g)) { + fclose(f); + fclose(g); + return 0; + } + + return !stb_fcmp_core(f,g); +} + +int stb_copyfile(char *src, char *dest) +{ + char raw_buffer[1024]; + char *buffer; + int buf_size = 65536; + + FILE *f, *g; + + // if file already exists at destination, do nothing + if (stb_feq(src, dest)) return 1; + + // open file + f = stb__fopen(src, "rb"); + if (f == NULL) return 0; + + // open file for writing + g = stb__fopen(dest, "wb"); + if (g == NULL) { + fclose(f); + return 0; + } + + buffer = (char *) malloc(buf_size); + if (buffer == NULL) { + buffer = raw_buffer; + buf_size = sizeof(raw_buffer); + } + + while (!feof(f)) { + int n = fread(buffer, 1, buf_size, f); + if (n != 0) + fwrite(buffer, 1, n, g); + } + + fclose(f); + if (buffer != raw_buffer) + free(buffer); + + fclose(g); + return 1; +} + +#define stb_fgetc(f) ((unsigned char) fgetc(f)) + +#if 0 +// strip the trailing '/' or '\\' from a directory so we can refer to it +// as a file for _stat() +char *stb_strip_final_slash(char *t) +{ + if (t[0]) { + char *z = t + strlen(t) - 1; + // *z is the last character + if (*z == '\\' || *z == '/') + if (z != t+2 || t[1] != ':') // but don't strip it if it's e.g. "c:/" + *z = 0; + if (*z == '\\') + *z = '/'; // canonicalize to make sure it matches db + } + return t; +} + +char *stb_strip_final_slash_regardless(char *t) +{ + if (t[0]) { + char *z = t + strlen(t) - 1; + // *z is the last character + if (*z == '\\' || *z == '/') + *z = 0; + if (*z == '\\') + *z = '/'; // canonicalize to make sure it matches db + } + return t; +} +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Portable directory reading +// + +#ifndef STB_INCLUDE_STB_LIB_H +STB_EXTERN char **stb_readdir_files (char *dir); +STB_EXTERN char **stb_readdir_files_mask(char *dir, char *wild); +STB_EXTERN char **stb_readdir_subdirs(char *dir); +STB_EXTERN char **stb_readdir_subdirs_mask(char *dir, char *wild); +STB_EXTERN void stb_readdir_free (char **files); +STB_EXTERN char **stb_readdir_recursive(char *dir, char *filespec); +STB_EXTERN void stb_delete_directory_recursive(char *dir); + +// forward declare for implementation +STB_EXTERN int stb_wildmatchi(char *expr, char *candidate); +#endif // STB_INCLUDE_STB_LIB_H + + +#ifdef STB_LIB_IMPLEMENTATION + +#ifdef _MSC_VER +#include +#else +#include +#include +#endif + +void stb_readdir_free(char **files) +{ + char **f2 = files; + int i; + for (i=0; i < stb_arr_len(f2); ++i) + free(f2[i]); + stb_arr_free(f2); +} + +static int isdotdirname(char *name) +{ + if (name[0] == '.') + return (name[1] == '.') ? !name[2] : !name[1]; + return 0; +} + +static char **readdir_raw(char *dir, int return_subdirs, char *mask) +{ + char **results = NULL; + char buffer[4096], with_slash[4096]; + size_t n; + + #ifdef _MSC_VER + stb__wchar *ws; + struct _wfinddata_t data; + #ifdef _WIN64 + const intptr_t none = -1; + intptr_t z; + #else + const long none = -1; + long z; + #endif + #else // !_MSC_VER + const DIR *none = NULL; + DIR *z; + #endif + + n = stb_strscpy(buffer,dir,sizeof(buffer)); + if (!n || n >= sizeof(buffer)) + return NULL; + stb_fixpath(buffer); + n--; + + if (n > 0 && (buffer[n-1] != '/')) { + buffer[n++] = '/'; + } + buffer[n] = 0; + if (!stb_strscpy(with_slash,buffer,sizeof(with_slash))) + return NULL; + + #ifdef _MSC_VER + if (!stb_strscpy(buffer+n,"*.*",sizeof(buffer)-n)) + return NULL; + ws = stb__from_utf8(buffer); + z = _wfindfirst((const wchar_t *)ws, &data); + #else + z = opendir(dir); + #endif + + if (z != none) { + int nonempty = 1; + #ifndef _MSC_VER + struct dirent *data = readdir(z); + nonempty = (data != NULL); + #endif + + if (nonempty) { + + do { + int is_subdir; + #ifdef _MSC_VER + char *name = stb__to_utf8((stb__wchar *)data.name); + if (name == NULL) { + fprintf(stderr, "%s to convert '%S' to %s!\n", "Unable", data.name, "utf8"); + continue; + } + is_subdir = !!(data.attrib & _A_SUBDIR); + #else + char *name = data->d_name; + if (!stb_strscpy(buffer+n,name,sizeof(buffer)-n)) + break; + // Could follow DT_LNK, but would need to check for recursive links. + is_subdir = !!(data->d_type & DT_DIR); + #endif + + if (is_subdir == return_subdirs) { + if (!is_subdir || !isdotdirname(name)) { + if (!mask || stb_wildmatchi(mask, name)) { + char buffer[4096],*p=buffer; + if ( stb_snprintf(buffer, sizeof(buffer), "%s%s", with_slash, name) < 0 ) + break; + if (buffer[0] == '.' && buffer[1] == '/') + p = buffer+2; + stb_arr_push(results, strdup(p)); + } + } + } + } + #ifdef _MSC_VER + while (0 == _wfindnext(z, &data)); + #else + while ((data = readdir(z)) != NULL); + #endif + } + #ifdef _MSC_VER + _findclose(z); + #else + closedir(z); + #endif + } + return results; +} + +char **stb_readdir_files (char *dir) { return readdir_raw(dir, 0, NULL); } +char **stb_readdir_subdirs(char *dir) { return readdir_raw(dir, 1, NULL); } +char **stb_readdir_files_mask(char *dir, char *wild) { return readdir_raw(dir, 0, wild); } +char **stb_readdir_subdirs_mask(char *dir, char *wild) { return readdir_raw(dir, 1, wild); } + +int stb__rec_max=0x7fffffff; +static char **stb_readdir_rec(char **sofar, char *dir, char *filespec) +{ + char **files; + char ** dirs; + char **p; + + if (stb_arr_len(sofar) >= stb__rec_max) return sofar; + + files = stb_readdir_files_mask(dir, filespec); + stb_arr_for(p, files) { + stb_arr_push(sofar, strdup(*p)); + if (stb_arr_len(sofar) >= stb__rec_max) break; + } + stb_readdir_free(files); + if (stb_arr_len(sofar) >= stb__rec_max) return sofar; + + dirs = stb_readdir_subdirs(dir); + stb_arr_for(p, dirs) + sofar = stb_readdir_rec(sofar, *p, filespec); + stb_readdir_free(dirs); + return sofar; +} + +char **stb_readdir_recursive(char *dir, char *filespec) +{ + return stb_readdir_rec(NULL, dir, filespec); +} + +void stb_delete_directory_recursive(char *dir) +{ + char **list = stb_readdir_subdirs(dir); + int i; + for (i=0; i < stb_arr_len(list); ++i) + stb_delete_directory_recursive(list[i]); + stb_arr_free(list); + list = stb_readdir_files(dir); + for (i=0; i < stb_arr_len(list); ++i) + if (!remove(list[i])) { + // on windows, try again after making it writeable; don't ALWAYS + // do this first since that would be slow in the normal case + #ifdef _MSC_VER + _chmod(list[i], _S_IWRITE); + remove(list[i]); + #endif + } + stb_arr_free(list); + stb__windows(_rmdir,rmdir)(dir); +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Checksums: CRC-32, ADLER32, SHA-1 +// +// CRC-32 and ADLER32 allow streaming blocks +// SHA-1 requires either a complete buffer, max size 2^32 - 73 +// or it can checksum directly from a file, max 2^61 + +#ifndef STB_INCLUDE_STB_LIB_H +#define STB_ADLER32_SEED 1 +#define STB_CRC32_SEED 0 // note that we logical NOT this in the code + +STB_EXTERN stb_uint stb_adler32 (stb_uint adler32, stb_uchar *buffer, stb_uint buflen); +STB_EXTERN stb_uint stb_crc32_block(stb_uint crc32 , stb_uchar *buffer, stb_uint buflen); +STB_EXTERN stb_uint stb_crc32 ( stb_uchar *buffer, stb_uint buflen); + +STB_EXTERN void stb_sha1( unsigned char output[20], stb_uchar *buffer, unsigned int len); +STB_EXTERN int stb_sha1_file(unsigned char output[20], char *file); +#endif // STB_INCLUDE_STB_LIB_H + +#ifdef STB_LIB_IMPLEMENTATION +stb_uint stb_crc32_block(stb_uint crc, unsigned char *buffer, stb_uint len) +{ + static stb_uint crc_table[256]; + stb_uint i,j,s; + crc = ~crc; + + if (crc_table[1] == 0) + for(i=0; i < 256; i++) { + for (s=i, j=0; j < 8; ++j) + s = (s >> 1) ^ (s & 1 ? 0xedb88320 : 0); + crc_table[i] = s; + } + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +stb_uint stb_crc32(unsigned char *buffer, stb_uint len) +{ + return stb_crc32_block(0, buffer, len); +} + +stb_uint stb_adler32(stb_uint adler32, stb_uchar *buffer, stb_uint buflen) +{ + const unsigned long ADLER_MOD = 65521; + unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; + unsigned long blocklen, i; + + blocklen = buflen % 5552; + while (buflen) { + for (i=0; i + 7 < blocklen; i += 8) { + s1 += buffer[0], s2 += s1; + s1 += buffer[1], s2 += s1; + s1 += buffer[2], s2 += s1; + s1 += buffer[3], s2 += s1; + s1 += buffer[4], s2 += s1; + s1 += buffer[5], s2 += s1; + s1 += buffer[6], s2 += s1; + s1 += buffer[7], s2 += s1; + + buffer += 8; + } + + for (; i < blocklen; ++i) + s1 += *buffer++, s2 += s1; + + s1 %= ADLER_MOD, s2 %= ADLER_MOD; + buflen -= blocklen; + blocklen = 5552; + } + return (s2 << 16) + s1; +} + +#define stb__big32(c) (((c)[0]<<24) + (c)[1]*65536 + (c)[2]*256 + (c)[3]) +static void stb__sha1(stb_uchar *chunk, stb_uint h[5]) +{ + int i; + stb_uint a,b,c,d,e; + stb_uint w[80]; + + for (i=0; i < 16; ++i) + w[i] = stb__big32(&chunk[i*4]); + for (i=16; i < 80; ++i) { + stb_uint t; + t = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]; + w[i] = (t + t) | (t >> 31); + } + + a = h[0]; + b = h[1]; + c = h[2]; + d = h[3]; + e = h[4]; + + #define STB__SHA1(k,f) \ + { \ + stb_uint temp = (a << 5) + (a >> 27) + (f) + e + (k) + w[i]; \ + e = d; \ + d = c; \ + c = (b << 30) + (b >> 2); \ + b = a; \ + a = temp; \ + } + + i=0; + for (; i < 20; ++i) STB__SHA1(0x5a827999, d ^ (b & (c ^ d)) ); + for (; i < 40; ++i) STB__SHA1(0x6ed9eba1, b ^ c ^ d ); + for (; i < 60; ++i) STB__SHA1(0x8f1bbcdc, (b & c) + (d & (b ^ c)) ); + for (; i < 80; ++i) STB__SHA1(0xca62c1d6, b ^ c ^ d ); + + #undef STB__SHA1 + + h[0] += a; + h[1] += b; + h[2] += c; + h[3] += d; + h[4] += e; +} + +void stb_sha1(stb_uchar output[20], stb_uchar *buffer, stb_uint len) +{ + unsigned char final_block[128]; + stb_uint end_start, final_len, j; + int i; + + stb_uint h[5]; + + h[0] = 0x67452301; + h[1] = 0xefcdab89; + h[2] = 0x98badcfe; + h[3] = 0x10325476; + h[4] = 0xc3d2e1f0; + + // we need to write padding to the last one or two + // blocks, so build those first into 'final_block' + + // we have to write one special byte, plus the 8-byte length + + // compute the block where the data runs out + end_start = len & ~63; + + // compute the earliest we can encode the length + if (((len+9) & ~63) == end_start) { + // it all fits in one block, so fill a second-to-last block + end_start -= 64; + } + + final_len = end_start + 128; + + // now we need to copy the data in + assert(end_start + 128 >= len+9); + assert(end_start < len || len < 64-9); + + j = 0; + if (end_start > len) + j = (stb_uint) - (int) end_start; + + for (; end_start + j < len; ++j) + final_block[j] = buffer[end_start + j]; + final_block[j++] = 0x80; + while (j < 128-5) // 5 byte length, so write 4 extra padding bytes + final_block[j++] = 0; + // big-endian size + final_block[j++] = len >> 29; + final_block[j++] = len >> 21; + final_block[j++] = len >> 13; + final_block[j++] = len >> 5; + final_block[j++] = len << 3; + assert(j == 128 && end_start + j == final_len); + + for (j=0; j < final_len; j += 64) { // 512-bit chunks + if (j+64 >= end_start+64) + stb__sha1(&final_block[j - end_start], h); + else + stb__sha1(&buffer[j], h); + } + + for (i=0; i < 5; ++i) { + output[i*4 + 0] = h[i] >> 24; + output[i*4 + 1] = h[i] >> 16; + output[i*4 + 2] = h[i] >> 8; + output[i*4 + 3] = h[i] >> 0; + } +} + +int stb_sha1_file(stb_uchar output[20], char *file) +{ + int i; + stb_uint64 length=0; + unsigned char buffer[128]; + + FILE *f = stb__fopen(file, "rb"); + stb_uint h[5]; + + if (f == NULL) return 0; // file not found + + h[0] = 0x67452301; + h[1] = 0xefcdab89; + h[2] = 0x98badcfe; + h[3] = 0x10325476; + h[4] = 0xc3d2e1f0; + + for(;;) { + int n = fread(buffer, 1, 64, f); + if (n == 64) { + stb__sha1(buffer, h); + length += n; + } else { + int block = 64; + + length += n; + + buffer[n++] = 0x80; + + // if there isn't enough room for the length, double the block + if (n + 8 > 64) + block = 128; + + // pad to end + memset(buffer+n, 0, block-8-n); + + i = block - 8; + buffer[i++] = (stb_uchar) (length >> 53); + buffer[i++] = (stb_uchar) (length >> 45); + buffer[i++] = (stb_uchar) (length >> 37); + buffer[i++] = (stb_uchar) (length >> 29); + buffer[i++] = (stb_uchar) (length >> 21); + buffer[i++] = (stb_uchar) (length >> 13); + buffer[i++] = (stb_uchar) (length >> 5); + buffer[i++] = (stb_uchar) (length << 3); + assert(i == block); + stb__sha1(buffer, h); + if (block == 128) + stb__sha1(buffer+64, h); + else + assert(block == 64); + break; + } + } + fclose(f); + + for (i=0; i < 5; ++i) { + output[i*4 + 0] = h[i] >> 24; + output[i*4 + 1] = h[i] >> 16; + output[i*4 + 2] = h[i] >> 8; + output[i*4 + 3] = h[i] >> 0; + } + + return 1; +} +#endif // STB_LIB_IMPLEMENTATION + +////////////////////////////////////////////////////////////////////////////// +// +// Random Numbers via Meresenne Twister or LCG +// + +#ifndef STB_INCLUDE_STB_LIB_H +STB_EXTERN unsigned long stb_srandLCG(unsigned long seed); +STB_EXTERN unsigned long stb_randLCG(void); +STB_EXTERN double stb_frandLCG(void); + +STB_EXTERN void stb_srand(unsigned long seed); +STB_EXTERN unsigned long stb_rand(void); +STB_EXTERN double stb_frand(void); +STB_EXTERN void stb_shuffle(void *p, size_t n, size_t sz, + unsigned long seed); +STB_EXTERN void stb_reverse(void *p, size_t n, size_t sz); + +STB_EXTERN unsigned long stb_randLCG_explicit(unsigned long seed); +#endif // STB_INCLUDE_STB_LIB_H + +#ifdef STB_LIB_IMPLEMENTATION +unsigned long stb_randLCG_explicit(unsigned long seed) +{ + return seed * 2147001325 + 715136305; +} + +static unsigned long stb__rand_seed=0; + +unsigned long stb_srandLCG(unsigned long seed) +{ + unsigned long previous = stb__rand_seed; + stb__rand_seed = seed; + return previous; +} + +unsigned long stb_randLCG(void) +{ + stb__rand_seed = stb__rand_seed * 2147001325 + 715136305; // BCPL generator + // shuffle non-random bits to the middle, and xor to decorrelate with seed + return 0x31415926 ^ ((stb__rand_seed >> 16) + (stb__rand_seed << 16)); +} + +double stb_frandLCG(void) +{ + return stb_randLCG() / ((double) (1 << 16) * (1 << 16)); +} + +void stb_shuffle(void *p, size_t n, size_t sz, unsigned long seed) +{ + char *a; + unsigned long old_seed; + int i; + if (seed) + old_seed = stb_srandLCG(seed); + a = (char *) p + (n-1) * sz; + + for (i=n; i > 1; --i) { + int j = stb_randLCG() % i; + stb_swap(a, (char *) p + j * sz, sz); + a -= sz; + } + if (seed) + stb_srandLCG(old_seed); +} + +void stb_reverse(void *p, size_t n, size_t sz) +{ + int i,j = n-1; + for (i=0; i < j; ++i,--j) { + stb_swap((char *) p + i * sz, (char *) p + j * sz, sz); + } +} + +// public domain Mersenne Twister by Michael Brundage +#define STB__MT_LEN 624 + +int stb__mt_index = STB__MT_LEN*sizeof(unsigned long)+1; +unsigned long stb__mt_buffer[STB__MT_LEN]; + +void stb_srand(unsigned long seed) +{ + int i; + unsigned long old = stb_srandLCG(seed); + for (i = 0; i < STB__MT_LEN; i++) + stb__mt_buffer[i] = stb_randLCG(); + stb_srandLCG(old); + stb__mt_index = STB__MT_LEN*sizeof(unsigned long); +} + +#define STB__MT_IA 397 +#define STB__MT_IB (STB__MT_LEN - STB__MT_IA) +#define STB__UPPER_MASK 0x80000000 +#define STB__LOWER_MASK 0x7FFFFFFF +#define STB__MATRIX_A 0x9908B0DF +#define STB__TWIST(b,i,j) ((b)[i] & STB__UPPER_MASK) | ((b)[j] & STB__LOWER_MASK) +#define STB__MAGIC(s) (((s)&1)*STB__MATRIX_A) + +unsigned long stb_rand() +{ + unsigned long * b = stb__mt_buffer; + int idx = stb__mt_index; + unsigned long s,r; + int i; + + if (idx >= STB__MT_LEN*sizeof(unsigned long)) { + if (idx > STB__MT_LEN*sizeof(unsigned long)) + stb_srand(0); + idx = 0; + i = 0; + for (; i < STB__MT_IB; i++) { + s = STB__TWIST(b, i, i+1); + b[i] = b[i + STB__MT_IA] ^ (s >> 1) ^ STB__MAGIC(s); + } + for (; i < STB__MT_LEN-1; i++) { + s = STB__TWIST(b, i, i+1); + b[i] = b[i - STB__MT_IB] ^ (s >> 1) ^ STB__MAGIC(s); + } + + s = STB__TWIST(b, STB__MT_LEN-1, 0); + b[STB__MT_LEN-1] = b[STB__MT_IA-1] ^ (s >> 1) ^ STB__MAGIC(s); + } + stb__mt_index = idx + sizeof(unsigned long); + + r = *(unsigned long *)((unsigned char *)b + idx); + + r ^= (r >> 11); + r ^= (r << 7) & 0x9D2C5680; + r ^= (r << 15) & 0xEFC60000; + r ^= (r >> 18); + + return r; +} + +double stb_frand(void) +{ + return stb_rand() / ((double) (1 << 16) * (1 << 16)); +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// wildcards and regexping +// + +#ifndef STB_INCLUDE_STB_LIB_H +STB_EXTERN int stb_wildmatch (char *expr, char *candidate); +STB_EXTERN int stb_wildmatchi(char *expr, char *candidate); +STB_EXTERN int stb_wildfind (char *expr, char *candidate); +STB_EXTERN int stb_wildfindi (char *expr, char *candidate); +#endif // STB_INCLUDE_STB_LIB_H + +#ifdef STB_LIB_IMPLEMENTATION +static int stb__match_qstring(char *candidate, char *qstring, int qlen, int insensitive) +{ + int i; + if (insensitive) { + for (i=0; i < qlen; ++i) + if (qstring[i] == '?') { + if (!candidate[i]) return 0; + } else + if (tolower(qstring[i]) != tolower(candidate[i])) + return 0; + } else { + for (i=0; i < qlen; ++i) + if (qstring[i] == '?') { + if (!candidate[i]) return 0; + } else + if (qstring[i] != candidate[i]) + return 0; + } + return 1; +} + +static int stb__find_qstring(char *candidate, char *qstring, int qlen, int insensitive) +{ + char c; + + int offset=0; + while (*qstring == '?') { + ++qstring; + --qlen; + ++candidate; + if (qlen == 0) return 0; + if (*candidate == 0) return -1; + } + + c = *qstring++; + --qlen; + if (insensitive) c = tolower(c); + + while (candidate[offset]) { + if (c == (insensitive ? tolower(candidate[offset]) : candidate[offset])) + if (stb__match_qstring(candidate+offset+1, qstring, qlen, insensitive)) + return offset; + ++offset; + } + + return -1; +} + +int stb__wildmatch_raw2(char *expr, char *candidate, int search, int insensitive) +{ + int where=0; + int start = -1; + + if (!search) { + // parse to first '*' + if (*expr != '*') + start = 0; + while (*expr != '*') { + if (!*expr) + return *candidate == 0 ? 0 : -1; + if (*expr == '?') { + if (!*candidate) return -1; + } else { + if (insensitive) { + if (tolower(*candidate) != tolower(*expr)) + return -1; + } else + if (*candidate != *expr) + return -1; + } + ++candidate, ++expr, ++where; + } + } else { + // 0-length search string + if (!*expr) + return 0; + } + + assert(search || *expr == '*'); + if (!search) + ++expr; + + // implicit '*' at this point + + while (*expr) { + int o=0; + // combine redundant * characters + while (expr[0] == '*') ++expr; + + // ok, at this point, expr[-1] == '*', + // and expr[0] != '*' + + if (!expr[0]) return start >= 0 ? start : 0; + + // now find next '*' + o = 0; + while (expr[o] != '*') { + if (expr[o] == 0) + break; + ++o; + } + // if no '*', scan to end, then match at end + if (expr[o] == 0 && !search) { + int z; + for (z=0; z < o; ++z) + if (candidate[z] == 0) + return -1; + while (candidate[z]) + ++z; + // ok, now check if they match + if (stb__match_qstring(candidate+z-o, expr, o, insensitive)) + return start >= 0 ? start : 0; + return -1; + } else { + // if yes '*', then do stb__find_qmatch on the intervening chars + int n = stb__find_qstring(candidate, expr, o, insensitive); + if (n < 0) + return -1; + if (start < 0) + start = where + n; + expr += o; + candidate += n+o; + } + + if (*expr == 0) { + assert(search); + return start; + } + + assert(*expr == '*'); + ++expr; + } + + return start >= 0 ? start : 0; +} + +int stb__wildmatch_raw(char *expr, char *candidate, int search, int insensitive) +{ + char buffer[256]; + // handle multiple search strings + char *s = strchr(expr, ';'); + char *last = expr; + while (s) { + int z; + // need to allow for non-writeable strings... assume they're small + if (s - last < 256) { + stb_strncpy(buffer, last, s-last+1); + z = stb__wildmatch_raw2(buffer, candidate, search, insensitive); + } else { + *s = 0; + z = stb__wildmatch_raw2(last, candidate, search, insensitive); + *s = ';'; + } + if (z >= 0) return z; + last = s+1; + s = strchr(last, ';'); + } + return stb__wildmatch_raw2(last, candidate, search, insensitive); +} + +int stb_wildmatch(char *expr, char *candidate) +{ + return stb__wildmatch_raw(expr, candidate, 0,0) >= 0; +} + +int stb_wildmatchi(char *expr, char *candidate) +{ + return stb__wildmatch_raw(expr, candidate, 0,1) >= 0; +} + +int stb_wildfind(char *expr, char *candidate) +{ + return stb__wildmatch_raw(expr, candidate, 1,0); +} + +int stb_wildfindi(char *expr, char *candidate) +{ + return stb__wildmatch_raw(expr, candidate, 1,1); +} + +#undef STB_LIB_IMPLEMENTATION +#endif // STB_LIB_IMPLEMENTATION + +#ifndef STB_INCLUDE_STB_LIB_H +#define STB_INCLUDE_STB_LIB_H +#undef STB_EXTERN +#endif + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +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/tests/stb.dsp b/tests/stb.dsp index 7e20a14..5b870c7 100644 --- a/tests/stb.dsp +++ b/tests/stb.dsp @@ -66,7 +66,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "VORBIS_TEST" /FR /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "TT_TEST" /FR /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -190,6 +190,34 @@ SOURCE=.\test_c_compilation.c # End Source File # Begin Source File +SOURCE=.\test_c_lexer.c +# End Source File +# Begin Source File + +SOURCE=.\test_dxt.c +# End Source File +# Begin Source File + +SOURCE=.\test_easyfont.c +# End Source File +# Begin Source File + +SOURCE=.\test_image.c +# End Source File +# Begin Source File + +SOURCE=.\test_image_write.c +# End Source File +# Begin Source File + +SOURCE=.\test_perlin.c +# End Source File +# Begin Source File + +SOURCE=.\test_sprintf.c +# End Source File +# Begin Source File + SOURCE=.\test_truetype.c # End Source File # Begin Source File @@ -198,6 +226,10 @@ SOURCE=.\test_vorbis.c # End Source File # Begin Source File +SOURCE=.\test_voxel.c +# End Source File +# Begin Source File + SOURCE=.\textedit_sample.c # End Source File # End Target diff --git a/tests/stb.dsw b/tests/stb.dsw index 45a52b6..ef3c43e 100644 --- a/tests/stb.dsw +++ b/tests/stb.dsw @@ -123,6 +123,18 @@ Package=<4> ############################################################################### +Project: "stblib"=.\stblib.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + Project: "stretch_test"=.\stretch_test.dsp - Package Owner=<4> Package=<5> diff --git a/tests/stblib.dsp b/tests/stblib.dsp new file mode 100644 index 0000000..c43513d --- /dev/null +++ b/tests/stblib.dsp @@ -0,0 +1,102 @@ +# Microsoft Developer Studio Project File - Name="stblib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=stblib - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "stblib.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "stblib.mak" CFG="stblib - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "stblib - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "stblib - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "stblib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "stblib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "stblib - Win32 Release" +# Name "stblib - Win32 Debug" +# Begin Source File + +SOURCE=.\prerelease\stb_lib.h +# End Source File +# Begin Source File + +SOURCE=..\stb_regex.h +# End Source File +# Begin Source File + +SOURCE=.\stblib_test.c +# End Source File +# Begin Source File + +SOURCE=.\stblib_test_companion.c +# End Source File +# End Target +# End Project diff --git a/tests/stblib_test.c b/tests/stblib_test.c new file mode 100644 index 0000000..9f2e176 --- /dev/null +++ b/tests/stblib_test.c @@ -0,0 +1,11 @@ +#include "prerelease/stb_lib.h" +#define STB_LIB_IMPLEMENTATION +#include "prerelease/stb_lib.h" + +//#define STB_REGEX_IMPLEMENTATION +//#include "stb_regex.h" + +int main(int argc, char **argv) +{ + +} \ No newline at end of file diff --git a/tests/stblib_test_companion.c b/tests/stblib_test_companion.c new file mode 100644 index 0000000..88f233c --- /dev/null +++ b/tests/stblib_test_companion.c @@ -0,0 +1,4 @@ +//#include "stb_regex.h" +//#include "stb_regex.h" +#include "prerelease/stb_lib.h" +#include "prerelease/stb_lib.h" diff --git a/tests/test_c_compilation.c b/tests/test_c_compilation.c index 2a54df5..40bc5a4 100644 --- a/tests/test_c_compilation.c +++ b/tests/test_c_compilation.c @@ -1,10 +1,8 @@ -#include "stb_sprintf.h" #define STB_SPRINTF_IMPLEMENTATION #include "stb_sprintf.h" #define STB_PERLIN_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION -#define STB_DXT_IMPLEMENATION #define STB_C_LEXER_IMPLEMENTATIOn #define STB_DIVIDE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION @@ -14,17 +12,18 @@ #define STB_VOXEL_RENDER_IMPLEMENTATION #define STB_EASY_FONT_IMPLEMENTATION -#include "stb_easy_font.h" #include "stb_herringbone_wang_tile.h" #include "stb_image.h" #include "stb_image_write.h" #include "stb_perlin.h" -#include "stb_dxt.h" #include "stb_c_lexer.h" #include "stb_divide.h" #include "stb_image_resize.h" #include "stb_rect_pack.h" +#define STB_DXT_IMPLEMENTATION +#include "stb_dxt.h" + #define STBVOX_CONFIG_MODE 1 #include "stb_voxel_render.h" diff --git a/tests/test_c_lexer.c b/tests/test_c_lexer.c new file mode 100644 index 0000000..579ed5d --- /dev/null +++ b/tests/test_c_lexer.c @@ -0,0 +1 @@ +#include "stb_c_lexer.h" diff --git a/tests/test_cpp_compilation.cpp b/tests/test_cpp_compilation.cpp index 138948b..96ecf9d 100644 --- a/tests/test_cpp_compilation.cpp +++ b/tests/test_cpp_compilation.cpp @@ -2,9 +2,11 @@ #define STB_SPRINTF_IMPLEMENTATION #include "stb_sprintf.h" +#define STB_IMAGE_WRITE_STATIC + +#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_TRUETYPE_IMPLEMENTATION #define STB_PERLIN_IMPLEMENTATION -#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_DXT_IMPLEMENATION #define STB_C_LEXER_IMPLEMENTATIOn #define STB_DIVIDE_IMPLEMENTATION diff --git a/tests/test_dxt.c b/tests/test_dxt.c new file mode 100644 index 0000000..a1ef29d --- /dev/null +++ b/tests/test_dxt.c @@ -0,0 +1 @@ +#include "stb_dxt.h" diff --git a/tests/test_easyfont.c b/tests/test_easyfont.c new file mode 100644 index 0000000..70f284b --- /dev/null +++ b/tests/test_easyfont.c @@ -0,0 +1 @@ +#include "stb_easy_font.h" diff --git a/tests/test_image.c b/tests/test_image.c new file mode 100644 index 0000000..e23d4a0 --- /dev/null +++ b/tests/test_image.c @@ -0,0 +1 @@ +#include "stb_image.h" \ No newline at end of file diff --git a/tests/test_image_write.c b/tests/test_image_write.c new file mode 100644 index 0000000..cf23025 --- /dev/null +++ b/tests/test_image_write.c @@ -0,0 +1 @@ +#include "stb_image_write.h" \ No newline at end of file diff --git a/tests/test_perlin.c b/tests/test_perlin.c new file mode 100644 index 0000000..de702c9 --- /dev/null +++ b/tests/test_perlin.c @@ -0,0 +1 @@ +#include "stb_perlin.h" \ No newline at end of file diff --git a/tests/test_sprintf.c b/tests/test_sprintf.c new file mode 100644 index 0000000..078faa8 --- /dev/null +++ b/tests/test_sprintf.c @@ -0,0 +1 @@ +#include "stb_sprintf.h" \ No newline at end of file diff --git a/tests/test_truetype.c b/tests/test_truetype.c index d18d3b5..1ac147b 100644 --- a/tests/test_truetype.c +++ b/tests/test_truetype.c @@ -1,3 +1,11 @@ +#ifndef _CRT_SECURE_NO_WARNINGS +// Fixes Compile Errors for Visual Studio 2005 or newer + #define _CRT_SECURE_NO_WARNINGS +#endif + +#include + +// this isn't meant to compile standalone; link with test_c_compilation.c as well #include "stb_rect_pack.h" #define STB_TRUETYPE_IMPLEMENTATION #include "stb_truetype.h" @@ -7,7 +15,7 @@ #include -char ttf_buffer[1<<25]; +unsigned char ttf_buffer[1 << 25]; unsigned char output[512*100]; void debug(void) @@ -66,12 +74,14 @@ int main(int argc, char **argv) stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL); pr[0].chardata_for_range = pdata; - pr[0].first_unicode_char_in_range = 32; - pr[0].num_chars_in_range = 95; + pr[0].array_of_unicode_codepoints = NULL; + pr[0].first_unicode_codepoint_in_range = 32; + pr[0].num_chars = 95; pr[0].font_size = 20.0f; pr[1].chardata_for_range = pdata+256; - pr[1].first_unicode_char_in_range = 0xa0; - pr[1].num_chars_in_range = 0x100 - 0xa0; + pr[1].array_of_unicode_codepoints = NULL; + pr[1].first_unicode_codepoint_in_range = 0xa0; + pr[1].num_chars = 0x100 - 0xa0; pr[1].font_size = 20.0f; stbtt_PackSetOversampling(&pc, 2, 2); diff --git a/tests/test_voxel.c b/tests/test_voxel.c new file mode 100644 index 0000000..e177b6f --- /dev/null +++ b/tests/test_voxel.c @@ -0,0 +1 @@ +#include "stb_voxel_render.h" \ No newline at end of file