From c38ec91d229cbe56167f8af865c05b3d92db705f Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 01:27:15 -0700 Subject: [PATCH 01/19] stb_divide: Fix integer overflow issues We've established the signs of values before so we can carefully jiggle the expressions to be guaranteed overflow-free; the tests for <0 here were meant to check if the value was "still negative", i.e. if the sum did not underflow below INT_MIN, and we can rewrite that using algebra to be overflow-free. We need an extra case in the Euclidean dvision for INT_MIN / INT_MIN which is a bit annoying but trivial; finally, for two's complement platforms, note that abs(x) = (x > 0) ? x : -x does not work (since for x=INT_MIN, -x still gives INT_MIN), but the equivalent formulation for _negative_ absolute value (x < 0) ? x : -x is always in range and overflow-free, so rewrite the relevant expressions using that negative absolute value instead. Fixes issue #741. --- stb_divide.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/stb_divide.h b/stb_divide.h index f8b1f3e..c8a1d92 100644 --- a/stb_divide.h +++ b/stb_divide.h @@ -166,15 +166,15 @@ int stb_div_floor(int v1, int v2) return v1/v2; #else if (v1 >= 0 && v2 < 0) { - if ((-v1)+v2+1 < 0) // check if increasing v1's magnitude overflows - return -stb__div(-v1+v2+1,v2); // nope, so just compute it + if (v2 + 1 >= INT_MIN + v1) // check if increasing v1's magnitude overflows + return -stb__div((v2+1)-v1,v2); // nope, so just compute it else return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0); } if (v1 < 0 && v2 >= 0) { if (v1 != INT_MIN) { - if (v1-v2+1 < 0) // check if increasing v1's magnitude overflows - return -stb__div(v1-v2+1,-v2); // nope, so just compute it + if (v1 + 1 >= INT_MIN + v2) // check if increasing v1's magnitude overflows + return -stb__div((v1+1)-v2,-v2); // nope, so just compute it else return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0); } else // it must be possible to compute -(v1+v2) without overflowing @@ -209,8 +209,10 @@ int stb_div_eucl(int v1, int v2) else // if v1 is INT_MIN, we have to move away from overflow place if (v2 >= 0) q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2); - else + else if (v2 != INT_MIN) q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2); + else // for INT_MIN / INT_MIN, we need to be extra-careful to avoid overflow + q = 1, r = 0; #endif if (r >= 0) return q; @@ -228,13 +230,13 @@ int stb_mod_trunc(int v1, int v2) if (r >= 0) return r; else - return r + (v2 > 0 ? v2 : -v2); + return r - (v2 < 0 ? v2 : -v2); } else { // modulus result should always be negative int r = stb__mod(v1,v2); if (r <= 0) return r; else - return r - (v2 > 0 ? v2 : -v2); + return r + (v2 < 0 ? v2 : -v2); } #endif } @@ -267,7 +269,7 @@ int stb_mod_eucl(int v1, int v2) if (r >= 0) return r; else - return r + (v2 > 0 ? v2 : -v2); // abs() + return r - (v2 < 0 ? v2 : -v2); // negative abs() [to avoid overflow] } #ifdef STB_DIVIDE_TEST From e7f9f92c9d466fac3b42a2b4a3e6849df7bea51c Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 01:33:35 -0700 Subject: [PATCH 02/19] stb_divide: Update changelog --- stb_divide.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stb_divide.h b/stb_divide.h index c8a1d92..455aacf 100644 --- a/stb_divide.h +++ b/stb_divide.h @@ -1,8 +1,9 @@ -// stb_divide.h - v0.93 - public domain - Sean Barrett, Feb 2010 +// stb_divide.h - v0.94 - public domain - Sean Barrett, Feb 2010 // Three kinds of divide/modulus of signed integers. // // HISTORY // +// v0.94 Fix integer overflow issues // v0.93 2020-02-02 Write useful exit() value from main() // v0.92 2019-02-25 Fix warning // v0.91 2010-02-27 Fix euclidean division by INT_MIN for non-truncating C From 14c224c84e51af5b4932f2a410e66a06f20c8cf7 Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 01:45:29 -0700 Subject: [PATCH 03/19] stb_image_resize: Remove ill-advised asserts. These mostly add very little and have caused problems for people, nor does it make sense to require this when the underlying computations are performed in floating-point arithmetic depending on ratios of user-passed in image dimensions. Arbitrary absolute epsilons here would just be garbage; we could try and compute desired relative error bounds based on the determined scale values, but this still leaves the questions of what purpose this would even serve, which is unclear. Leave the filter kernel asserts as comment for documentation of what the behavior would be with exact math, but don't actually bother asserting here. Fixes issue #736. --- stb_image_resize.h | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 42a8efb..17e46b7 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1064,7 +1064,11 @@ static void stbir__calculate_coefficients_upsample(stbir_filter filter, float sc total_filter += coefficient_group[i]; } - STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); + // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. + // It would be true in exact math but is at best approximately true in floating-point math, + // and it would not make sense to try and put actual bounds on this here because it depends + // on the image aspect ratio which can get pretty extreme. + //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); STBIR_ASSERT(total_filter > 0.9); STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. @@ -1089,7 +1093,7 @@ static void stbir__calculate_coefficients_downsample(stbir_filter filter, float { int i; - STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. contributor->n0 = out_first_pixel; contributor->n1 = out_last_pixel; @@ -1103,7 +1107,11 @@ static void stbir__calculate_coefficients_downsample(stbir_filter filter, float coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; } - STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); + // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. + // It would be true in exact math but is at best approximately true in floating-point math, + // and it would not make sense to try and put actual bounds on this here because it depends + // on the image aspect ratio which can get pretty extreme. + //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); for (i = out_last_pixel - out_first_pixel; i >= 0; i--) { @@ -1552,7 +1560,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float { int out_pixel_index = k * 1; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; } } @@ -1573,7 +1580,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float { int out_pixel_index = k * 2; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; } @@ -1595,7 +1601,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float { int out_pixel_index = k * 3; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; @@ -1618,7 +1623,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float { int out_pixel_index = k * 4; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; @@ -1643,7 +1647,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float int c; int out_pixel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); for (c = 0; c < channels; c++) output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; } From db6e91b7d867f33eb5e9c5012324e2702a1eeedd Mon Sep 17 00:00:00 2001 From: Andrew Kensler Date: Sat, 3 Jul 2021 23:39:43 -0700 Subject: [PATCH 04/19] Store uncompressed if compression was worse If the data was uncompressible and this deflate implementation expanded by more than the overhead of simply storing it uncompressed, fall back to deflate's uncompressed storage mode. This bounds the maximum deflated size at the original size plus an overhead of 6 fixed bytes with another 5 bytes per 32767 byte block. Fixes issue #948. --- stb_image_write.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/stb_image_write.h b/stb_image_write.h index 271d4ac..a909e82 100644 --- a/stb_image_write.h +++ b/stb_image_write.h @@ -140,6 +140,7 @@ CREDITS: Ivan Tikhonov github:ignotion Adam Schackart + Andrew Kensler LICENSE @@ -969,6 +970,23 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i (void) stbiw__sbfree(hash_table[i]); STBIW_FREE(hash_table); + // store uncompressed instead if compression was worse + if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) { + stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1 + for (j = 0; j < data_len;) { + int blocklen = data_len - j; + if (blocklen > 32767) blocklen = 32767; + stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression + stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN + stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN + stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8)); + memcpy(out+stbiw__sbn(out), data+j, blocklen); + stbiw__sbn(out) += blocklen; + j += blocklen; + } + } + { // compute adler32 on input unsigned int s1=1, s2=0; @@ -1599,6 +1617,8 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history + 1.15 ( ) + make Deflate code emit uncompressed blocks when it would otherwise expand 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels 1.13 1.12 From e59050549285deb717d8a2245586c0b8c2a3d38a Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 02:45:41 -0700 Subject: [PATCH 05/19] stb_truetype: GCC warning fix GCC warns about a potentially uninitialized variable here. It's not (or at least I don't see how), but fix it anyway. --- stb_truetype.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stb_truetype.h b/stb_truetype.h index ce32bb3..8b3bd87 100644 --- a/stb_truetype.h +++ b/stb_truetype.h @@ -4624,7 +4624,8 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc float ax = x1-x0, ay = y1-y0; float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; float mx = x0 - sx, my = y0 - sy; - float res[3],px,py,t,it,dist2; + float res[3] = {0.f,0.f,0.f}; + float px,py,t,it,dist2; float a_inv = precompute[i]; if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula float a = 3*(ax*bx + ay*by); From 1252a3e6415353bb8f82cc52fc392c4073634eca Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 02:58:28 -0700 Subject: [PATCH 06/19] stb_truetype: GPOS parsing fixes Glyphs not assigned a glyph class should default to class 0 as per the OpenType spec. Also, change the code to treat malformed data as an error to be handled, not an assert, and change the way the version checking works. Fixes the issue mentioned in PR #1035. Also, this part of the code is indented incorrectly; will fix that in a subsequent commit. --- stb_truetype.h | 99 +++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/stb_truetype.h b/stb_truetype.h index 8b3bd87..f959774 100644 --- a/stb_truetype.h +++ b/stb_truetype.h @@ -2479,12 +2479,13 @@ static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) } break; default: { - // There are no other cases. - STBTT_assert(0); + // Unsupported defition type; return an error. + return -1; } break; } - return -1; + // "All glyphs not assigned to a class fall into class 0". (OpenType spec) + return 0; } // Define to STBTT_assert(x) if you want to break on unimplemented formats. @@ -2533,75 +2534,67 @@ static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, i int straw, needle; stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); - stbtt_int32 valueRecordPairSizeInBytes = 2; - stbtt_uint16 pairSetCount = ttUSHORT(table + 8); - stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex); - stbtt_uint8 *pairValueTable = table + pairPosOffset; - stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable); - stbtt_uint8 *pairValueArray = pairValueTable + 2; - // 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 (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? + stbtt_int32 valueRecordPairSizeInBytes = 2; + stbtt_uint16 pairSetCount = ttUSHORT(table + 8); + stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex); + stbtt_uint8 *pairValueTable = table + pairPosOffset; + stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable); + stbtt_uint8 *pairValueArray = pairValueTable + 2; - STBTT_assert(coverageIndex < pairSetCount); - STBTT__NOTUSED(pairSetCount); + if (coverageIndex >= pairSetCount) return 0; - needle=glyph2; - r=pairValueCount-1; - l=0; + needle=glyph2; + r=pairValueCount-1; + l=0; - // Binary search. - while (l <= r) { - stbtt_uint16 secondGlyph; - stbtt_uint8 *pairValue; - m = (l + r) >> 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; + // Binary search. + while (l <= r) { + stbtt_uint16 secondGlyph; + stbtt_uint8 *pairValue; + m = (l + r) >> 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; + } } - } + } else + return 0; } break; case 2: { stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? + 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 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_uint16 class1Count = ttUSHORT(table + 12); - stbtt_uint16 class2Count = ttUSHORT(table + 14); - STBTT_assert(glyph1class < class1Count); - STBTT_assert(glyph2class < class2Count); + if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed + if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed - // 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; - } + } else + return 0; } break; default: { - // There are no other cases. - STBTT_assert(0); + // Unsupported definition type + return 0; break; }; } From 9fe3b4bb52ff671809d72afa7e5dead88d292cb1 Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 03:12:33 -0700 Subject: [PATCH 07/19] stb_truetype: GPOS handling formatting changes Was using 4-character spaces and otherwise formatted unlike the rest of the code, fix this. Also get rid of the outer switch in GetGlyphGPOSInfoAdvance with just one case; just use an if. No behavioral changes. --- stb_truetype.h | 363 ++++++++++++++++++++++++------------------------- 1 file changed, 177 insertions(+), 186 deletions(-) diff --git a/stb_truetype.h b/stb_truetype.h index f959774..783ecfa 100644 --- a/stb_truetype.h +++ b/stb_truetype.h @@ -2353,7 +2353,7 @@ STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningent return length; } -static int stbtt__GetGlyphKernInfoAdvance(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; @@ -2383,232 +2383,223 @@ static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph return 0; } -static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) +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); + 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; - } + // 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; + } + break; + } - case 2: { - stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); - stbtt_uint8 *rangeArray = coverageTable + 4; + 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; - } + // 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; + } + break; + } - default: { - // There are no other cases. - STBTT_assert(0); - } break; - } + default: return -1; // unsupported + } - return -1; + 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; + 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)); - } break; + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); + break; + } - case 2: { - stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); - stbtt_uint8 *classRangeRecords = classDefTable + 4; + 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); - } - } break; + // 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); + } + break; + } - default: { - // Unsupported defition type; return an error. - return -1; - } break; - } + default: + return -1; // Unsupported definition type, return an error. + } - // "All glyphs not assigned to a class fall into class 0". (OpenType spec) - return 0; + // "All glyphs not assigned to a class fall into class 0". (OpenType spec) + return 0; } // 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) +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; + stbtt_uint16 lookupListOffset; + stbtt_uint8 *lookupList; + stbtt_uint16 lookupCount; + stbtt_uint8 *data; + stbtt_int32 i, sti; - if (!info->gpos) return 0; + if (!info->gpos) return 0; - data = info->data + info->gpos; + 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 + 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); + lookupListOffset = ttUSHORT(data+8); + lookupList = data + lookupListOffset; + lookupCount = ttUSHORT(lookupList); - for (i=0; i= pairSetCount) return 0; + switch (posFormat) { + case 1: { + stbtt_int32 l, r, m; + int straw, needle; + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? + stbtt_int32 valueRecordPairSizeInBytes = 2; + stbtt_uint16 pairSetCount = ttUSHORT(table + 8); + stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex); + stbtt_uint8 *pairValueTable = table + pairPosOffset; + stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable); + stbtt_uint8 *pairValueArray = pairValueTable + 2; - needle=glyph2; - r=pairValueCount-1; - l=0; + if (coverageIndex >= pairSetCount) return 0; - // Binary search. - while (l <= r) { - stbtt_uint16 secondGlyph; - stbtt_uint8 *pairValue; - m = (l + r) >> 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; - } - } - } else - return 0; - } break; + needle=glyph2; + r=pairValueCount-1; + l=0; - case 2: { - stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); - stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); - if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? - 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); + // Binary search. + while (l <= r) { + stbtt_uint16 secondGlyph; + stbtt_uint8 *pairValue; + m = (l + r) >> 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; + } + } + } else + return 0; + break; + } - stbtt_uint16 class1Count = ttUSHORT(table + 12); - stbtt_uint16 class2Count = ttUSHORT(table + 14); + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? + 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); - if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed - if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed + stbtt_uint16 class1Count = ttUSHORT(table + 12); + stbtt_uint16 class2Count = ttUSHORT(table + 14); - stbtt_uint8 *class1Records = table + 16; - stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count); - stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class); - return xAdvance; - } else - return 0; - } break; + if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed + if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed - default: { - // Unsupported definition type - return 0; - break; - }; - } - } - break; - }; + stbtt_uint8 *class1Records = table + 16; + stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count); + stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } else + return 0; + break; + } default: - // TODO: Implement other stuff. - break; - } - } + return 0; // Unsupported position format + } + } + } - return 0; + return 0; } STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) From 46c259ff903a4951f1acf0e129b9648a54eb8b75 Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 03:32:04 -0700 Subject: [PATCH 08/19] stb_sprintf: PUBLICDEC on declarations, as intended The code was using PUBLICDEF on both declarations and definitions. Fixes issue #458. --- stb_sprintf.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stb_sprintf.h b/stb_sprintf.h index 3152ff3..e055697 100644 --- a/stb_sprintf.h +++ b/stb_sprintf.h @@ -199,13 +199,13 @@ typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); #define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names #endif -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); -STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); #endif // STB_SPRINTF_H_INCLUDE From afc9c16bfd5be9ce81e0e0e1255b47d38218c773 Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 03:43:15 -0700 Subject: [PATCH 09/19] stb_truetype: Change spelling of fallthrough comment To pacify GCC warnings at -Wimplicit-fallthrough=4. Why the all-caps version works and the others don't, I'm not sure; the GCC manual page lists regular expressions that GCC is checking for but does not say which regular expression syntax variant it's using, whether it's looking for a substring match or a full match for the comment, and what exactly the text being matched against is for a single-line comment. Sigh. Fixes issue #507. --- stb_truetype.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stb_truetype.h b/stb_truetype.h index 783ecfa..667da88 100644 --- a/stb_truetype.h +++ b/stb_truetype.h @@ -2136,7 +2136,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); has_subrs = 1; } - // fallthrough + // FALLTHROUGH case 0x1D: // callgsubr if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); v = (int) s[--sp]; From 8af4d40950e38301d3ce8c6cda380aa28ba8c710 Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 03:58:30 -0700 Subject: [PATCH 10/19] stb_c_lexer: Move token enum defn to header portion Required some tinkering, hope I didn't mess the logic up. This now requires the config section to be set for the header file, and different sites that include it should agree on what the values are. This is kind of iffy but hard to avoid. Fixes issue #647. --- stb_c_lexer.h | 126 +++++++++++++++++++++++++++----------------------- 1 file changed, 67 insertions(+), 59 deletions(-) diff --git a/stb_c_lexer.h b/stb_c_lexer.h index 0cb9f39..f8da55d 100644 --- a/stb_c_lexer.h +++ b/stb_c_lexer.h @@ -42,13 +42,19 @@ // // See end of file for license information. -#ifdef STB_C_LEXER_IMPLEMENTATION #ifndef STB_C_LEXER_DEFINITIONS // to change the default parsing rules, copy the following lines // into your C/C++ file *before* including this, and then replace -// the Y's with N's for the ones you don't want. +// the Y's with N's for the ones you don't want. This needs to be +// set to the same values for every place in your program where +// stb_c_lexer.h is included. // --BEGIN-- +#if defined(Y) || defined(N) +#error "Can only use stb_c_lexer in contexts where the preprocessor symbols 'Y' and 'N' are not defined" +#endif + + #define STB_C_LEX_C_DECIMAL_INTS Y // "0|[1-9][0-9]*" CLEX_intlit #define STB_C_LEX_C_HEX_INTS Y // "0x[0-9a-fA-F]+" CLEX_intlit #define STB_C_LEX_C_OCTAL_INTS Y // "[0-7]+" CLEX_intlit @@ -97,7 +103,6 @@ #define STB_C_LEXER_DEFINITIONS // This line prevents the header file from replacing your definitions // --END-- -#endif #endif #ifndef INCLUDE_STB_C_LEXER_H @@ -167,35 +172,11 @@ extern void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, } #endif -#endif // INCLUDE_STB_C_LEXER_H - -#ifdef STB_C_LEXER_IMPLEMENTATION - - #if defined(Y) || defined(N) - #error "Can only use stb_c_lexer in contexts where the preprocessor symbols 'Y' and 'N' are not defined" - #endif - - // Hacky definitions so we can easily #if on them #define Y(x) 1 #define N(x) 0 -#if STB_C_LEX_INTEGERS_AS_DOUBLES(x) -typedef double stb__clex_int; -#define intfield real_number -#define STB__clex_int_as_double -#else -typedef long stb__clex_int; -#define intfield int_number -#endif - -// Convert these config options to simple conditional #defines so we can more -// easily test them once we've change the meaning of Y/N - -#if STB_C_LEX_PARSE_SUFFIXES(x) -#define STB__clex_parse_suffixes -#endif - +// Config variable that influence which lexer tokens get declared need to go here #if STB_C_LEX_C_DECIMAL_INTS(x) || STB_C_LEX_C_HEX_INTS(x) || STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) #define STB__clex_define_int #endif @@ -204,35 +185,6 @@ typedef long stb__clex_int; #define STB__clex_define_shifts #endif -#if STB_C_LEX_C99_HEX_FLOATS(x) -#define STB__clex_hex_floats -#endif - -#if STB_C_LEX_C_HEX_INTS(x) -#define STB__clex_hex_ints -#endif - -#if STB_C_LEX_C_DECIMAL_INTS(x) -#define STB__clex_decimal_ints -#endif - -#if STB_C_LEX_C_OCTAL_INTS(x) -#define STB__clex_octal_ints -#endif - -#if STB_C_LEX_C_DECIMAL_FLOATS(x) -#define STB__clex_decimal_floats -#endif - -#if STB_C_LEX_DISCARD_PREPROCESSOR(x) -#define STB__clex_discard_preprocessor -#endif - -#if STB_C_LEX_USE_STDLIB(x) && (!defined(STB__clex_hex_floats) || __STDC_VERSION__ >= 199901L) -#define STB__CLEX_use_stdlib -#include -#endif - // Now pick a definition of Y/N that's conducive to // defining the enum of token names. #if STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) || defined(STB_C_LEXER_SELF_TEST) @@ -287,12 +239,68 @@ enum CLEX_first_unused_token -#undef Y -#define Y(a) a }; +#undef Y +#undef N + +#endif // INCLUDE_STB_C_LEXER_H + +#ifdef STB_C_LEXER_IMPLEMENTATION + +// Hacky definitions so we can easily #if on them +#define Y(x) 1 +#define N(x) 0 + +#if STB_C_LEX_INTEGERS_AS_DOUBLES(x) +typedef double stb__clex_int; +#define intfield real_number +#define STB__clex_int_as_double +#else +typedef long stb__clex_int; +#define intfield int_number +#endif + +// Convert these config options to simple conditional #defines so we can more +// easily test them once we've change the meaning of Y/N + +#if STB_C_LEX_PARSE_SUFFIXES(x) +#define STB__clex_parse_suffixes +#endif + +#if STB_C_LEX_C99_HEX_FLOATS(x) +#define STB__clex_hex_floats +#endif + +#if STB_C_LEX_C_HEX_INTS(x) +#define STB__clex_hex_ints +#endif + +#if STB_C_LEX_C_DECIMAL_INTS(x) +#define STB__clex_decimal_ints +#endif + +#if STB_C_LEX_C_OCTAL_INTS(x) +#define STB__clex_octal_ints +#endif + +#if STB_C_LEX_C_DECIMAL_FLOATS(x) +#define STB__clex_decimal_floats +#endif + +#if STB_C_LEX_DISCARD_PREPROCESSOR(x) +#define STB__clex_discard_preprocessor +#endif + +#if STB_C_LEX_USE_STDLIB(x) && (!defined(STB__clex_hex_floats) || __STDC_VERSION__ >= 199901L) +#define STB__CLEX_use_stdlib +#include +#endif + // Now for the rest of the file we'll use the basic definition where // where Y expands to its contents and N expands to nothing +#undef Y +#define Y(a) a #undef N #define N(a) From 315e979f34a1f8479bfc82481fb60fd22965fd1d Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 04:10:52 -0700 Subject: [PATCH 11/19] stb_truetype: Add protoype for stbtt_FindSVGDoc Fixes issue #979. --- stb_truetype.h | 1 + 1 file changed, 1 insertion(+) diff --git a/stb_truetype.h b/stb_truetype.h index 667da88..99ec365 100644 --- a/stb_truetype.h +++ b/stb_truetype.h @@ -855,6 +855,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); // frees the data allocated above +STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); // fills svg with the character's SVG data. From b14a80784c689979b75f6e8929a7d7b4b4a86678 Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 14:35:46 -0700 Subject: [PATCH 12/19] stb_image_write: Support writing BMPs with alpha Fixes issue #1159. --- stb_image_write.h | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/stb_image_write.h b/stb_image_write.h index a909e82..f1a1773 100644 --- a/stb_image_write.h +++ b/stb_image_write.h @@ -491,11 +491,22 @@ static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { - int pad = (-x*3) & 3; - return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, - "11 4 22 4" "4 44 22 444444", - 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header - 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + if (comp != 4) { + // write RGB bitmap + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + } else { + // RGBA bitmaps need a v4 header + // use BI_BITFIELDS mode with 32bpp and alpha mask + // (straight BI_RGB with alpha mask doesn't work in most readers) + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0, + "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444", + 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header + 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header + } } STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) @@ -1619,6 +1630,7 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const /* Revision history 1.15 ( ) make Deflate code emit uncompressed blocks when it would otherwise expand + support writing BMPs with alpha channel 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels 1.13 1.12 From 2af2e692197267092f737db562817231feb3b5be Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 14:59:13 -0700 Subject: [PATCH 13/19] stb_ds: Fix addn when n=0 In this case, the array pointer may still be 0 even after the maybegrow. Fixes issue #1015. --- stb_ds.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stb_ds.h b/stb_ds.h index f6ec239..530f504 100644 --- a/stb_ds.h +++ b/stb_ds.h @@ -544,8 +544,8 @@ extern void * stbds_shmode_func(size_t elemsize, int mode); #define stbds_arrpush stbds_arrput // synonym #define stbds_arrpop(a) (stbds_header(a)->length--, (a)[stbds_header(a)->length]) #define stbds_arraddn(a,n) ((void)(stbds_arraddnindex(a, n))) // deprecated, use one of the following instead: -#define stbds_arraddnptr(a,n) (stbds_arrmaybegrow(a,n), stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) -#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), stbds_header(a)->length += (n), stbds_header(a)->length-(n)) +#define stbds_arraddnptr(a,n) (stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a)) +#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a)) #define stbds_arraddnoff stbds_arraddnindex #define stbds_arrlast(a) ((a)[stbds_header(a)->length-1]) #define stbds_arrfree(a) ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL) From 013884b53b3e346c8d497ca65b438857ffefbad9 Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 15:38:42 -0700 Subject: [PATCH 14/19] stb_sprintf: Fix string length calc Factor out string length computation into helper func, comment it a bit more, always use a limit to avoid 32b unsigned overflow, and avoid reading past the bounds of non-0-terminated strings given with specified precision. Fixes issue #966. --- stb_sprintf.h | 74 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/stb_sprintf.h b/stb_sprintf.h index e055697..46369d5 100644 --- a/stb_sprintf.h +++ b/stb_sprintf.h @@ -300,6 +300,46 @@ static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) } } +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) +{ + char const * sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) { static char hex[] = "0123456789abcdefxp"; @@ -543,37 +583,9 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, s = va_arg(va, char *); if (s == 0) s = (char *)"null"; - // get the length - sn = s; - for (;;) { - if ((((stbsp__uintptr)sn) & 3) == 0) - break; - lchk: - if (sn[0] == 0) - goto ld; - ++sn; - } - n = 0xffffffff; - if (pr >= 0) { - n = (stbsp__uint32)(sn - s); - if (n >= (stbsp__uint32)pr) - goto ld; - n = ((stbsp__uint32)(pr - n)) >> 2; - } - while (n) { - stbsp__uint32 v = *(stbsp__uint32 *)sn; - if ((v - 0x01010101) & (~v) & 0x80808080UL) - goto lchk; - sn += 4; - --n; - } - goto lchk; - ld: - - l = (stbsp__uint32)(sn - s); - // clamp to precision - if (l > (stbsp__uint32)pr) - l = pr; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); lead[0] = 0; tail[0] = 0; pr = 0; From 3be65f1c0c5e605bcc3d75a6466328ddb9e68945 Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 15:43:47 -0700 Subject: [PATCH 15/19] stb_sprintf: Small simplification This was intentional but we might as well just set cs=0 directly here. Fixes issue #997. --- stb_sprintf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stb_sprintf.h b/stb_sprintf.h index 46369d5..0271568 100644 --- a/stb_sprintf.h +++ b/stb_sprintf.h @@ -1022,7 +1022,7 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, lead[0] = 0; if (pr == 0) { l = 0; - cs = (((l >> 4) & 15)) << 24; + cs = 0; goto scopy; } } From 06b6009e3be9985a47829976c3c00711bb51d4f3 Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 15:51:56 -0700 Subject: [PATCH 16/19] stb_sprintf: Fix unused variable warning with STB_SPRINTF_NOFLOAT Fixes issue #986. --- stb_sprintf.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/stb_sprintf.h b/stb_sprintf.h index 0271568..e491425 100644 --- a/stb_sprintf.h +++ b/stb_sprintf.h @@ -187,6 +187,12 @@ PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): #define STBSP__ATTRIBUTE_FORMAT(fmt,va) #endif +#ifdef _MSC_VER +#define STBSP__NOTUSED(v) (void)(v) +#else +#define STBSP__NOTUSED(v) (void)sizeof(v) +#endif + #include // for va_arg(), va_list() #include // size_t, ptrdiff_t @@ -626,8 +632,8 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, lead[0] = 0; tail[0] = 0; pr = 0; - dp = 0; cs = 0; + STBSP__NOTUSED(dp); goto scopy; #else case 'A': // hex float From d2476c384527020cf145d359e8a2a5689e1c51cd Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Thu, 8 Jul 2021 00:21:08 -0700 Subject: [PATCH 17/19] stb_sprintf: GCC compilation fix Fixes issue #1010. --- stb_sprintf.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stb_sprintf.h b/stb_sprintf.h index e491425..82358a5 100644 --- a/stb_sprintf.h +++ b/stb_sprintf.h @@ -154,8 +154,8 @@ PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): #endif #endif #endif -#elif __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - #if __SANITIZE_ADDRESS__ +#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ #define STBSP__ASAN __attribute__((__no_sanitize_address__)) #endif #endif From 1e1efbdc75487a6c29d76314eb174a6a346a4d8e Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Thu, 8 Jul 2021 00:24:00 -0700 Subject: [PATCH 18/19] stb_image_write: Make globals extern "C" in C++ Fixes issue #921 --- stb_image_write.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stb_image_write.h b/stb_image_write.h index f1a1773..d7ed1d3 100644 --- a/stb_image_write.h +++ b/stb_image_write.h @@ -167,9 +167,9 @@ LICENSE #endif #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; +STBIWDEF int stbi_write_tga_with_rle; +STBIWDEF int stbi_write_png_compression_level; +STBIWDEF int stbi_write_force_png_filter; #endif #ifndef STBI_WRITE_NO_STDIO From 2b667e4d30e8fd04aebec170763a2c118f635652 Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Thu, 8 Jul 2021 00:32:38 -0700 Subject: [PATCH 19/19] stb_rect_pack: Several minor fixes 1. Always use large rects mode to avoid definition of stbrp_coord in header file depending on implementation #defines 2. Expose STBRP__MAXVAL to users 3. Fix value of STBRP__MAXVAL for large rect mode (stbrp_coord is a 32-bit int, so needs to be <=0x7fffffff, 0xffffffff doesn't work) 4. Add comment at the top about which #define to set to get the implementation. Fixes issue #1143, or rather, replaces that pull request. --- stb_rect_pack.h | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/stb_rect_pack.h b/stb_rect_pack.h index 5c848de..48362eb 100644 --- a/stb_rect_pack.h +++ b/stb_rect_pack.h @@ -1,9 +1,15 @@ -// stb_rect_pack.h - v1.00 - public domain - rectangle packing +// stb_rect_pack.h - v1.01 - public domain - rectangle packing // Sean Barrett 2014 // // Useful for e.g. packing rectangular textures into an atlas. // Does not do rotation. // +// Before #including, +// +// #define STB_RECT_PACK_IMPLEMENTATION +// +// in the file that you want to have the implementation. +// // Not necessarily the awesomest packing method, but better than // the totally naive one in stb_truetype (which is primarily what // this is meant to replace). @@ -35,6 +41,7 @@ // // Version history: // +// 1.01 ( ) always use large rect mode, expose STBRP__MAXVAL in public section // 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles // 0.99 (2019-02-07) warning fixes // 0.11 (2017-03-03) return packing success/fail result @@ -75,11 +82,10 @@ typedef struct stbrp_context stbrp_context; typedef struct stbrp_node stbrp_node; typedef struct stbrp_rect stbrp_rect; -#ifdef STBRP_LARGE_RECTS typedef int stbrp_coord; -#else -typedef unsigned short stbrp_coord; -#endif + +#define STBRP__MAXVAL 0x7fffffff +// Mostly for internal use, but this is the maximum supported coordinate value. STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); // Assign packed locations to rectangles. The rectangles are of type @@ -253,9 +259,6 @@ STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_ou STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) { int i; -#ifndef STBRP_LARGE_RECTS - STBRP_ASSERT(width <= 0xffff && height <= 0xffff); -#endif for (i=0; i < num_nodes-1; ++i) nodes[i].next = &nodes[i+1]; @@ -274,11 +277,7 @@ STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, context->extra[0].y = 0; context->extra[0].next = &context->extra[1]; context->extra[1].x = (stbrp_coord) width; -#ifdef STBRP_LARGE_RECTS context->extra[1].y = (1<<30); -#else - context->extra[1].y = 65535; -#endif context->extra[1].next = NULL; } @@ -538,12 +537,6 @@ static int rect_original_order(const void *a, const void *b) return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); } -#ifdef STBRP_LARGE_RECTS -#define STBRP__MAXVAL 0xffffffff -#else -#define STBRP__MAXVAL 0xffff -#endif - STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) { int i, all_rects_packed = 1;