From 4e580cf9edb2213635fb6dff9295c6975ca98d6e Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Wed, 10 Sep 2014 15:11:35 -0700 Subject: [PATCH 1/5] fix compile --- tests/resample_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 9fb6593..c172866 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -45,7 +45,7 @@ void* stbir_malloc(void* context, size_t size) void stbir_free(void* context, void* memory) { if (!context) - return free(memory); + free(memory); } void stbir_progress(float p) From 30c7a981ec335c2267b3cec2955cde42376b8fd9 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Thu, 11 Sep 2014 01:47:50 -0700 Subject: [PATCH 2/5] compile as C; fix some unsigned/signed comparisons; avoid round() since it's not in pre-C99 C; remove MAX_CHANNELS since I never ended up needing it; rename STBIR_EPSILON to STBIR_ALPHA_EPSILON; don't use STBIR_ALPHA_EPSILON on float input (can't remove it properly due to numeric precision, and they can do it themselves); optimize subtraction of STBIR_ALPHA_EPSILON; sorry i forgot to commit these separately!; --- stb_image_resize.h | 54 +++++++++++++++++++++++-------------------------- tests/resample_test.cpp | 31 ++++++++++++++++++---------- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index f454286..2c6b6a6 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -19,14 +19,11 @@ supported filters see the stbir_filter enum. To add a new filter, write a filter function and add it to stbir__filter_info_table. - STBIR_MAX_CHANNELS: defaults to 16, if you need more, bump it up - Revisions: 0.50 (2014-??-??) first released version TODO: Installable filters - Specify wrap and filter modes independently for each axis Resize that respects alpha test coverage (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) @@ -130,7 +127,7 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels // * Alpha-channel can be processed separately // * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE // * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) -// * Filters can be weighted by alpha channel (if flags&STBIR_FLAG_NONPREMUL_ALPHA) +// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_PREMULTIPLIED_ALPHA) // * Filter can be selected explicitly // * uint16 image type // * sRGB colorspace available for all types @@ -246,8 +243,8 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int // that behavior (it may interfere if you have floating point images with // very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to // disable it. -#ifndef STBIR_EPSILON -#define STBIR_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) +#ifndef STBIR_ALPHA_EPSILON +#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) #endif // @@ -317,10 +314,6 @@ typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1] #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL #endif -#ifndef STBIR_MAX_CHANNELS -#define STBIR_MAX_CHANNELS 16 -#endif - #define STBIR__UNUSED_PARAM(s) s=s; // must match stbir_datatype @@ -534,11 +527,11 @@ static unsigned char stbir__linear_to_srgb_uchar(float f) static float stbir__filter_trapezoid(float x, float scale) { - STBIR__DEBUG_ASSERT(scale <= 1); - x = (float)fabs(x); - float halfscale = scale / 2; float t = 0.5f + halfscale; + STBIR__DEBUG_ASSERT(scale <= 1); + + x = (float)fabs(x); if (x >= t) return 0; @@ -870,6 +863,7 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) int i; for (i = 0; i < stbir_info->output_w; i++) { + float scale; float total = 0; int j; for (j = 0; j < num_contributors; j++) @@ -886,7 +880,7 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) STBIR__DEBUG_ASSERT(total > 0.9f); STBIR__DEBUG_ASSERT(total < 1.1f); - float scale = 1 / total; + scale = 1 / total; for (j = 0; j < num_contributors; j++) { @@ -1097,7 +1091,11 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) int decode_pixel_index = x * channels; // If the alpha value is 0 it will clobber the color values. Make sure it's not. - float alpha = (decode_buffer[decode_pixel_index + alpha_channel] += STBIR_EPSILON); + float alpha = decode_buffer[decode_pixel_index + alpha_channel]; + if (stbir_info->type != STBIR_TYPE_FLOAT) { + alpha += STBIR_ALPHA_EPSILON; + decode_buffer[decode_pixel_index + alpha_channel] = alpha; + } for (c = 0; c < channels; c++) { @@ -1270,7 +1268,6 @@ static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buff } -// @OPTIMIZE: embed stbir__encode_pixel and move switch out of per-pixel loop static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) { int x; @@ -1284,14 +1281,14 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void int pixel_index = x*channels; float alpha = encode_buffer[pixel_index + alpha_channel]; - STBIR__DEBUG_ASSERT(alpha > 0); float reciprocal_alpha = alpha ? 1.0f / alpha : 0; for (n = 0; n < channels; n++) if (n != alpha_channel) encode_buffer[pixel_index + n] *= reciprocal_alpha; - // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. Remove it now. - encode_buffer[pixel_index + alpha_channel] -= STBIR_EPSILON; + // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. + // Because we only add it for integer types, it will automatically be discarded on integer + // conversion. } } #endif @@ -1306,7 +1303,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void for (n = 0; n < channels; n++) { int index = pixel_index + n; - ((unsigned char*)output_buffer)[index] = (unsigned char)(round(stbir__saturate(encode_buffer[index]) * 255)); + ((unsigned char*)output_buffer)[index] = (unsigned char)(floor(stbir__saturate(encode_buffer[index]) * 255 + 0.5f)); } } break; @@ -1323,7 +1320,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void } if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned char*)output_buffer)[pixel_index + alpha_channel] = (unsigned char)(round(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 255)); + ((unsigned char*)output_buffer)[pixel_index + alpha_channel] = (unsigned char)(floor(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 255+0.5f)); } break; @@ -1335,7 +1332,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void for (n = 0; n < channels; n++) { int index = pixel_index + n; - ((unsigned short*)output_buffer)[index] = (unsigned short)(round(stbir__saturate(encode_buffer[index]) * 65535)); + ((unsigned short*)output_buffer)[index] = (unsigned short)(floor(stbir__saturate(encode_buffer[index]) * 65535+0.5f)); } } break; @@ -1348,11 +1345,11 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void for (n = 0; n < channels; n++) { int index = pixel_index + n; - ((unsigned short*)output_buffer)[index] = (unsigned short)(round(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535)); + ((unsigned short*)output_buffer)[index] = (unsigned short)(floor(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535 + 0.5f)); } if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = (unsigned short)(round(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 65535)); + ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = (unsigned short)(floor(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 65535 + 0.5f)); } break; @@ -1365,7 +1362,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void for (n = 0; n < channels; n++) { int index = pixel_index + n; - ((unsigned int*)output_buffer)[index] = (unsigned int)(round(((double)stbir__saturate(encode_buffer[index])) * 4294967295)); + ((unsigned int*)output_buffer)[index] = (unsigned int)(floor(((double)stbir__saturate(encode_buffer[index])) * 4294967295 + 0.5f)); } } break; @@ -1378,11 +1375,11 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void for (n = 0; n < channels; n++) { int index = pixel_index + n; - ((unsigned int*)output_buffer)[index] = (unsigned int)(round(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295)); + ((unsigned int*)output_buffer)[index] = (unsigned int)(floor(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295 + 0.5f)); } if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)(round(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295)); + ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)(floor(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295 + 0.5f)); } break; @@ -1769,10 +1766,9 @@ static int stbir__resize_allocated(stbir__info *info, memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); #endif - STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); STBIR_ASSERT(info->channels >= 0); - if (info->channels > STBIR_MAX_CHANNELS || info->channels < 0) + if (info->channels < 0) return 0; STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index c50ef3c..1cbd047 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -425,10 +425,10 @@ void test_premul() float ga = (float)25 / 4294967296; float a = (ra + ga) / 2; - STBIR_ASSERT(output[0] == (int)(r * ra / 2 / a * 4294967296 + 0.5f)); // 232 - STBIR_ASSERT(output[1] == (int)(g * ga / 2 / a * 4294967296 + 0.5f)); // 23 + STBIR_ASSERT(output[0] == (unsigned int)(r * ra / 2 / a * 4294967296 + 0.5f)); // 232 + STBIR_ASSERT(output[1] == (unsigned int)(g * ga / 2 / a * 4294967296 + 0.5f)); // 23 STBIR_ASSERT(output[2] == 0); - STBIR_ASSERT(output[3] == (int)(a * 4294967296 + 0.5f)); // 140 + STBIR_ASSERT(output[3] == (unsigned int)(a * 4294967296 + 0.5f)); // 140 // Now a test to make sure it doesn't clobber existing values. @@ -606,7 +606,7 @@ void verify_box(void) STBIR_ASSERT(output11[0][0] == ((t+32)>>6)); } -void verify_filter_normalized(stbir_filter filter, int output_size, int value) +void verify_filter_normalized(stbir_filter filter, int output_size, unsigned int value) { int i, j; unsigned int output[64]; @@ -618,6 +618,11 @@ void verify_filter_normalized(stbir_filter filter, int output_size, int value) STBIR_ASSERT(value == output[j*output_size + i]); } +float round2(float f) +{ + return (float) floor(f+0.5f); // round() isn't C standard pre-C99 +} + void test_filters(void) { int i,j; @@ -673,7 +678,7 @@ void test_filters(void) { // This test is designed to produce coefficients that are very badly denormalized. - int v = 556; + unsigned int v = 556; unsigned int input[100 * 100]; unsigned int output[11 * 11]; @@ -698,13 +703,13 @@ void test_filters(void) stbir_resize(input, 3, 1, 0, output, 2, 1, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); - STBIR_ASSERT(output[0] == (int)round((float)(input[0] * 2 + input[1]) / 3)); - STBIR_ASSERT(output[1] == (int)round((float)(input[2] * 2 + input[1]) / 3)); + STBIR_ASSERT(output[0] == (unsigned int)round2((float)(input[0] * 2 + input[1]) / 3)); + STBIR_ASSERT(output[1] == (unsigned int)round2((float)(input[2] * 2 + input[1]) / 3)); stbir_resize(input, 1, 3, 0, output, 1, 2, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); - STBIR_ASSERT(output[0] == (int)round((float)(input[0] * 2 + input[1]) / 3)); - STBIR_ASSERT(output[1] == (int)round((float)(input[2] * 2 + input[1]) / 3)); + STBIR_ASSERT(output[0] == (unsigned int)round2((float)(input[0] * 2 + input[1]) / 3)); + STBIR_ASSERT(output[1] == (unsigned int)round2((float)(input[2] * 2 + input[1]) / 3)); } { @@ -767,6 +772,7 @@ void test_suite(int argc, char **argv) else barbara = "barbara.png"; + // check what cases we need normalization for #if 1 { float x, y; @@ -804,8 +810,11 @@ void test_suite(int argc, char **argv) } #endif - for (i = 0; i < 256; i++) - STBIR_ASSERT(stbir__linear_to_srgb_uchar(stbir__srgb_to_linear(float(i) / 255)) == i); + for (i = 0; i < 256; i++) { + float f = stbir__srgb_to_linear(float(i) / 255); + int n = stbir__linear_to_srgb_uchar(f); + STBIR_ASSERT(n == i); + } #if 0 // linear_to_srgb_uchar table for (i=0; i < 256; ++i) { From 16d68d14f81e79779b5239239f5ee5df9d0c7340 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Thu, 11 Sep 2014 02:05:53 -0700 Subject: [PATCH 3/5] fix stbir__linear_to_srgb_uchar: 1. table stored threshhold of transition from i to i+1, but wants to be i-1 to i 2. table was computed by dividing uchar by 256.0 instead of 255.0, causing it to be 100% wrong --- stb_image_resize.h | 89 +++++++++++++++++++++++-------------------------- tests/resample_test.cpp | 17 +++++----- 2 files changed, 50 insertions(+), 56 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 2c6b6a6..7548b54 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -450,38 +450,38 @@ static float stbir__srgb_uchar_to_linear_float[256] = { // sRGB transition values, scaled by 1<<28 static int stbir__srgb_offset_to_linear_scaled[256] = { - 40579, 121738, 202897, 284056, 365216, 446375, 527534, 608693, - 689852, 771011, 852421, 938035, 1028466, 1123787, 1224073, 1329393, - 1439819, 1555418, 1676257, 1802402, 1933917, 2070867, 2213313, 2361317, - 2514938, 2674237, 2839271, 3010099, 3186776, 3369359, 3557903, 3752463, - 3953090, 4159840, 4372764, 4591913, 4817339, 5049091, 5287220, 5531775, - 5782804, 6040356, 6304477, 6575216, 6852618, 7136729, 7427596, 7725263, - 8029775, 8341176, 8659511, 8984821, 9317151, 9656544, 10003040, 10356683, - 10717513, 11085572, 11460901, 11843540, 12233529, 12630908, 13035717, 13447994, - 13867779, 14295110, 14730025, 15172563, 15622760, 16080655, 16546285, 17019686, - 17500894, 17989948, 18486882, 18991734, 19504536, 20025326, 20554138, 21091010, - 21635972, 22189062, 22750312, 23319758, 23897432, 24483368, 25077600, 25680162, - 26291086, 26910406, 27538152, 28174360, 28819058, 29472282, 30134062, 30804430, - 31483418, 32171058, 32867378, 33572412, 34286192, 35008744, 35740104, 36480296, - 37229356, 37987316, 38754196, 39530036, 40314860, 41108700, 41911584, 42723540, - 43544600, 44374792, 45214140, 46062680, 46920440, 47787444, 48663720, 49549300, - 50444212, 51348480, 52262136, 53185204, 54117712, 55059688, 56011160, 56972156, - 57942704, 58922824, 59912552, 60911908, 61920920, 62939616, 63968024, 65006168, - 66054072, 67111760, 68179272, 69256616, 70343832, 71440936, 72547952, 73664920, - 74791848, 75928776, 77075720, 78232704, 79399760, 80576904, 81764168, 82961576, - 84169152, 85386920, 86614904, 87853120, 89101608, 90360384, 91629480, 92908904, - 94198688, 95498864, 96809440, 98130456, 99461928, 100803872, 102156320, 103519296, - 104892824, 106276920, 107671616, 109076928, 110492880, 111919504, 113356808, 114804824, - 116263576, 117733080, 119213360, 120704448, 122206352, 123719104, 125242720, 126777232, - 128322648, 129879000, 131446312, 133024600, 134613888, 136214192, 137825552, 139447968, - 141081456, 142726080, 144381808, 146048704, 147726768, 149416016, 151116496, 152828192, - 154551168, 156285408, 158030944, 159787808, 161556000, 163335568, 165126512, 166928864, - 168742640, 170567856, 172404544, 174252704, 176112384, 177983568, 179866320, 181760640, - 183666528, 185584032, 187513168, 189453952, 191406400, 193370544, 195346384, 197333952, - 199333264, 201344352, 203367216, 205401904, 207448400, 209506752, 211576960, 213659056, - 215753056, 217858976, 219976832, 222106656, 224248464, 226402272, 228568096, 230745952, - 232935872, 235137872, 237351968, 239578176, 241816512, 244066992, 246329648, 248604512, - 250891568, 253190848, 255502368, 257826160, 260162240, 262510608, 264871312, 267244336, + 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, + 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, + 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, + 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, + 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, + 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, + 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, + 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, + 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, + 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, + 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, + 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, + 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, + 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, + 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, + 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, + 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, + 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, + 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, + 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, + 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, + 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, + 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, + 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, + 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968, + 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184, + 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992, + 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968, + 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480, + 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656, + 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464, + 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, }; static float stbir__srgb_to_linear(float f) @@ -506,23 +506,16 @@ static unsigned char stbir__linear_to_srgb_uchar(float f) int v = 0; int i; - // Everything below 128 is off by 1. This fixes that. - int fix = 0; + i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - // Adding 1 to 0 with the fix gives incorrect results for input 0. This fixes that. - if (x < 81000) - return 0; - - i = 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; else fix = 1; - i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - - return (unsigned char)v + fix; + return (unsigned char) v; } static float stbir__filter_trapezoid(float x, float scale) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 1cbd047..3a90c4b 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -810,20 +810,21 @@ void test_suite(int argc, char **argv) } #endif +#if 0 // linear_to_srgb_uchar table + for (i=0; i < 256; ++i) { + float f = stbir__srgb_to_linear((i-0.5f)/255.0f); + printf("%9d, ", (int) ((f) * (1<<28))); + if ((i & 7) == 7) + printf("\n"); + } +#endif + for (i = 0; i < 256; i++) { float f = stbir__srgb_to_linear(float(i) / 255); int n = stbir__linear_to_srgb_uchar(f); STBIR_ASSERT(n == i); } -#if 0 // linear_to_srgb_uchar table - for (i=0; i < 256; ++i) { - float f = stbir__srgb_to_linear((i+0.5f)/256.0f); - printf("%9d, ", (int) ((f) * (1<<28))); - if ((i & 7) == 7) - printf("\n"); - } -#endif test_filters(); From 7a4f1f4665585ce3c8da1ba0320b9e77aaf3a24c Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Thu, 11 Sep 2014 02:10:37 -0700 Subject: [PATCH 4/5] update with new tests for srgb conversion --- tests/resample_test.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 3a90c4b..6028cd5 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -819,12 +819,23 @@ void test_suite(int argc, char **argv) } #endif + // old tests that hacky fix worked on - test that + // every uint8 maps to itself for (i = 0; i < 256; i++) { float f = stbir__srgb_to_linear(float(i) / 255); int n = stbir__linear_to_srgb_uchar(f); STBIR_ASSERT(n == i); } + // new tests that hacky fix failed for - test that + // values adjacent to uint8 round to nearest uint8 + for (i = 0; i < 256; i++) { + for (float y = -0.49f; y <= 0.491f; y += 0.01f) { + float f = stbir__srgb_to_linear((i+y) / 255.0f); + int n = stbir__linear_to_srgb_uchar(f); + STBIR_ASSERT(n == i); + } + } test_filters(); From 27f26f8337714c6d038554e40b3f31478b9c4054 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Thu, 11 Sep 2014 03:02:20 -0700 Subject: [PATCH 5/5] Documentation; rename STBIR_FLAG_PREMULTIPLED_ALPHA to STBIR_FLAG_ALPHA_PREMULTIPLIED so that both flags have "ALPHA" first --- stb_image_resize.h | 203 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 146 insertions(+), 57 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 7548b54..7a228f9 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1,34 +1,132 @@ -/* stb_image_resize - v0.50 - public domain image resampling - no warranty implied; use at your own risk +/* stb_image_resize - v0.90 - public domain image resizing + by Jorge L Rodriguez (@VinoBS) - 2014 + http://github.com/nothings/stb - Do this: - #define STB_IMAGE_RESIZE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. + Written with emphasis on usage and speed. Only scaling is + currently supported, no rotations or translations. - #define STBIR_ASSERT(x) to avoid using assert.h. + DOCUMENTATION - #define STBIR_MALLOC(size,context) and STBIR_FREE(ptr,context) to avoid using stdlib.h malloc. - Each function makes exactly one call to malloc/free, so to avoid allocations, - pass in a temp memory block as context and return that from MALLOC. + COMPILING & LINKING + In one C/C++ file that #includes this file, do this: + #define STB_IMAGE_RESIZE_IMPLEMENTATION + before the #include. That will create the implementation in that file. - QUICK NOTES: - Written with emphasis on usage and speed. Only the resize operation is - currently supported, no rotations or translations. + API + See the "header file" section of the source for API documentation. - Supports arbitrary resize for separable filters. For a list of - supported filters see the stbir_filter enum. To add a new filter, - write a filter function and add it to stbir__filter_info_table. + MEMORY ALLOCATION + The resize functions here perform a single memory allocation using + malloc. To control the memory allocation, before the #include that + triggers the implementation, do: - Revisions: - 0.50 (2014-??-??) first released version + #define STBIR_MALLOC(size,context) ... + #define STBIR_FREE(ptr,context) ... - TODO: - Installable filters - Resize that respects alpha test coverage - (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: - https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) + Each resize function makes exactly one call to malloc/free, so to use + temp memory, store the temp memory in the context and return that. - Initial implementation by Jorge L Rodriguez, @VinoBS + ASSERT + Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + + DEFAULT FILTERS + For functions which don't provide explicit control over what filters + to use, you can change the compile-time defaults with + + #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something + #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something + + See stbir_filter in the header-file section for the list of filters. + + NEW FILTERS + A number of 1D filter kernels are used. For a list of + supported filters see the stbir_filter enum. To add a new filter, + write a filter function and add it to stbir__filter_info_table. + + PROGRESS + For interactive use with slow resize operations, you can install + a progress-report callback: + + #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) + + The parameter val is a float which goes from 0 to 1 as progress is made. + + For example: + + void my_progress_report(float progress) + { + printf("Progress: %f%%\n", progress*100); + } + + ALPHA CHANNEL + Most of the resizing functions provide the ability to control how + the alpha channel of an image is processed. The important things + to know about this: + + 1. The best mathematically-behaved version of alpha to use is + called "premultiplied alpha", in which the other color channels + have had the alpha value multiplied in. If you use premultiplied + alpha, linear filtering (such as image resampling done by this + library, or performed in texture units on GPUs) does the "right + thing". While premultiplied alpha is standard in the movie CGI + industry, it is still uncommon in the videogame/real-time world. + + If you linearly filter non-premultiplied alpha, strange effects + occur. (For example, the average of 1% opaque bright green + and 99% opaque black produces 50% transparent dark green when + non-premultiplied, whereas premultiplied it produces 50% + transparent near-black. The former introduces green energy + that doesn't exist in the source.) + + 2. Artists should not edit premultiplied-alpha images; artists + want non-premultiplied alpha images. Thus, art tools generally output + non-premultiplied alpha images. + + 3. You will get best results in most cases by converting images + to premultiplied alpha before processing them mathematically. + + 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the + resizer does not do anything special for the alpha channel; + it is resampled identically to other channels. + + 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, + then the resizer weights the contribution of input pixels + based on their alpha values, or, equivalently, it multiplies + the alpha value into the color channels, resamples, then divides + by the resultant alpha value. Input pixels which have alpha=0 do + not contribute at all to output pixels unless _all_ of the input + pixels affecting that output pixel have alpha=0, in which case + the result for that pixel is the same as it would be without + STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for + input images in integer formats. For input images in float format, + input pixels with alpha=0 have no effect, and output pixels + which have alpha=0 will be 0 in all channels. (For float images, + you can manually achieve the same result by adding a tiny epsilon + value to the alpha channel of every image, and then subtracting + or clamping it at the end.) + + 6. You can separately control whether the alpha channel is + interpreted as linear or affected by the colorspace. By default + it is linear; you almost never want to apply the colorspace. + (For example, graphics hardware does not apply sRGB conversion + to the alpha channel.) + + ADDITIONAL CONTRIBUTORS + Sean Barrett: API design, optimizations + + REVISIONS + 0.90 (2014-??-??) first released version + + LICENSE + This software is in the public domain. Where that dedication is not + recognized, you are granted a perpetual, irrevocable license to copy + and modify this file as you see fit. + + TODO + Installable filters + Resize that respects alpha test coverage + (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: + https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) */ #ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H @@ -92,9 +190,9 @@ STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , i #define STBIR_ALPHA_CHANNEL_NONE -1 // Set this flag if your texture has premultiplied alpha. Otherwise, stbir will -// use alpha-correct resampling by multiplying the the specified alpha channel -// into all other channels before resampling, then dividing back out after. -#define STBIR_FLAG_PREMULTIPLIED_ALPHA (1 << 0) +// use alpha-weighted resampling (effectively premultiplying, resampling, +// then unpremultiplying). +#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) // The specified alpha channel should be handled as gamma-corrected value even // when doing sRGB operations. #define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) @@ -127,7 +225,7 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels // * Alpha-channel can be processed separately // * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE // * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) -// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_PREMULTIPLIED_ALPHA) +// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED) // * Filter can be selected explicitly // * uint16 image type // * sRGB colorspace available for all types @@ -224,29 +322,6 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int float s0, float t0, float s1, float t1); // (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. - -// Define this if you want a progress report. -// Example: -// void my_progress_report(float progress) -// { -// printf("Progress: %f%%\n", progress*100); -// } -// -// #define STBIR_PROGRESS_REPORT my_progress_report - -#ifndef STBIR_PROGRESS_REPORT -#define STBIR_PROGRESS_REPORT(float_0_to_1) -#endif - -// This value is added to alpha just before premultiplication to avoid -// zeroing out color values. It is equivalent to 2^-80. If you don't want -// that behavior (it may interfere if you have floating point images with -// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to -// disable it. -#ifndef STBIR_ALPHA_EPSILON -#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) -#endif - // // //// end header file ///////////////////////////////////////////////////// @@ -279,9 +354,8 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int #ifndef STBIR_MALLOC #include - -#define STBIR_MALLOC(x,c) malloc(x) -#define STBIR_FREE(x,c) free(x) +#define STBIR_MALLOC(size,c) malloc(size) +#define STBIR_FREE(ptr,c) free(ptr) #endif #ifndef _MSC_VER @@ -314,6 +388,21 @@ typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1] #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL #endif +#ifndef STBIR_PROGRESS_REPORT +#define STBIR_PROGRESS_REPORT(float_0_to_1) +#endif + +// This value is added to alpha just before premultiplication to avoid +// zeroing out color values. It is equivalent to 2^-80. If you don't want +// that behavior (it may interfere if you have floating point images with +// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to +// disable it. +#ifndef STBIR_ALPHA_EPSILON +#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) +#endif + + + #define STBIR__UNUSED_PARAM(s) s=s; // must match stbir_datatype @@ -1077,7 +1166,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) } #ifndef STBIR_NO_ALPHA_EPSILON - if (!(stbir_info->flags & STBIR_FLAG_PREMULTIPLIED_ALPHA)) + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) { for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++) { @@ -1267,7 +1356,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void int n; #ifndef STBIR_NO_ALPHA_EPSILON - if (!(stbir_info->flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) { for (x=0; x < num_pixels; ++x) { @@ -1773,9 +1862,9 @@ static int stbir__resize_allocated(stbir__info *info, return 0; if (alpha_channel < 0) - flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_PREMULTIPLIED_ALPHA; + flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; - if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) + if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); if (alpha_channel >= info->channels)