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) {