diff --git a/README.md b/README.md index eec9e98..6694d03 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,34 @@ + + stb === single-file public domain libraries for C/C++ -library | lastest version | category | description ---------------------- | ---- | -------- | -------------------------------- -**stb_vorbis.c** | 1.04 | audio | decode ogg vorbis files from file/memory to float/16-bit signed output -**stb_image.h** | 2.03 | graphics | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC -**stb_truetype.h** | 1.03 | graphics | parse, decode, and rasterize characters from truetype fonts -**stb_image_write.h** | 0.98 | graphics | image writing to disk: PNG, TGA, BMP -**stb_image_resize.h** | 0.90 | graphics | resize images larger/smaller with good quality -**stb_rect_pack.h** | 0.05 | graphics | simple 2D rectangle packer with decent quality -**stretchy_buffer.h** | 1.01 | utility | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++ -**stb_textedit.h** | 1.5 | UI | guts of a text editor for games etc implementing them from scratch -**stb_voxel_render.h** | 0.80 | 3D graphics | Minecraft-esque voxel rendering "engine" with many more features -**stb_dxt.h** | 1.04 | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor -**stb_perlin.h** | 0.2 | 3D graphics | revised Perlin noise (3D input, 1D output) -**stb_easy_font.h** | 0.5 | 3D graphics | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc -**stb_tilemap_editor.h** | 0.30 | game development | embeddable tilemap editor -**stb_herringbone_wang_tile.h** | 0.6 | game development | herringbone Wang tile map generator -**stb_c_lexer.h** | 0.06 | parsing | simplify writing parsers for C-like languages -**stb_divide.h** | 0.91 | math | more useful 32-bit modulus e.g. "euclidean divide" -**stb.h** | 2.24 | misc | helper functions for C, mostly redundant in C++; basically author's personal stuff -**stb_leakcheck.h** | 0.1 | misc | quick-and-dirty malloc/free leak-checking +library | lastest version | category | LoC | description +--------------------- | ---- | -------- | --- | -------------------------------- +**stb_vorbis.c** | 1.05 | audio | 5445 | decode ogg vorbis files from file/memory to float/16-bit signed output +**stb_image.h** | 2.06 | graphics | 6437 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC +**stb_truetype.h** | 1.06 | graphics | 2632 | parse, decode, and rasterize characters from truetype fonts +**stb_image_write.h** | 0.98 | graphics | 730 | image writing to disk: PNG, TGA, BMP +**stb_image_resize.h** | 0.90 | graphics | 2585 | resize images larger/smaller with good quality +**stb_rect_pack.h** | 0.06 | graphics | 560 | simple 2D rectangle packer with decent quality +**stretchy_buffer.h** | 1.02 | utility | 210 | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++ +**stb_textedit.h** | 1.6 | UI | 1290 | guts of a text editor for games etc implementing them from scratch +**stb_voxel_render.h** | 0.81 | 3D graphics | 3644 | Minecraft-esque voxel rendering "engine" with many more features +**stb_dxt.h** | 1.04 | 3D graphics | 624 | Fabian "ryg" Giesen's real-time DXT compressor +**stb_perlin.h** | 0.2 | 3D graphics | 175 | revised Perlin noise (3D input, 1D output) +**stb_easy_font.h** | 0.5 | 3D graphics | 220 | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc +**stb_tilemap_editor.h** | 0.35 | game dev | 4120 | embeddable tilemap editor +**stb_herringbone_wa...** | 0.6 | game dev | 1217 | herringbone Wang tile map generator +**stb_c_lexer.h** | 0.06 | parsing | 809 | simplify writing parsers for C-like languages +**stb_divide.h** | 0.91 | math | 373 | more useful 32-bit modulus e.g. "euclidean divide" +**stb.h** | 2.24 | misc | 14086 | helper functions for C, mostly redundant in C++; basically author's personal stuff +**stb_leakcheck.h** | 0.2 | misc | 117 | quick-and-dirty malloc/free leak-checking + +Total libraries: 18 +Total lines of C code: 45274 + FAQ --- @@ -45,6 +51,17 @@ attribution requirement). They may be less featureful, slower, and/or use more memory. If you're already using an equivalent library, there's probably no good reason to switch. +#### Why do you list "lines of code"? It's a terrible metric. + +Just to give you some idea of the internal complexity of the library, +to help you manage your expectations, or to let you know what you're +getting into. While not all the libraries are written in the same +style, they're certainly similar styles, and so comparisons between +the libraries are probably still meaningful. + +Note though that the lines do include both the implementation, the +part that corresponds to a header file, and the documentation. + #### Why single-file headers? Windows doesn't have standard directories where libraries @@ -52,12 +69,21 @@ live. That makes deploying libraries in Windows a lot more painful than open source developers on Unix-derivates generally realize. (It also makes library dependencies a lot worse in Windows.) +There's also a common problem in Windows where a library was built +against a different version of the runtime library, which causes +link conflicts and confusion. Shipping the libs as headers means +you normally just compile them straight into your project without +making libraries, thus sidestepping that problem. + Making them a single file makes it very easy to just drop them into a project that needs them. (Of course you can still put them in a proper shared library tree if you want.) +Why not two files, one a header and one an implementation? The difference between 10 files and 9 files is not a big deal, but the difference between 2 files and 1 file is a big deal. +You don't need to zip or tar the files up, you don't have to +remember to attach *two* files, etc. #### Why "stb"? Is this something to do with Set-Top Boxes? @@ -83,12 +109,9 @@ Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt #### Why public domain? -Because more people will use it. Because it's not viral, people -are not obligated to give back, so you could argue that it hurts -the *development* of it, and then because it doesn't develop as -well it's not as good, and then because it's not as good, in the -long run maybe fewer people will use it. I have total respect for -that opinion, but I just don't believe it myself for most software. +I prefer it over GPL, LGPL, BSD, zlib, etc. for many reasons. +Some of them are listed here: +https://github.com/nothings/stb/blob/master/docs/why_public_domain.md #### Why C? diff --git a/docs/why_public_domain.md b/docs/why_public_domain.md new file mode 100644 index 0000000..8636921 --- /dev/null +++ b/docs/why_public_domain.md @@ -0,0 +1,116 @@ +My collected rationales for placing these libraries +in the public domain: + +1. Public domain vs. viral licenses + + Why is this library public domain? + Because more people will use it. Because it's not viral, people are + not obligated to give back, so you could argue that it hurts the + development of it, and then because it doesn't develop as well it's + not as good, and then because it's not as good, in the long run + maybe fewer people will use it. I have total respect for that + opinion, but I just don't believe it myself for most software. + +2. Public domain vs. attribution-required licenses + + The primary difference between public domain and, say, a Creative Commons + commercial / non-share-alike / attribution license is solely the + requirement for attribution. (Similarly the BSD license and such.) + While I would *appreciate* acknowledgement and attribution, I believe + that it is foolish to place a legal encumberment (i.e. a license) on + the software *solely* to get attribution. + + In other words, I'm arguing that PD is superior to the BSD license and + the Creative Commons 'Attribution' license. If the license offers + anything besides attribution -- as does, e.g., CC NonCommercial-ShareAlike, + or the GPL -- that's a separate discussion. + +3. Other aspects of BSD-style licenses besides attribution + + Permissive licenses like zlib and BSD license are perfectly reasonable + in their requirements, but they are very wordy and + have only two benefits over public domain: legally-mandated + attribution and liability-control. I do not believe these + are worth the excessive verbosity and user-unfriendliness + these licenses induce, especially in the single-file + case where those licenses tend to be at the top of + the file, the first thing you see. + + To the specific points, I have had no trouble receiving + attribution for my libraries; liability in the face of + no explicit disclaimer of liability is an open question, + but one I have a lot of difficulty imagining there being + any actual doubt about in court. Sometimes I explicitly + note in my libraries that I make no guarantees about them + being fit for purpose, but it's pretty absurd to do this; + as a whole, it comes across as "here is a library to decode + vorbis audio files, but it may not actually work and if + you have problems it's not my fault, but also please + report bugs so I can fix them"--so dumb! + +4. full discussion from stb_howto.txt on what YOU should do for YOUR libs + +``` +EASY-TO-COMPLY LICENSE + +I make my libraries public domain. You don't have to. +But my goal in releasing stb-style libraries is to +reduce friction for potential users as much as +possible. That means: + + a. easy to build (what this file is mostly about) + b. easy to invoke (which requires good API design) + c. easy to deploy (which is about licensing) + +I choose to place all my libraries in the public +domain, abjuring copyright, rather than license +the libraries. This has some benefits and some +drawbacks. + +Any license which is "viral" to modifications +causes worries for lawyers, even if their programmers +aren't modifying it. + +Any license which requires crediting in documentation +adds friction which can add up. Valve used to have +a page with a list of all of these on their web site, +and it was insane, and obviously nobody ever looked +at it so why would you care whether your credit appeared +there? + +Permissive licenses like zlib and BSD license are +perfectly reasonable, but they are very wordy and +have only two benefits over public domain: legally-mandated +attribution and liability-control. I do not believe these +are worth the excessive verbosity and user-unfriendliness +these licenses induce, especially in the single-file +case where those licenses tend to be at the top of +the file, the first thing you see. (To the specific +points, I have had no trouble receiving attribution +for my libraries; liability in the face of no explicit +disclaimer of liability is an open question.) + +However, public domain has frictions of its own, because +public domain declarations aren't necessary recognized +in the USA and some other locations. For that reason, +I recommend a declaration along these lines: + +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy +// and modify this file as you see fit. + +I typically place this declaration at the end of the initial +comment block of the file and just say 'public domain' +at the top. + +I have had people say they couldn't use one of my +libraries because it was only "public domain" and didn't +have the additional fallback clause, who asked if +I could dual-license it under a traditional license. + +My answer: they can create a derivative work by +modifying one character, and then license that however +they like. (Indeed, *adding* the zlib or BSD license +would be such a modification!) Unfortunately, their +lawyers reportedly didn't like that answer. :( +``` diff --git a/stb.h b/stb.h index d78c465..671ffcd 100644 --- a/stb.h +++ b/stb.h @@ -5255,7 +5255,7 @@ int stb_fullpath(char *abs, int abs_size, char *rel) #ifdef _MSC_VER return _fullpath(abs, rel, abs_size) != NULL; #else - if (abs[0] == '/' || abs[0] == '~') { + if (rel[0] == '/' || rel[0] == '~') { if ((int) strlen(rel) >= abs_size) return 0; strcpy(abs,rel); diff --git a/stb_image.h b/stb_image.h index c00f346..d0fa9c2 100644 --- a/stb_image.h +++ b/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.03 - public domain image loader - http://nothings.org/stb_image.h +/* stb_image - v2.06 - public domain image loader - http://nothings.org/stb_image.h no warranty implied; use at your own risk Do this: @@ -143,6 +143,9 @@ Latest revision history: + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit 2.03 (2015-04-12) additional corruption checking stbi_set_flip_vertically_on_load fix NEON support; fix mingw support @@ -159,8 +162,6 @@ 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) optimize PNG fix bug in interlaced PNG with user-specified channel count - 1.46 (2014-08-26) fix broken tRNS chunk in non-paletted PNG - 1.45 (2014-08-16) workaround MSVC-ARM internal compiler error by wrapping malloc See end of file for full revision history. @@ -633,11 +634,14 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #endif // x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86) +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) #define STBI__X86_TARGET #endif -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// NOTE: not clear do we actually need this for the 64-bit path? // gcc doesn't support sse2 intrinsics unless you compile with -msse2, // (but compiling with -msse2 allows the compiler to use SSE2 everywhere; // this is just broken and gcc are jerks for not fixing it properly @@ -646,6 +650,8 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #endif #if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// // 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the // Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. // As a result, enabling SSE2 on 32-bit MinGW is dangerous when not @@ -1640,7 +1646,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < sizeof(stbi__bmask)/sizeof(*stbi__bmask)); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; @@ -1846,8 +1852,11 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ if (r) j->eob_run += stbi__jpeg_get_bits(j, r); r = 64; // force end of block - } else - r = 16; // r=15 is the code for 16 0s + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } } else { if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); // sign bit @@ -1859,7 +1868,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ // advance by r while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k]]; + short *p = &data[stbi__jpeg_dezigzag[k++]]; if (*p != 0) { if (stbi__jpeg_get_bit(j)) if ((*p & bit)==0) { @@ -1868,15 +1877,12 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ else *p -= bit; } - ++k; } else { if (r == 0) { - if (s) - data[stbi__jpeg_dezigzag[k++]] = (short) s; + *p = (short) s; break; } --r; - ++k; } } } while (k <= j->spec_end); @@ -5134,7 +5140,8 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int p = out+channel; if (channel >= channelCount) { // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); } else { // Read the RLE data. count = 0; @@ -5180,11 +5187,12 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int p = out + channel; if (channel > channelCount) { // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + for (i = 0; i < pixelCount; i++, p += 4) + *p = channel == 3 ? 255 : 0; } else { // Read the data. - for (i = 0; i < pixelCount; i++) - *p = stbi__get8(s), p += 4; + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); } } } @@ -5194,7 +5202,7 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int if (out == NULL) return out; // stbi__convert_format frees input on failure } - if (comp) *comp = channelCount; + if (comp) *comp = 4; *y = h; *x = w; @@ -6287,6 +6295,9 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int /* revision history: + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit 2.03 (2015-04-12) extra corruption checking (mmozeiko) stbi_set_flip_vertically_on_load (nguillemot) fix NEON support; fix mingw support diff --git a/stb_leakcheck.h b/stb_leakcheck.h index d309da5..a75df7c 100644 --- a/stb_leakcheck.h +++ b/stb_leakcheck.h @@ -1,4 +1,4 @@ -// stb_leakcheck.h - v0.1 - quick & dirty malloc leak-checking - public domain +// stb_leakcheck.h - v0.2 - quick & dirty malloc leak-checking - public domain #ifdef STB_LEAKCHECK_IMPLEMENTATION #undef STB_LEAKCHECK_IMPLEMENTATION // don't implenment more than once @@ -27,7 +27,7 @@ static stb_leakcheck_malloc_info *mi_head; void *stb_leakcheck_malloc(size_t sz, char *file, int line) { - stb_leakcheck_malloc_info *mi = malloc(sz + sizeof(*mi)); + stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) malloc(sz + sizeof(*mi)); if (mi == NULL) return mi; mi->file = file; mi->line = line; diff --git a/stb_rect_pack.h b/stb_rect_pack.h index dcc9d88..63a5b15 100644 --- a/stb_rect_pack.h +++ b/stb_rect_pack.h @@ -1,4 +1,4 @@ -// stb_rect_pack.h - v0.05 - public domain - rectangle packing +// stb_rect_pack.h - v0.06 - public domain - rectangle packing // Sean Barrett 2014 // // Useful for e.g. packing rectangular textures into an atlas. @@ -13,6 +13,7 @@ // More docs to come. // // No memory allocations; uses qsort() and assert() from stdlib. +// Can override those by defining STBRP_SORT and STBRP_ASSERT. // // This library currently uses the Skyline Bottom-Left algorithm. // @@ -20,8 +21,18 @@ // implement them to the same API, but with a different init // function. // +// Credits +// +// Library +// Sean Barrett +// Minor features +// Martins Mozeiko +// Bugfixes / warning fixes +// [your name could be here] +// // Version history: // +// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort // 0.05: added STBRP_ASSERT to allow replacing assert // 0.04: fixed minor bug in STBRP_LARGE_RECTS support // 0.01: initial release @@ -169,7 +180,10 @@ struct stbrp_context // #ifdef STB_RECT_PACK_IMPLEMENTATION +#ifndef STBRP_SORT #include +#define STBRP_SORT qsort +#endif #ifndef STBRP_ASSERT #include @@ -524,7 +538,7 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n } // sort according to heuristic - qsort(rects, num_rects, sizeof(rects[0]), rect_height_compare); + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); for (i=0; i < num_rects; ++i) { stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); @@ -537,7 +551,7 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n } // unsort - qsort(rects, num_rects, sizeof(rects[0]), rect_original_order); + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); // set was_packed flags for (i=0; i < num_rects; ++i) diff --git a/stb_textedit.h b/stb_textedit.h index 32e90e6..1f78048 100644 --- a/stb_textedit.h +++ b/stb_textedit.h @@ -1,4 +1,4 @@ -// stb_textedit.h - v1.5 - public domain - Sean Barrett +// stb_textedit.h - v1.6 - 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 @@ -24,12 +24,14 @@ // // DEPENDENCIES // -// Uses the C runtime function 'memmove'. Uses no other functions. -// Performs no runtime allocations. +// Uses the C runtime function 'memmove', which you can override +// by defining STB_TEXTEDIT_memmove before the implementation. +// Uses no other functions. Performs no runtime allocations. // // // VERSION HISTORY // +// 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove // 1.5 (2014-09-10) add support for secondary keys for OS X // 1.4 (2014-08-17) fix signed/unsigned warnings // 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary @@ -45,6 +47,7 @@ // Ulf Winklemann: move-by-word in 1.1 // Scott Graham: mouse selection bugfix in 1.3 // Fabian Giesen: secondary key inputs in 1.5 +// Martins Mozeiko: STB_TEXTEDIT_memmove // // USAGE // @@ -358,7 +361,10 @@ typedef struct // included just the "header" portion #ifdef STB_TEXTEDIT_IMPLEMENTATION -#include // memmove +#ifndef STB_TEXTEDIT_memmove +#include +#define STB_TEXTEDIT_memmove memmove +#endif ///////////////////////////////////////////////////////////////////////////// @@ -1038,13 +1044,13 @@ static void stb_textedit_discard_undo(StbUndoState *state) int n = state->undo_rec[0].insert_length, i; // delete n characters from all other records state->undo_char_point = state->undo_char_point - (short) n; // vsnet05 - memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); + STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); for (i=0; i < state->undo_point; ++i) if (state->undo_rec[i].char_storage >= 0) state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short) n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it } --state->undo_point; - memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); + STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); } } @@ -1062,13 +1068,13 @@ static void stb_textedit_discard_redo(StbUndoState *state) int n = state->undo_rec[k].insert_length, i; // delete n characters from all other records state->redo_char_point = state->redo_char_point + (short) n; // vsnet05 - 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_UNDOSTATECOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); 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->redo_point; - 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]))); + 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]))); } } diff --git a/stb_tilemap_editor.h b/stb_tilemap_editor.h index ed56a10..bd22a4d 100644 --- a/stb_tilemap_editor.h +++ b/stb_tilemap_editor.h @@ -1,4 +1,4 @@ -// stb_tilemap_editor.h - v0.30 - Sean Barrett - http://nothings.org/stb +// stb_tilemap_editor.h - v0.35 - Sean Barrett - http://nothings.org/stb // placed in the public domain - not copyrighted - first released 2014-09 // // Embeddable tilemap editor for C/C++ @@ -275,6 +275,10 @@ // either approach allows cut&pasting between levels.) // // REVISION HISTORY +// 0.35 layername button changes +// - layername buttons grow with the layer panel +// - fix stbte_create_map being declared as stbte_create +// - fix declaration of stbte_create_map // 0.30 properties release // - properties panel for editing user-defined "object" properties // - can link each tile to one other tile @@ -296,11 +300,16 @@ // Support STBTE_HITTEST_TILE above // ?Cancel drags by clicking other button? - may be fixed // Finish support for toolbar at side -// Layer name buttons grow to fill box // // CREDITS // -// Written by Sean Barrett, September & October 2014. +// +// Main editor & features +// Sean Barrett +// Additional features: +// Josh Huelsman +// Bugfixes: +// [this could be you!] // // LICENSE // @@ -339,7 +348,7 @@ enum // creation // -extern stbte_tilemap *stbte_create(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles); +extern stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles); // create an editable tilemap // map_x : dimensions of map horizontally (user can change this in editor), <= STBTE_MAX_TILEMAP_X // map_y : dimensions of map vertically (user can change this in editor) <= STBTE_MAX_TILEMAP_Y @@ -352,7 +361,7 @@ extern stbte_tilemap *stbte_create(int map_x, int map_y, int map_layers, int spa extern void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category); // call this repeatedly for each tile to install the tile definitions into the editable tilemap -// tm : tilemap created by stbte_create +// tm : tilemap created by stbte_create_map // id : unique identifier for each tile, 0 <= id < 32768 // layermask : bitmask of which layers tile is allowed on: 1 = layer 0, 255 = layers 0..7 // (note that onscreen, the editor numbers the layers from 1 not 0) @@ -938,6 +947,7 @@ struct stbte_tilemap int tileinfo_dirty; stbte__layer layerinfo[STBTE_MAX_LAYERS]; int has_layer_names; + int layername_width; int layer_scroll; int propmode; int solo_layer; @@ -1016,6 +1026,7 @@ stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacin tm->layer_scroll = 0; tm->propmode = 0; tm->has_layer_names = 0; + tm->layername_width = 0; tm->undo_available_valid = 0; for (i=0; i < tm->num_layers; ++i) { @@ -1088,12 +1099,17 @@ void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layerm tm->tileinfo_dirty = 1; } +static int stbte__text_width(const char *str); + void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername) { STBTE_ASSERT(layer >= 0 && layer < tm->num_layers); if (layer >= 0 && layer < tm->num_layers) { + int width; tm->layerinfo[layer].name = layername; tm->has_layer_names = 1; + width = stbte__text_width(layername); + tm->layername_width = (width > tm->layername_width ? width : tm->layername_width); } } @@ -3382,14 +3398,21 @@ static void stbte__info(stbte_tilemap *tm, int x0, int y0, int w, int h) static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h) { - int i, y, n; - int x1 = x0+w; - int y1 = y0+h; - int xoff = tm->has_layer_names ? 50 : 20; static char *propmodes[3] = { "default", "always", "never" }; int num_rows; + int i, y, n; + int x1 = x0+w; + int y1 = y0+h; + int xoff = 20; + + if (tm->has_layer_names) { + int side = stbte__ui.panel[STBTE__panel_layers].side; + xoff = stbte__region[side].width - 42; + xoff = (xoff < tm->layername_width + 10 ? xoff : tm->layername_width + 10); + } + x0 += 2; y0 += 5; if (!tm->has_layer_names) { @@ -3427,7 +3450,7 @@ static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h) n = stbte__text_width("prop:")+2; stbte__draw_text(x0,y+2, "prop:", w, STBTE__TEXTCOLOR(STBTE__cpanel)); i = w - n - 4; - if (i > 45) i = 45; + if (i > 50) i = 50; if (stbte__button(STBTE__clayer_button, propmodes[tm->propmode], x0+n,y,0,i, STBTE__ID(STBTE__layer,256), 0,0)) tm->propmode = (tm->propmode+1)%3; #endif diff --git a/stb_truetype.h b/stb_truetype.h index 26aba20..663aef4 100644 --- a/stb_truetype.h +++ b/stb_truetype.h @@ -1,4 +1,4 @@ -// stb_truetype.h - v1.03 - public domain +// stb_truetype.h - v1.06 - public domain // authored from 2009-2014 by Sean Barrett / RAD Game Tools // // This library processes TrueType files: @@ -38,12 +38,18 @@ // Cap Petschulat // Omar Cornut // github:aloucks +// Peter LaValle // // Misc other: // Ryan Gordon // // VERSION HISTORY // +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example // 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes // 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ // 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match @@ -117,6 +123,15 @@ // stbtt_GetFontVMetrics() // stbtt_GetCodepointKernAdvance() // +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// // ADDITIONAL DOCUMENTATION // // Immediately after this block comment are a series of sample programs. @@ -216,7 +231,15 @@ // Baked bitmap interface 70 LOC / // Font name matching & access 150 LOC ---- 150 // C runtime library abstraction 60 LOC ---- 60 - +// +// +// PERFORMANCE MEASUREMENTS FOR 1.06: +// +// 32-bit 64-bit +// Previous release: 8.83 s 7.68 s +// Pool allocations: 7.72 s 6.34 s +// Inline sort : 6.54 s 5.65 s +// New rasterizer : 5.63 s 5.00 s ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -239,7 +262,7 @@ GLuint ftex; void my_stbtt_initfont(void) { fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); - stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! // can free ttf_buffer at this point glGenTextures(1, &ftex); glBindTexture(GL_TEXTURE_2D, ftex); @@ -327,7 +350,7 @@ int main(int arg, char **argv) stbtt_fontinfo font; int i,j,ascent,baseline,ch=0; float scale, xpos=2; // leave a little padding in case the character extends left - char *text = "Heljo World!"; + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); stbtt_InitFont(&font, buffer, 0); @@ -385,18 +408,6 @@ int main(int arg, char **argv) typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; - #ifdef STBTT_STATIC - #define STBTT_DEF static - #else - #define STBTT_DEF extern - #endif - - // #define your own STBTT_sort() to override this to avoid qsort - #ifndef STBTT_sort - #include - #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) - #endif - // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h #ifndef STBTT_ifloor #include @@ -443,6 +454,12 @@ int main(int arg, char **argv) #ifndef __STB_INCLUDE_STB_TRUETYPE_H__ #define __STB_INCLUDE_STB_TRUETYPE_H__ +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + #ifdef __cplusplus extern "C" { #endif @@ -899,6 +916,10 @@ enum { // languageID for STBTT_PLATFORM_ID_MAC typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + ////////////////////////////////////////////////////////////////////////// // // accessors to parse data from file @@ -1547,42 +1568,129 @@ STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codep stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); } +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + typedef struct stbtt__edge { float x0,y0, x1,y1; int invert; } stbtt__edge; + typedef struct stbtt__active_edge { + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 int x,dx; float ey; - struct stbtt__active_edge *next; - int valid; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif } stbtt__active_edge; -#define FIXSHIFT 10 -#define FIX (1 << FIXSHIFT) -#define FIXMASK (FIX-1) +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) -static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) { - stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); - STBTT_assert(e->y0 <= start_point); if (!z) return z; - // round dx down to avoid going too far + + // round dx down to avoid overshooting if (dxdy < 0) - z->dx = -STBTT_ifloor(FIX * -dxdy); + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); else - z->dx = STBTT_ifloor(FIX * dxdy); - z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); - z->x -= off_x * FIX; + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + z->ey = e->y1; z->next = 0; - z->valid = e->invert ? 1 : -1; + z->direction = e->invert ? 1 : -1; return z; } +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = (1/dxdy); + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif +#if STBTT_RASTERIZER_VERSION == 1 // note: this routine clips fills that extend off the edges... ideally this // wouldn't happen, but it could happen if the truetype glyph bounding boxes // are wrong, or if the user supplies a too-small bitmap @@ -1594,26 +1702,26 @@ static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__ac while (e) { if (w == 0) { // if we're currently at zero, we need to record the edge start point - x0 = e->x; w += e->valid; + x0 = e->x; w += e->direction; } else { - int x1 = e->x; w += e->valid; + int x1 = e->x; w += e->direction; // if we went to zero, we need to draw if (w == 0) { - int i = x0 >> FIXSHIFT; - int j = x1 >> FIXSHIFT; + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; if (i < len && j >= 0) { if (i == j) { // x0,x1 are the same pixel, so compute combined coverage - scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); } else { if (i >= 0) // add antialiasing for x0 - scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); else i = -1; // clip if (j < len) // add antialiasing for x1 - scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); else j = len; // clip @@ -1630,6 +1738,7 @@ static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__ac static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) { + stbtt__hheap hh = { 0 }; stbtt__active_edge *active = NULL; int y,j=0; int max_weight = (255 / vsubsample); // weight per vertical scanline @@ -1657,9 +1766,9 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, stbtt__active_edge * z = *step; if (z->ey <= scan_y) { *step = z->next; // delete from list - STBTT_assert(z->valid); - z->valid = 0; - STBTT_free(z, userdata); + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); } else { z->x += z->dx; // advance to position for current scanline step = &((*step)->next); // advance through list @@ -1688,7 +1797,7 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline while (e->y0 <= scan_y) { if (e->y1 > scan_y) { - stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); // find insertion point if (active == NULL) active = z; @@ -1719,24 +1828,377 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, ++j; } - while (active) { - stbtt__active_edge *z = active; - active = active->next; - STBTT_free(z, userdata); - } + stbtt__hheap_cleanup(&hh, userdata); if (scanline != scanline_data) STBTT_free(scanline, userdata); } -static int stbtt__edge_compare(const void *p, const void *q) -{ - stbtt__edge *a = (stbtt__edge *) p; - stbtt__edge *b = (stbtt__edge *) q; +#elif STBTT_RASTERIZER_VERSION == 2 - if (a->y0 < b->y0) return -1; - if (a->y0 > b->y0) return 1; - return 0; +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + assert(y0 < y1); + assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + assert(x1 <= x+1); + else if (x0 == x+1) + assert(x1 >= x); + else if (x0 <= x) + assert(x1 <= x); + else if (x0 >= x+1) + assert(x1 >= x+1); + else + assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float y0,y1; + float dy = e->fdy; + assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + y0 = e->sy; + } else { + x_top = x0; + y0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + y1 = e->ey; + } else { + x_bottom = xb; + y1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = y1 - y0; + assert(x >= 0 && x < len); + scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; + scanline_fill[x] += e->direction * height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + y0 = y_bottom - (y0 - y_top); + y1 = y_bottom - (y1 - y_top); + t = y0, y0 = y1, y1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = (x1+1 - x0) * dy + y_top; + + sign = e->direction; + // area of the rectangle covered from y0..y_crossing + area = sign * (y_crossing-y0); + // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) + scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += dy * (x2 - (x1+1)); + + assert(fabs(area) <= 1.01f); + + scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (y1-y_crossing); + + scanline_fill[x2] += sign * (y1-y0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + float y0,y1; + float y_cur = y_top, x_cur = x0; + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + y0 = (x - x0) / dx + y_top; + y1 = (x+1 - x0) / dx + y_top; + + if (y0 < y1) { + if (y0 > y_top && y0 < y_bottom) { + stbtt__handle_clipped_edge(scanline,x,e, x_cur,y_cur, (float) x,y0); + y_cur = y0; + x_cur = (float) x; + } + if (y1 >= y_cur && y1 < y_bottom) { + stbtt__handle_clipped_edge(scanline,x,e, x_cur,y_cur, (float) x+1,y1); + y_cur = y1; + x_cur = (float) x+1; + } + } else { + if (y1 >= y_cur && y1 < y_bottom) { + stbtt__handle_clipped_edge(scanline,x,e, x_cur,y_cur, (float) x+1,y1); + y_cur = y1; + x_cur = (float) x+1; + } + if (y0 > y_top && y0 < y_bottom) { + stbtt__handle_clipped_edge(scanline,x,e, x_cur,y_cur, (float) x,y0); + y_cur = y0; + x_cur = (float) x; + } + } + stbtt__handle_clipped_edge(scanline,x,e, x_cur,y_cur, xb,y_bottom); + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + assert(z->ey >= scan_y_top); + // insert at front + z->next = active; + active = z; + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshhold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); } typedef struct @@ -1749,7 +2211,13 @@ static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcou float y_scale_inv = invert ? -scale_y : scale_y; stbtt__edge *e; int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif // vsubsample should divide 255 evenly; otherwise we won't reach full opacity // now we have to blow out the windings into explicit edge lists @@ -1786,7 +2254,8 @@ static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcou } // now sort the edges by their highest point (should snap to integer, and then by x) - STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); diff --git a/stb_voxel_render.h b/stb_voxel_render.h index 4210821..0f462ee 100644 --- a/stb_voxel_render.h +++ b/stb_voxel_render.h @@ -1,4 +1,4 @@ -// stb_voxel_render.h - v0.80 - Sean Barrett, 2015 - public domain +// stb_voxel_render.h - v0.81 - Sean Barrett, 2015 - public domain // // This library helps render large-scale "voxel" worlds for games, // in this case, one with blocks that can have textures and that @@ -190,7 +190,8 @@ // // VERSION HISTORY // -// 0.81 (2015-04-12) added input.packed_compact to store rot, vheight & texlerp efficiently +// 0.82 (2015-08-01) added input.packed_compact to store rot, vheight & texlerp efficiently +// 0.81 (2015-05-28) fix broken STBVOX_CONFIG_OPTIMIZED_VHEIGHT // 0.80 (2015-04-11) fix broken STBVOX_CONFIG_ROTATION_IN_LIGHTING refactoring // change STBVOX_MAKE_LIGHTING to STBVOX_MAKE_LIGHTING_EXT so // that header defs don't need to see config vars @@ -2813,7 +2814,6 @@ void stbvox_make_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int fac } } -#ifndef STBVOX_CONFIG_OPTIMIZED_VHEIGHT // get opposite-facing normal & texgen for opposite face, used to map up-facing vheight data to down-facing data static unsigned char stbvox_reverse_face[STBVF_count] = { @@ -2823,7 +2823,7 @@ static unsigned char stbvox_reverse_face[STBVF_count] = 0, 0, 0, 0, STBVF_ne_d, STBVF_ne_d, STBVF_nd, STBVF_nu }; - +#ifndef STBVOX_CONFIG_OPTIMIZED_VHEIGHT // render non-planar quads by splitting into two triangles, rendering each as a degenerate quad static void stbvox_make_12_split_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, unsigned char *ht) { diff --git a/stretchy_buffer.h b/stretchy_buffer.h index 967e076..a89be09 100644 --- a/stretchy_buffer.h +++ b/stretchy_buffer.h @@ -1,7 +1,8 @@ -// stretchy_buffer.h - v1.01 - public domain - nothings.org/stb +// stretchy_buffer.h - v1.02 - public domain - nothings.org/stb // a vector<>-like dynamic array for C // // version history: +// 1.02 - compiles as C++, but untested // 1.01 - added a "common uses" documentation section // 1.0 - fixed bug in the version I posted prematurely // 0.9 - rewrite to try to avoid strict-aliasing optimization @@ -193,7 +194,7 @@ static void * stb__sbgrowf(void *arr, int increment, int itemsize) int dbl_cur = arr ? 2*stb__sbm(arr) : 0; int min_needed = stb_sb_count(arr) + increment; int m = dbl_cur > min_needed ? dbl_cur : min_needed; - int *p = realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2); + int *p = (int *) realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2); if (p) { if (!arr) p[1] = 0; diff --git a/tests/stb.dsp b/tests/stb.dsp index b86c6d2..621f5a4 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 "TT_TEST" /FR /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 # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -150,6 +150,10 @@ SOURCE=..\stb_vorbis.c # End Source File # Begin Source File +SOURCE=..\stb_voxel_render.h +# End Source File +# Begin Source File + SOURCE=..\stretchy_buffer.h # End Source File # Begin Source File diff --git a/tests/stb_cpp.cpp b/tests/stb_cpp.cpp index 94a7310..57f2cfa 100644 --- a/tests/stb_cpp.cpp +++ b/tests/stb_cpp.cpp @@ -80,4 +80,3 @@ int main(int argc, char **argv) } return 0; } - \ No newline at end of file diff --git a/tests/test_cpp_compilation.cpp b/tests/test_cpp_compilation.cpp index 59b7969..97bd2c2 100644 --- a/tests/test_cpp_compilation.cpp +++ b/tests/test_cpp_compilation.cpp @@ -8,7 +8,6 @@ #define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION #define STB_RECT_PACK_IMPLEMENTATION #define STB_VOXEL_RENDER_IMPLEMENTATION -#define STBVOX_CONFIG_MODE 1 #define STBI_MALLOC my_malloc #define STBI_FREE my_free @@ -27,9 +26,112 @@ void my_free(void *) { } #include "stb_c_lexer.h" #include "stb_divide.h" #include "stb_herringbone_wang_tile.h" + +#define STBVOX_CONFIG_MODE 1 #include "stb_voxel_render.h" #define STBTE_DRAW_RECT(x0,y0,x1,y1,color) do ; while(0) #define STBTE_DRAW_TILE(x,y,id,highlight,data) do ; while(0) #define STB_TILEMAP_EDITOR_IMPLEMENTATION #include "stb_tilemap_editor.h" + +#include "stb_easy_font.h" + +#define STB_LEAKCHECK_IMPLEMENTATION +#include "stb_leakcheck.h" + +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include "stb_image_resize.h" + +#include "stretchy_buffer.h" + + + +//////////////////////////////////////////////////////////// +// +// text edit + +#include +#include // memmove +#include // isspace + +#define STB_TEXTEDIT_CHARTYPE char +#define STB_TEXTEDIT_STRING text_control + +// get the base type +#include "stb_textedit.h" + +// define our editor structure +typedef struct +{ + char *string; + int stringlen; + STB_TexteditState state; +} text_control; + +// define the functions we need +void layout_func(StbTexteditRow *row, STB_TEXTEDIT_STRING *str, int start_i) +{ + int remaining_chars = str->stringlen - start_i; + row->num_chars = remaining_chars > 20 ? 20 : remaining_chars; // should do real word wrap here + row->x0 = 0; + row->x1 = 20; // need to account for actual size of characters + row->baseline_y_delta = 1.25; + row->ymin = -1; + row->ymax = 0; +} + +int delete_chars(STB_TEXTEDIT_STRING *str, int pos, int num) +{ + memmove(&str->string[pos], &str->string[pos+num], str->stringlen - (pos+num)); + str->stringlen -= num; + return 1; // always succeeds +} + +int insert_chars(STB_TEXTEDIT_STRING *str, int pos, STB_TEXTEDIT_CHARTYPE *newtext, int num) +{ + str->string = (char *) realloc(str->string, str->stringlen + num); + memmove(&str->string[pos+num], &str->string[pos], str->stringlen - pos); + memcpy(&str->string[pos], newtext, num); + str->stringlen += num; + return 1; // always succeeds +} + +// define all the #defines needed + +#define KEYDOWN_BIT 0x80000000 + +#define STB_TEXTEDIT_STRINGLEN(tc) ((tc)->stringlen) +#define STB_TEXTEDIT_LAYOUTROW layout_func +#define STB_TEXTEDIT_GETWIDTH(tc,n,i) (1) // quick hack for monospaced +#define STB_TEXTEDIT_KEYTOTEXT(key) (((key) & KEYDOWN_BIT) ? 0 : (key)) +#define STB_TEXTEDIT_GETCHAR(tc,i) ((tc)->string[i]) +#define STB_TEXTEDIT_NEWLINE '\n' +#define STB_TEXTEDIT_IS_SPACE(ch) isspace(ch) +#define STB_TEXTEDIT_DELETECHARS delete_chars +#define STB_TEXTEDIT_INSERTCHARS insert_chars + +#define STB_TEXTEDIT_K_SHIFT 0x40000000 +#define STB_TEXTEDIT_K_CONTROL 0x20000000 +#define STB_TEXTEDIT_K_LEFT (KEYDOWN_BIT | 1) // actually use VK_LEFT, SDLK_LEFT, etc +#define STB_TEXTEDIT_K_RIGHT (KEYDOWN_BIT | 2) // VK_RIGHT +#define STB_TEXTEDIT_K_UP (KEYDOWN_BIT | 3) // VK_UP +#define STB_TEXTEDIT_K_DOWN (KEYDOWN_BIT | 4) // VK_DOWN +#define STB_TEXTEDIT_K_LINESTART (KEYDOWN_BIT | 5) // VK_HOME +#define STB_TEXTEDIT_K_LINEEND (KEYDOWN_BIT | 6) // VK_END +#define STB_TEXTEDIT_K_TEXTSTART (STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_TEXTEND (STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_DELETE (KEYDOWN_BIT | 7) // VK_DELETE +#define STB_TEXTEDIT_K_BACKSPACE (KEYDOWN_BIT | 8) // VK_BACKSPACE +#define STB_TEXTEDIT_K_UNDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'z') +#define STB_TEXTEDIT_K_REDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'y') +#define STB_TEXTEDIT_K_INSERT (KEYDOWN_BIT | 9) // VK_INSERT +#define STB_TEXTEDIT_K_WORDLEFT (STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_WORDRIGHT (STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_PGUP (KEYDOWN_BIT | 10) // VK_PGUP -- not implemented +#define STB_TEXTEDIT_K_PGDOWN (KEYDOWN_BIT | 11) // VK_PGDOWN -- not implemented + +#define STB_TEXTEDIT_IMPLEMENTATION +#include "stb_textedit.h" + + diff --git a/tests/test_vorbis.c b/tests/test_vorbis.c index a9a9f26..5841381 100644 --- a/tests/test_vorbis.c +++ b/tests/test_vorbis.c @@ -8,11 +8,10 @@ extern void stb_vorbis_dumpmem(void); int main(int argc, char **argv) { size_t memlen; - unsigned char *mem = stb_fileu("c:/x/01.ogg", &memlen); + unsigned char *mem = stb_fileu("c:/x/theme_03.ogg", &memlen); int chan, samplerate; short *output; int samples = stb_vorbis_decode_memory(mem, memlen, &chan, &samplerate, &output); - stb_vorbis_dumpmem(); return 0; } #endif diff --git a/tools/README.footer.md b/tools/README.footer.md index 24925a3..aa07eeb 100644 --- a/tools/README.footer.md +++ b/tools/README.footer.md @@ -20,6 +20,17 @@ attribution requirement). They may be less featureful, slower, and/or use more memory. If you're already using an equivalent library, there's probably no good reason to switch. +#### Why do you list "lines of code"? It's a terrible metric. + +Just to give you some idea of the internal complexity of the library, +to help you manage your expectations, or to let you know what you're +getting into. While not all the libraries are written in the same +style, they're certainly similar styles, and so comparisons between +the libraries are probably still meaningful. + +Note though that the lines do include both the implementation, the +part that corresponds to a header file, and the documentation. + #### Why single-file headers? Windows doesn't have standard directories where libraries @@ -27,12 +38,21 @@ live. That makes deploying libraries in Windows a lot more painful than open source developers on Unix-derivates generally realize. (It also makes library dependencies a lot worse in Windows.) +There's also a common problem in Windows where a library was built +against a different version of the runtime library, which causes +link conflicts and confusion. Shipping the libs as headers means +you normally just compile them straight into your project without +making libraries, thus sidestepping that problem. + Making them a single file makes it very easy to just drop them into a project that needs them. (Of course you can still put them in a proper shared library tree if you want.) +Why not two files, one a header and one an implementation? The difference between 10 files and 9 files is not a big deal, but the difference between 2 files and 1 file is a big deal. +You don't need to zip or tar the files up, you don't have to +remember to attach *two* files, etc. #### Why "stb"? Is this something to do with Set-Top Boxes? @@ -58,12 +78,9 @@ Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt #### Why public domain? -Because more people will use it. Because it's not viral, people -are not obligated to give back, so you could argue that it hurts -the *development* of it, and then because it doesn't develop as -well it's not as good, and then because it's not as good, in the -long run maybe fewer people will use it. I have total respect for -that opinion, but I just don't believe it myself for most software. +I prefer it over GPL, LGPL, BSD, zlib, etc. for many reasons. +Some of them are listed here: +https://github.com/nothings/stb/blob/master/docs/why_public_domain.md #### Why C? diff --git a/tools/README.header.md b/tools/README.header.md index 66871d2..f30e631 100644 --- a/tools/README.header.md +++ b/tools/README.header.md @@ -3,5 +3,5 @@ stb single-file public domain libraries for C/C++ -library | lastest version | category | description ---------------------- | ---- | -------- | -------------------------------- +library | lastest version | category | LoC | description +--------------------- | ---- | -------- | --- | -------------------------------- diff --git a/tools/README.list b/tools/README.list index 70f6ff7..8cf1f5f 100644 --- a/tools/README.list +++ b/tools/README.list @@ -10,8 +10,8 @@ stb_voxel_render.h | 3D graphics | Minecraft-esque voxel rendering stb_dxt.h | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor stb_perlin.h | 3D graphics | revised Perlin noise (3D input, 1D output) stb_easy_font.h | 3D graphics | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc -stb_tilemap_editor.h | game development | embeddable tilemap editor -stb_herringbone_wang_tile.h | game development | herringbone Wang tile map generator +stb_tilemap_editor.h | game dev | embeddable tilemap editor +stb_herringbone_wang_tile.h | game dev | herringbone Wang tile map generator stb_c_lexer.h | parsing | simplify writing parsers for C-like languages stb_divide.h | math | more useful 32-bit modulus e.g. "euclidean divide" stb.h | misc | helper functions for C, mostly redundant in C++; basically author's personal stuff diff --git a/tools/make_readme.c b/tools/make_readme.c index cdc0b85..224f289 100644 --- a/tools/make_readme.c +++ b/tools/make_readme.c @@ -4,23 +4,23 @@ int main(int argc, char **argv) { int i; - int hlen, flen, listlen; + int hlen, flen, listlen, total_lines = 0; char *header = stb_file("README.header.md", &hlen); // stb_file - read file into malloc()ed buffer char *footer = stb_file("README.footer.md", &flen); // stb_file - read file into malloc()ed buffer char **list = stb_stringfile("README.list", &listlen); // stb_stringfile - read file lines into malloced array of strings FILE *f = fopen("../README.md", "wb"); + + fprintf(f, "\n\n"); fwrite(header, 1, hlen, f); for (i=0; i < listlen; ++i) { int num,j; char **tokens = stb_tokens_stripwhite(list[i], "|", &num); // stb_tokens -- tokenize string into malloced array of strings - FILE *g = fopen(stb_sprintf("../%s", tokens[0]), "rb"); // stb_sprintf -- sprintf to a static temp buffer (not threadsafe or secure) - char buffer[256], *s1, *s2; - fread(buffer, 1, 256, g); - fclose(g); - buffer[255] = 0; - s1 = strchr(buffer, '-'); + int num_lines; + char **lines = stb_stringfile(stb_sprintf("../%s", tokens[0]), &num_lines); + char *s1, *s2,*s3; + s1 = strchr(lines[0], '-'); if (!s1) stb_fatal("Couldn't find '-' before version number in %s", tokens[0]); // stb_fatal -- print error message & exit s2 = strchr(s1+2, '-'); if (!s2) stb_fatal("Couldn't find '-' after version number in %s", tokens[0]); // stb_fatal -- print error message & exit @@ -28,16 +28,32 @@ int main(int argc, char **argv) s1 += 1; s1 = stb_trimwhite(s1); // stb_trimwhite -- advance pointer to after whitespace & delete trailing whitespace if (*s1 == 'v') ++s1; - fprintf(f, "**%s** | %s", tokens[0], s1); + s3 = tokens[0]; + stb_trimwhite(s3); + if (strlen(s3) < 21) { + fprintf(f, "**%s** | %s", tokens[0], s1); + } else { + char buffer[256]; + strncpy(buffer, s3, 18); + buffer[18] = 0; + strcat(buffer, "..."); + fprintf(f, "**%s** | %s", buffer, s1); + } s1 = stb_trimwhite(tokens[1]); // stb_trimwhite -- advance pointer to after whitespace & delete trailing whitespace s2 = stb_dupreplace(s1, " ", " "); // stb_dupreplace -- search & replace string and malloc result fprintf(f, " | %s", s2); free(s2); + fprintf(f, " | %d", num_lines); + total_lines += num_lines; for (j=2; j < num; ++j) fprintf(f, " | %s", tokens[j]); fprintf(f, "\n"); } + fprintf(f, "\n"); + fprintf(f, "Total libraries: %d \n", listlen); + fprintf(f, "Total lines of C code: %d\n\n", total_lines); + fwrite(footer, 1, flen, f); fclose(f); diff --git a/tools/make_readme.dsp b/tools/make_readme.dsp index e5969ae..232dd86 100644 --- a/tools/make_readme.dsp +++ b/tools/make_readme.dsp @@ -87,6 +87,10 @@ SOURCE=.\make_readme.c # End Source File # Begin Source File +SOURCE=.\README.header.md +# End Source File +# Begin Source File + SOURCE=.\README.list # End Source File # End Target