From d54e74092ed5c2d43d161b5bda88b9b4784b624e Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Mon, 21 Jul 2014 00:16:03 -0700 Subject: [PATCH 001/168] stb_resample initial implementation --- stb_resample.h | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/resample_test.c | 49 ++++++++++++ 2 files changed, 263 insertions(+) create mode 100644 stb_resample.h create mode 100644 tests/resample_test.c diff --git a/stb_resample.h b/stb_resample.h new file mode 100644 index 0000000..b5dae57 --- /dev/null +++ b/stb_resample.h @@ -0,0 +1,214 @@ +/* stb_resample - v0.1 - public domain image resampling +no warranty implied; use at your own risk + +Do this: +#define STB_RESAMPLE_IMPLEMENTATION +before you include this file in *one* C or C++ file to create the implementation. + +#define STBR_ASSERT(x) to avoid using assert.h. + +Latest revisions: + +See end of file for full revision history. + +Initial implementation by Jorge L Rodriguez +*/ + +#ifndef STBR_INCLUDE_STB_RESAMPLE_H +#define STBR_INCLUDE_STB_RESAMPLE_H + +// Basic usage: +// unsigned char *data = stbr_resize(input_data, input_w, input_h, input_components, STBR_FILTER_NEAREST, output_data, output_w, output_h); +// +// input_data is your supplied texels. +// output_data will be the resized texels. It should be of size output_w * output_h * input_components. +// Returned data == output_data, for convenience, or 0 in case of an error. +// +// + +typedef enum +{ + STBR_FILTER_NEAREST = 1, +} stbr_filter; + + +typedef unsigned char stbr_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_RESAMPLE_STATIC +#define STBRDEF static +#else +#define STBRDEF extern +#endif + + ////////////////////////////////////////////////////////////////////////////// + // + // PRIMARY API - resize an image + // + + STBRDEF stbr_uc* stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, stbr_filter filter, stbr_uc* output_data, int output_w, int output_h); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBR_INCLUDE_STB_RESAMPLE_H + +#ifdef STB_RESIZE_IMPLEMENTATION + +#ifndef STBR_ASSERT +#include +#define STBR_ASSERT(x) assert(x) +#endif + +#ifdef STBR_DEBUG_OVERWRITE_TEST +#include +#endif + + +// For size_t +#include + + +#ifndef _MSC_VER +#ifdef __cplusplus +#define stbr_inline inline +#else +#define stbr_inline +#endif +#else +#define stbr_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbr__uint16; +typedef signed short stbr__int16; +typedef unsigned int stbr__uint32; +typedef signed int stbr__int32; +#else +#include +typedef uint16_t stbr__uint16; +typedef int16_t stbr__int16; +typedef uint32_t stbr__uint32; +typedef int32_t stbr__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char stbr__validate_uint32[sizeof(stbr__uint32) == 4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBR_NOTUSED(v) (void)(v) +#else +#define STBR_NOTUSED(v) (void)sizeof(v) +#endif + +// i0 is a texel in [0, n0-1] +// What's the nearest texel center to i0's center in [0, n1-1] ? +// Remapping [0, n0-1] to [0, n1-1] gives (i0 + 0.5)*n1/n0 but we want to avoid +// floating point math so we rearrange it as (n1*i0 + n1/2)/n0 +stbr_inline static int stbr__nearest_texel(int i0, int n0, int n1) +{ + return (n1*i0 + n1/2) / n0; +} + +stbr_inline static size_t stbr__texel_index(int x, int y, int c, int width_stride, int num_c, int w, int h) +{ + STBR_ASSERT(x >= 0 && x < w); + STBR_ASSERT(y >= 0 && y < h); + + return y*width_stride + x*num_c + c; +} + +static void stbr__filter_nearest_1(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n) +{ + output_data[output_texel_index] = input_data[input_texel_index]; +} + +static void stbr__filter_nearest_3(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n) +{ + output_data[output_texel_index] = input_data[input_texel_index]; + output_data[output_texel_index + 1] = input_data[input_texel_index + 1]; + output_data[output_texel_index + 2] = input_data[input_texel_index + 2]; +} + +static void stbr__filter_nearest_4(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n) +{ + output_data[output_texel_index] = input_data[input_texel_index]; + output_data[output_texel_index + 1] = input_data[input_texel_index + 1]; + output_data[output_texel_index + 2] = input_data[input_texel_index + 2]; + output_data[output_texel_index + 3] = input_data[input_texel_index + 3]; +} + +static void stbr__filter_nearest_n(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n) +{ + size_t c; + for (c = 0; c < n; c++) + output_data[output_texel_index + c] = input_data[input_texel_index + c]; +} + +typedef void (stbr__filter_fn)(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n); + +STBRDEF stbr_uc* stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, stbr_filter filter, stbr_uc* output_data, int output_w, int output_h) +{ + int x, y; + int width_stride_input = input_components * input_w; + int width_stride_output = input_components * output_w; + +#ifdef STBR_DEBUG_OVERWRITE_TEST +#define OVERWRITE_ARRAY_SIZE 64 + unsigned char overwrite_contents_pre[OVERWRITE_ARRAY_SIZE]; + + memcpy(overwrite_contents_pre, &output_data[output_w * output_h * input_components], OVERWRITE_ARRAY_SIZE); +#endif + + if (filter == STBR_FILTER_NEAREST) + { + stbr__filter_fn* filter_fn; + + filter_fn = &stbr__filter_nearest_n; + + if (input_components == 1) + filter_fn = &stbr__filter_nearest_1; + else if (input_components == 3) + filter_fn = &stbr__filter_nearest_3; + else if (input_components == 4) + filter_fn = &stbr__filter_nearest_4; + + for (y = 0; y < output_h; y++) + { + int nearest_y = stbr__nearest_texel(y, output_h, input_h); + + for (x = 0; x < output_w; x++) + { + int nearest_x = stbr__nearest_texel(x, output_w, input_w); + size_t input_texel_index = stbr__texel_index(nearest_x, nearest_y, 0, width_stride_input, input_components, input_w, input_h); + size_t output_texel_index = stbr__texel_index(x, y, 0, width_stride_output, input_components, output_w, output_h); + + filter_fn(input_data, output_data, input_texel_index, output_texel_index, input_components); + } + } + } + else + return 0; + +#ifdef STBR_DEBUG_OVERWRITE_TEST + STBR_ASSERT(memcmp(overwrite_contents_pre, &output_data[output_w * output_h * input_components], OVERWRITE_ARRAY_SIZE) == 0); +#endif + + return output_data; +} + + +#endif // STB_RESAMPLE_IMPLEMENTATION + +/* +revision history: +*/ diff --git a/tests/resample_test.c b/tests/resample_test.c new file mode 100644 index 0000000..7d5b0eb --- /dev/null +++ b/tests/resample_test.c @@ -0,0 +1,49 @@ +#ifdef _WIN32 +#define STBR_ASSERT(x) \ + if (!(x)) \ + __debugbreak(); +#endif + +#define STB_RESAMPLE_IMPLEMENTATION +#include "stb_resample.h" + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +int main(int argc, char** argv) +{ + unsigned char* input_data; + unsigned char* output_data; + int w, h; + int n; + int out_w, out_h; + + if (argc <= 1) + { + printf("No input image\n"); + return 1; + } + + input_data = stbi_load(argv[1], &w, &h, &n, 0); + if (!input_data) + { + printf("Input image could not be loaded"); + return 1; + } + + out_w = 512; + out_h = 512; + + output_data = malloc(out_w * out_h * n); + + stbr_resize(input_data, w, h, n, STBR_FILTER_NEAREST, output_data, out_w, out_h); + + stbi_write_png("output.png", out_w, out_h, n, output_data, out_w * n); + + free(output_data); + + return 0; +} From c27c5b6fbe4ecee488ae675ab1588342e341b5c6 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Mon, 21 Jul 2014 15:36:43 -0700 Subject: [PATCH 002/168] There's really no point in returning the output buffer. --- stb_resample.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index b5dae57..684d199 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -18,13 +18,11 @@ Initial implementation by Jorge L Rodriguez #define STBR_INCLUDE_STB_RESAMPLE_H // Basic usage: -// unsigned char *data = stbr_resize(input_data, input_w, input_h, input_components, STBR_FILTER_NEAREST, output_data, output_w, output_h); +// result = stbr_resize(input_data, input_w, input_h, input_components, STBR_FILTER_NEAREST, output_data, output_w, output_h); // // input_data is your supplied texels. // output_data will be the resized texels. It should be of size output_w * output_h * input_components. -// Returned data == output_data, for convenience, or 0 in case of an error. -// -// +// Returned result is 1 for success or 0 in case of an error. typedef enum { @@ -49,7 +47,7 @@ extern "C" { // PRIMARY API - resize an image // - STBRDEF stbr_uc* stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, stbr_filter filter, stbr_uc* output_data, int output_w, int output_h); + STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, stbr_filter filter, stbr_uc* output_data, int output_w, int output_h); #ifdef __cplusplus @@ -156,7 +154,7 @@ static void stbr__filter_nearest_n(const stbr_uc* input_data, stbr_uc* output_da typedef void (stbr__filter_fn)(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n); -STBRDEF stbr_uc* stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, stbr_filter filter, stbr_uc* output_data, int output_w, int output_h) +STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, stbr_filter filter, stbr_uc* output_data, int output_w, int output_h) { int x, y; int width_stride_input = input_components * input_w; @@ -203,7 +201,7 @@ STBRDEF stbr_uc* stbr_resize(const stbr_uc* input_data, int input_w, int input_h STBR_ASSERT(memcmp(overwrite_contents_pre, &output_data[output_w * output_h * input_components], OVERWRITE_ARRAY_SIZE) == 0); #endif - return output_data; + return 1; } From 06b7b00696ad5ad20c8f672d5f1122a083ab7340 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Mon, 21 Jul 2014 16:14:32 -0700 Subject: [PATCH 003/168] It does nothing now but I want to support edge behavior in the future. --- stb_resample.h | 11 ++++++++--- tests/resample_test.c | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 684d199..b7b1680 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -18,7 +18,7 @@ Initial implementation by Jorge L Rodriguez #define STBR_INCLUDE_STB_RESAMPLE_H // Basic usage: -// result = stbr_resize(input_data, input_w, input_h, input_components, STBR_FILTER_NEAREST, output_data, output_w, output_h); +// result = stbr_resize(input_data, input_w, input_h, input_components, output_data, output_w, output_h, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP); // // input_data is your supplied texels. // output_data will be the resized texels. It should be of size output_w * output_h * input_components. @@ -29,6 +29,11 @@ typedef enum STBR_FILTER_NEAREST = 1, } stbr_filter; +typedef enum +{ + STBR_EDGE_CLAMP = 1, +} stbr_edge; + typedef unsigned char stbr_uc; @@ -47,7 +52,7 @@ extern "C" { // PRIMARY API - resize an image // - STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, stbr_filter filter, stbr_uc* output_data, int output_w, int output_h); + STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, stbr_uc* output_data, int output_w, int output_h, stbr_filter filter, stbr_edge edge); #ifdef __cplusplus @@ -154,7 +159,7 @@ static void stbr__filter_nearest_n(const stbr_uc* input_data, stbr_uc* output_da typedef void (stbr__filter_fn)(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n); -STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, stbr_filter filter, stbr_uc* output_data, int output_w, int output_h) +STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, stbr_uc* output_data, int output_w, int output_h, stbr_filter filter, stbr_edge edge) { int x, y; int width_stride_input = input_components * input_w; diff --git a/tests/resample_test.c b/tests/resample_test.c index 7d5b0eb..2c775c6 100644 --- a/tests/resample_test.c +++ b/tests/resample_test.c @@ -39,7 +39,7 @@ int main(int argc, char** argv) output_data = malloc(out_w * out_h * n); - stbr_resize(input_data, w, h, n, STBR_FILTER_NEAREST, output_data, out_w, out_h); + stbr_resize(input_data, w, h, n, output_data, out_w, out_h, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP); stbi_write_png("output.png", out_w, out_h, n, output_data, out_w * n); From ba861fa493de9a70746a8f5fbd3cab995058f1b8 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Mon, 21 Jul 2014 18:01:05 -0700 Subject: [PATCH 004/168] Allow specifying a stride. --- stb_resample.h | 18 ++++++++++-------- tests/resample_test.c | 10 ++++++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index b7b1680..41bc138 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -18,10 +18,11 @@ Initial implementation by Jorge L Rodriguez #define STBR_INCLUDE_STB_RESAMPLE_H // Basic usage: -// result = stbr_resize(input_data, input_w, input_h, input_components, output_data, output_w, output_h, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP); +// result = stbr_resize(input_data, input_w, input_h, input_components, 0, output_data, output_w, output_h, 0, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP); // // input_data is your supplied texels. -// output_data will be the resized texels. It should be of size output_w * output_h * input_components. +// output_data will be the resized texels. It should be of size output_w * output_h * input_components (or output_h * output_stride if you provided a stride.) +// If input_stride or output_stride is 0 (as in this example) the stride will be automatically calculated as width*components. // Returned result is 1 for success or 0 in case of an error. typedef enum @@ -52,7 +53,7 @@ extern "C" { // PRIMARY API - resize an image // - STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, stbr_uc* output_data, int output_w, int output_h, stbr_filter filter, stbr_edge edge); + STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, int input_stride, stbr_uc* output_data, int output_w, int output_h, int output_stride, stbr_filter filter, stbr_edge edge); #ifdef __cplusplus @@ -159,17 +160,18 @@ static void stbr__filter_nearest_n(const stbr_uc* input_data, stbr_uc* output_da typedef void (stbr__filter_fn)(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n); -STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, stbr_uc* output_data, int output_w, int output_h, stbr_filter filter, stbr_edge edge) +STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, int input_stride, stbr_uc* output_data, int output_w, int output_h, int output_stride, stbr_filter filter, stbr_edge edge) { int x, y; - int width_stride_input = input_components * input_w; - int width_stride_output = input_components * output_w; + int width_stride_input = input_stride ? input_stride : input_components * input_w; + int width_stride_output = output_stride ? output_stride : input_components * output_w; #ifdef STBR_DEBUG_OVERWRITE_TEST #define OVERWRITE_ARRAY_SIZE 64 unsigned char overwrite_contents_pre[OVERWRITE_ARRAY_SIZE]; - memcpy(overwrite_contents_pre, &output_data[output_w * output_h * input_components], OVERWRITE_ARRAY_SIZE); + size_t begin_forbidden = width_stride_output * (output_h - 1) + output_w * input_components; + memcpy(overwrite_contents_pre, &output_data[begin_forbidden], OVERWRITE_ARRAY_SIZE); #endif if (filter == STBR_FILTER_NEAREST) @@ -203,7 +205,7 @@ STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int return 0; #ifdef STBR_DEBUG_OVERWRITE_TEST - STBR_ASSERT(memcmp(overwrite_contents_pre, &output_data[output_w * output_h * input_components], OVERWRITE_ARRAY_SIZE) == 0); + STBR_ASSERT(memcmp(overwrite_contents_pre, &output_data[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); #endif return 1; diff --git a/tests/resample_test.c b/tests/resample_test.c index 2c775c6..276292c 100644 --- a/tests/resample_test.c +++ b/tests/resample_test.c @@ -19,7 +19,7 @@ int main(int argc, char** argv) unsigned char* output_data; int w, h; int n; - int out_w, out_h; + int out_w, out_h, out_stride; if (argc <= 1) { @@ -36,12 +36,14 @@ int main(int argc, char** argv) out_w = 512; out_h = 512; + out_stride = (out_w + 10) * n; - output_data = malloc(out_w * out_h * n); + output_data = malloc(out_stride * out_h); - stbr_resize(input_data, w, h, n, output_data, out_w, out_h, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP); + // Cut out the outside 64 pixels all around to test the stride. + stbr_resize(input_data + w*64*n + 64*n, w - 128, h - 128, n, w*n, output_data, out_w, out_h, out_stride, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP); - stbi_write_png("output.png", out_w, out_h, n, output_data, out_w * n); + stbi_write_png("output.png", out_w, out_h, n, output_data, out_stride); free(output_data); From 0155bd3ab6e1bfadd05ae5477e0489a8f5147eec Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Mon, 21 Jul 2014 19:51:11 -0700 Subject: [PATCH 005/168] We are going to support SRGB. --- stb_resample.h | 12 +++++++++--- tests/resample_test.c | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 41bc138..3477678 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -18,7 +18,7 @@ Initial implementation by Jorge L Rodriguez #define STBR_INCLUDE_STB_RESAMPLE_H // Basic usage: -// result = stbr_resize(input_data, input_w, input_h, input_components, 0, output_data, output_w, output_h, 0, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP); +// result = stbr_resize(input_data, input_w, input_h, input_components, 0, output_data, output_w, output_h, 0, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); // // input_data is your supplied texels. // output_data will be the resized texels. It should be of size output_w * output_h * input_components (or output_h * output_stride if you provided a stride.) @@ -35,6 +35,12 @@ typedef enum STBR_EDGE_CLAMP = 1, } stbr_edge; +typedef enum +{ + STBR_COLORSPACE_LINEAR = 1, + STBR_COLORSPACE_SRGB = 1, +} stbr_colorspace; + typedef unsigned char stbr_uc; @@ -53,7 +59,7 @@ extern "C" { // PRIMARY API - resize an image // - STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, int input_stride, stbr_uc* output_data, int output_w, int output_h, int output_stride, stbr_filter filter, stbr_edge edge); + STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, int input_stride, stbr_uc* output_data, int output_w, int output_h, int output_stride, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); #ifdef __cplusplus @@ -160,7 +166,7 @@ static void stbr__filter_nearest_n(const stbr_uc* input_data, stbr_uc* output_da typedef void (stbr__filter_fn)(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n); -STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, int input_stride, stbr_uc* output_data, int output_w, int output_h, int output_stride, stbr_filter filter, stbr_edge edge) +STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, int input_stride, stbr_uc* output_data, int output_w, int output_h, int output_stride, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int x, y; int width_stride_input = input_stride ? input_stride : input_components * input_w; diff --git a/tests/resample_test.c b/tests/resample_test.c index 276292c..af20af4 100644 --- a/tests/resample_test.c +++ b/tests/resample_test.c @@ -41,7 +41,7 @@ int main(int argc, char** argv) output_data = malloc(out_stride * out_h); // Cut out the outside 64 pixels all around to test the stride. - stbr_resize(input_data + w*64*n + 64*n, w - 128, h - 128, n, w*n, output_data, out_w, out_h, out_stride, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP); + stbr_resize(input_data + w*64*n + 64*n, w - 128, h - 128, n, w*n, output_data, out_w, out_h, out_stride, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); stbi_write_png("output.png", out_w, out_h, n, output_data, out_stride); From 55c5f0b3a0004a6591888e60a53cb92e53b6e18f Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 23 Jul 2014 22:17:56 -0700 Subject: [PATCH 006/168] Beginning of a more sophisticated resample algorithm, starting with calculating filter contributions per scan line. --- stb_resample.h | 249 ++++++++++++++++++++------- tests/{resample_test.c => resample_test.cpp} | 25 ++- tests/resample_test_c.c | 11 ++ 3 files changed, 220 insertions(+), 65 deletions(-) rename tests/{resample_test.c => resample_test.cpp} (55%) create mode 100644 tests/resample_test_c.c diff --git a/stb_resample.h b/stb_resample.h index 3477678..b187a2e 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -18,7 +18,7 @@ Initial implementation by Jorge L Rodriguez #define STBR_INCLUDE_STB_RESAMPLE_H // Basic usage: -// result = stbr_resize(input_data, input_w, input_h, input_components, 0, output_data, output_w, output_h, 0, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); +// result = stbr_resize(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, channels, alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_BILINEAR, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); // // input_data is your supplied texels. // output_data will be the resized texels. It should be of size output_w * output_h * input_components (or output_h * output_stride if you provided a stride.) @@ -38,11 +38,16 @@ typedef enum typedef enum { STBR_COLORSPACE_LINEAR = 1, - STBR_COLORSPACE_SRGB = 1, + STBR_COLORSPACE_SRGB = 2, } stbr_colorspace; +typedef enum +{ + STBR_TYPE_UINT8 = 1, +} stbr_type; typedef unsigned char stbr_uc; +typedef unsigned int stbr_size_t; // to avoid including a header for size_t #ifdef __cplusplus extern "C" { @@ -59,7 +64,15 @@ extern "C" { // PRIMARY API - resize an image // - STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, int input_stride, stbr_uc* output_data, int output_w, int output_h, int output_stride, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int input_stride_in_bytes, + int output_w, int output_h, int output_stride_in_bytes, + int channels, stbr_filter filter); + + STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + //int channels, int alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, + int channels, stbr_type type, stbr_filter filter, + void* tempmem, stbr_size_t tempmem_size_in_bytes); #ifdef __cplusplus @@ -78,13 +91,21 @@ extern "C" { #define STBR_ASSERT(x) assert(x) #endif +#ifdef STBR_DEBUG +#define STBR_DEBUG_ASSERT STBR_ASSERT +#else +#define STBR_DEBUG_ASSERT +#endif + +// If you hit this it means I haven't done it yet. +#define STBR_UNIMPLEMENTED(x) STBR_ASSERT(!(x)) + #ifdef STBR_DEBUG_OVERWRITE_TEST #include #endif -// For size_t -#include +#include #ifndef _MSC_VER @@ -120,6 +141,64 @@ typedef unsigned char stbr__validate_uint32[sizeof(stbr__uint32) == 4 ? 1 : -1]; #define STBR_NOTUSED(v) (void)sizeof(v) #endif +#define STBR_ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) + +// Kernel function centered at 0 +typedef float (stbr__kernel_fn)(float x); + +typedef struct +{ + stbr__kernel_fn* kernel; + float support; +} stbr__filter_info; + +typedef struct +{ + int n0; // First contributing source texel + int n1; // Last contributing source texel +} stbr__contributors; + +typedef struct +{ + int total_contributors; + int kernel_texel_width; + + float* decode_buffer; + stbr__contributors* horizontal_contributors; + float* horizontal_coefficients; +} stbr__info; + + +float stbr__filter_nearest(float x) +{ + if (fabs(x) < 0.5) + return 1; + else + return 0; +} + +stbr__filter_info stbr__filter_info_table[] = { + { NULL, 0.0f }, + { stbr__filter_nearest, 0.5f }, +}; + +// This is the maximum number of input samples that can affect an output sample +// with the given filter +int stbr__get_filter_texel_width(stbr_filter filter, int upsample) +{ + STBR_UNIMPLEMENTED(!upsample); + + STBR_ASSERT(filter != 0); + STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); + + return (int)ceil(stbr__filter_info_table[filter].support * 2); +} + +int stbr__get_total_contributors(stbr_filter filter, int input_w, int output_w) +{ + return output_w * stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0); +} + // i0 is a texel in [0, n0-1] // What's the nearest texel center to i0's center in [0, n1-1] ? // Remapping [0, n0-1] to [0, n1-1] gives (i0 + 0.5)*n1/n0 but we want to avoid @@ -129,95 +208,143 @@ stbr_inline static int stbr__nearest_texel(int i0, int n0, int n1) return (n1*i0 + n1/2) / n0; } -stbr_inline static size_t stbr__texel_index(int x, int y, int c, int width_stride, int num_c, int w, int h) +stbr_inline static stbr_size_t stbr__texel_index(int x, int y, int c, int width_stride, int num_c, int w, int h) { - STBR_ASSERT(x >= 0 && x < w); - STBR_ASSERT(y >= 0 && y < h); + STBR_DEBUG_ASSERT(x >= 0 && x < w); + STBR_DEBUG_ASSERT(y >= 0 && y < h); return y*width_stride + x*num_c + c; } -static void stbr__filter_nearest_1(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n) +stbr_inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_info, int n) { - output_data[output_texel_index] = input_data[input_texel_index]; + return &stbr_info->horizontal_contributors[n]; } -static void stbr__filter_nearest_3(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n) +stbr_inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, int c) { - output_data[output_texel_index] = input_data[input_texel_index]; - output_data[output_texel_index + 1] = input_data[input_texel_index + 1]; - output_data[output_texel_index + 2] = input_data[input_texel_index + 2]; + return &stbr_info->horizontal_coefficients[stbr_info->kernel_texel_width*n + c]; } -static void stbr__filter_nearest_4(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n) +// Each scan line uses the same kernel values so we should calculate the kernel +// values once and then we can use them for every scan line. +static void stbr__calculate_horizontal_filters(stbr__info* stbr_info, stbr_filter filter, int input_w, int output_w) { - output_data[output_texel_index] = input_data[input_texel_index]; - output_data[output_texel_index + 1] = input_data[input_texel_index + 1]; - output_data[output_texel_index + 2] = input_data[input_texel_index + 2]; - output_data[output_texel_index + 3] = input_data[input_texel_index + 3]; + int n, i; + float scale_ratio = (float)output_w / input_w; + + float out_pixels_radius = stbr__filter_info_table[filter].support * scale_ratio; + + STBR_UNIMPLEMENTED(output_w < input_w); + + for (n = 0; n < output_w; n++) + { + // What input texels contribute to this output texel? + float out_texel_center = (float)n + 0.5f; + float out_texel_influence_lowerbound = out_texel_center - out_pixels_radius; + float out_texel_influence_upperbound = out_texel_center + out_pixels_radius; + + float in_center_of_out = out_texel_center / scale_ratio; + float in_texel_influence_lowerbound = out_texel_influence_lowerbound / scale_ratio; + float in_texel_influence_upperbound = out_texel_influence_upperbound / scale_ratio; + + int in_first_texel = (int)(floor(in_texel_influence_lowerbound + 0.5)); + int in_last_texel = (int)(floor(in_texel_influence_upperbound - 0.5)); + + float total_filter = 0; + float filter_scale; + + STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr_info->kernel_texel_width); + STBR_DEBUG_ASSERT(in_first_texel >= 0); + STBR_DEBUG_ASSERT(in_last_texel < input_w); + + stbr__get_contributor(stbr_info, n)->n0 = in_first_texel; + stbr__get_contributor(stbr_info, n)->n1 = in_last_texel; + + for (i = 0; i <= in_last_texel - in_first_texel; i++) + { + float in_texel_center = (float)(i + in_first_texel) + 0.5f; + total_filter += *stbr__get_coefficient(stbr_info, n, i) = stbr__filter_info_table[filter].kernel(in_center_of_out - in_texel_center); + } + + STBR_DEBUG_ASSERT(total_filter > 0); + STBR_DEBUG_ASSERT(fabs(1-total_filter) < 0.1f); // Make sure it's not way off. + + // Make sure the sum of all coefficients is 1. + filter_scale = 1 / total_filter; + + for (i = 0; i <= in_last_texel - in_first_texel; i++) + *stbr__get_coefficient(stbr_info, n, i) *= filter_scale; + } } -static void stbr__filter_nearest_n(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n) +STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + int channels, stbr_type type, stbr_filter filter, + void* tempmem, stbr_size_t tempmem_size_in_bytes) { - size_t c; - for (c = 0; c < n; c++) - output_data[output_texel_index + c] = input_data[input_texel_index + c]; -} - -typedef void (stbr__filter_fn)(const stbr_uc* input_data, stbr_uc* output_data, size_t input_texel_index, size_t output_texel_index, size_t n); - -STBRDEF int stbr_resize(const stbr_uc* input_data, int input_w, int input_h, int input_components, int input_stride, stbr_uc* output_data, int output_w, int output_h, int output_stride, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) -{ - int x, y; - int width_stride_input = input_stride ? input_stride : input_components * input_w; - int width_stride_output = output_stride ? output_stride : input_components * output_w; + int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w; + int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w; #ifdef STBR_DEBUG_OVERWRITE_TEST #define OVERWRITE_ARRAY_SIZE 64 unsigned char overwrite_contents_pre[OVERWRITE_ARRAY_SIZE]; - size_t begin_forbidden = width_stride_output * (output_h - 1) + output_w * input_components; - memcpy(overwrite_contents_pre, &output_data[begin_forbidden], OVERWRITE_ARRAY_SIZE); + stbr_size_t begin_forbidden = width_stride_output * (output_h - 1) + output_w * channels; + memcpy(overwrite_contents_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); #endif - if (filter == STBR_FILTER_NEAREST) - { - stbr__filter_fn* filter_fn; + STBR_UNIMPLEMENTED(type != STBR_TYPE_UINT8); - filter_fn = &stbr__filter_nearest_n; + STBR_ASSERT(filter != 0); + STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); - if (input_components == 1) - filter_fn = &stbr__filter_nearest_1; - else if (input_components == 3) - filter_fn = &stbr__filter_nearest_3; - else if (input_components == 4) - filter_fn = &stbr__filter_nearest_4; - - for (y = 0; y < output_h; y++) - { - int nearest_y = stbr__nearest_texel(y, output_h, input_h); - - for (x = 0; x < output_w; x++) - { - int nearest_x = stbr__nearest_texel(x, output_w, input_w); - size_t input_texel_index = stbr__texel_index(nearest_x, nearest_y, 0, width_stride_input, input_components, input_w, input_h); - size_t output_texel_index = stbr__texel_index(x, y, 0, width_stride_output, input_components, output_w, output_h); - - filter_fn(input_data, output_data, input_texel_index, output_texel_index, input_components); - } - } - } - else + if (!tempmem) return 0; + if (tempmem_size_in_bytes < stbr_calculate_memory(input_w, input_h, input_stride_in_bytes, output_w, output_h, output_stride_in_bytes, channels, STBR_FILTER_NEAREST)) + return 0; + +#define STBR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) + + memset(tempmem, 0, tempmem_size_in_bytes); + + stbr__info* stbr_info = (stbr__info*)tempmem; + + stbr_info->total_contributors = stbr__get_total_contributors(filter, input_w, output_w); + stbr_info->kernel_texel_width = stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0); + + stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info, sizeof(stbr__info), float); + stbr_info->horizontal_contributors = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), stbr__contributors); + stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, stbr_info->total_contributors * sizeof(stbr__contributors), float); + +#undef STBR__NEXT_MEMPTR + + stbr__calculate_horizontal_filters(stbr_info, filter, input_w, output_w); + #ifdef STBR_DEBUG_OVERWRITE_TEST - STBR_ASSERT(memcmp(overwrite_contents_pre, &output_data[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); + STBR_DEBUG_ASSERT(memcmp(overwrite_contents_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); #endif return 1; } +STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int input_stride_in_bytes, + int output_w, int output_h, int output_stride_in_bytes, + int channels, stbr_filter filter) +{ + STBR_ASSERT(filter != 0); + STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); + + int info_size = sizeof(stbr__info); + int decode_buffer_size = input_w * channels * sizeof(float); + int contributors_size = stbr__get_total_contributors(filter, input_w, output_w) * sizeof(stbr__contributors); + int coefficients_size = stbr__get_total_contributors(filter, input_w, output_w) * sizeof(float); + + return info_size + decode_buffer_size + contributors_size + coefficients_size; +} + #endif // STB_RESAMPLE_IMPLEMENTATION /* diff --git a/tests/resample_test.c b/tests/resample_test.cpp similarity index 55% rename from tests/resample_test.c rename to tests/resample_test.cpp index af20af4..2ee2ffb 100644 --- a/tests/resample_test.c +++ b/tests/resample_test.cpp @@ -5,6 +5,7 @@ #endif #define STB_RESAMPLE_IMPLEMENTATION +#define STB_RESAMPLE_STATIC #include "stb_resample.h" #define STB_IMAGE_WRITE_IMPLEMENTATION @@ -13,6 +14,10 @@ #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" +#ifdef _WIN32 +#include +#endif + int main(int argc, char** argv) { unsigned char* input_data; @@ -34,14 +39,26 @@ int main(int argc, char** argv) return 1; } - out_w = 512; - out_h = 512; + out_w = 1024; + out_h = 1024; out_stride = (out_w + 10) * n; - output_data = malloc(out_stride * out_h); + output_data = (unsigned char*)malloc(out_stride * out_h); + + int in_w = 512; + int in_h = 512; + + size_t memory_required = stbr_calculate_memory(in_w, in_h, w*n, out_w, out_h, out_stride, n, STBR_FILTER_NEAREST); + void* extra_memory = malloc(memory_required); // Cut out the outside 64 pixels all around to test the stride. - stbr_resize(input_data + w*64*n + 64*n, w - 128, h - 128, n, w*n, output_data, out_w, out_h, out_stride, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + int border = 64; + STBR_ASSERT(in_w + border <= w); + STBR_ASSERT(in_h + border <= h); + + stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_NEAREST, extra_memory, memory_required); + + free(extra_memory); stbi_write_png("output.png", out_w, out_h, n, output_data, out_stride); diff --git a/tests/resample_test_c.c b/tests/resample_test_c.c new file mode 100644 index 0000000..dcc3572 --- /dev/null +++ b/tests/resample_test_c.c @@ -0,0 +1,11 @@ +#ifdef _WIN32 +#define STBR_ASSERT(x) \ + if (!(x)) \ + __debugbreak(); +#endif + +#define STB_RESAMPLE_IMPLEMENTATION +#define STB_RESAMPLE_STATIC +#include "stb_resample.h" + +// Just to make sure it will build properly with a c compiler From 158effb62ace68efc9223713c7e9d07e70363b95 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 23 Jul 2014 23:08:06 -0700 Subject: [PATCH 007/168] More accurate names. Smaller size for contributors memory, more accurate to what's needed. --- stb_resample.h | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index b187a2e..3ff072d 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -160,12 +160,13 @@ typedef struct typedef struct { - int total_contributors; + int total_coefficients; int kernel_texel_width; - float* decode_buffer; stbr__contributors* horizontal_contributors; float* horizontal_coefficients; + + float* decode_buffer; } stbr__info; @@ -194,7 +195,7 @@ int stbr__get_filter_texel_width(stbr_filter filter, int upsample) return (int)ceil(stbr__filter_info_table[filter].support * 2); } -int stbr__get_total_contributors(stbr_filter filter, int input_w, int output_w) +int stbr__get_total_coefficients(stbr_filter filter, int input_w, int output_w) { return output_w * stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0); } @@ -218,6 +219,7 @@ stbr_inline static stbr_size_t stbr__texel_index(int x, int y, int c, int width_ stbr_inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_info, int n) { + STBR_DEBUG_ASSERT(n >= 0 /*&& n < output_w*/); return &stbr_info->horizontal_contributors[n]; } @@ -305,18 +307,18 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input if (tempmem_size_in_bytes < stbr_calculate_memory(input_w, input_h, input_stride_in_bytes, output_w, output_h, output_stride_in_bytes, channels, STBR_FILTER_NEAREST)) return 0; -#define STBR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) - memset(tempmem, 0, tempmem_size_in_bytes); stbr__info* stbr_info = (stbr__info*)tempmem; - stbr_info->total_contributors = stbr__get_total_contributors(filter, input_w, output_w); + stbr_info->total_coefficients = stbr__get_total_coefficients(filter, input_w, output_w); stbr_info->kernel_texel_width = stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0); - stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info, sizeof(stbr__info), float); - stbr_info->horizontal_contributors = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), stbr__contributors); - stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, stbr_info->total_contributors * sizeof(stbr__contributors), float); +#define STBR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) + + stbr_info->horizontal_contributors = STBR__NEXT_MEMPTR(stbr_info, sizeof(stbr__info), stbr__contributors); + stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, output_w * sizeof(stbr__contributors), float); + stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr_info->total_coefficients * sizeof(stbr__contributors), float); #undef STBR__NEXT_MEMPTR @@ -339,8 +341,8 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int input_st int info_size = sizeof(stbr__info); int decode_buffer_size = input_w * channels * sizeof(float); - int contributors_size = stbr__get_total_contributors(filter, input_w, output_w) * sizeof(stbr__contributors); - int coefficients_size = stbr__get_total_contributors(filter, input_w, output_w) * sizeof(float); + int contributors_size = output_w * sizeof(stbr__contributors); + int coefficients_size = stbr__get_total_coefficients(filter, input_w, output_w) * sizeof(float); return info_size + decode_buffer_size + contributors_size + coefficients_size; } From 152965f3342d51973711e3cace0143640d712c13 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 24 Jul 2014 00:47:00 -0700 Subject: [PATCH 008/168] Decode enough scanlines into a ring buffer to make sure that we have enough source scanlines to do a vertical sampling. --- stb_resample.h | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 207 insertions(+), 23 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 3ff072d..c3186e1 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -160,6 +160,20 @@ typedef struct typedef struct { + const void* input_data; + int input_w; + int input_h; + int input_stride_bytes; + + void* output_data; + int output_w; + int output_h; + int output_stride_bytes; + + int channels; + stbr_type type; + stbr_filter filter; + int total_coefficients; int kernel_texel_width; @@ -167,6 +181,11 @@ typedef struct float* horizontal_coefficients; float* decode_buffer; + + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; + float* ring_buffer; } stbr__info; @@ -219,7 +238,7 @@ stbr_inline static stbr_size_t stbr__texel_index(int x, int y, int c, int width_ stbr_inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_info, int n) { - STBR_DEBUG_ASSERT(n >= 0 /*&& n < output_w*/); + STBR_DEBUG_ASSERT(n >= 0 && n < stbr_info->output_w); return &stbr_info->horizontal_contributors[n]; } @@ -228,37 +247,46 @@ stbr_inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, in return &stbr_info->horizontal_coefficients[stbr_info->kernel_texel_width*n + c]; } +// What input texels contribute to this output texel? +static void stbr__calculate_sample_range(int n, float out_filter_radius, float scale_ratio, int* in_first_texel, int* in_last_texel, float* in_center_of_out) +{ + // What input texels contribute to this output texel? + float out_texel_center = (float)n + 0.5f; + float out_texel_influence_lowerbound = out_texel_center - out_filter_radius; + float out_texel_influence_upperbound = out_texel_center + out_filter_radius; + + float in_texel_influence_lowerbound = out_texel_influence_lowerbound / scale_ratio; + float in_texel_influence_upperbound = out_texel_influence_upperbound / scale_ratio; + + *in_center_of_out = out_texel_center / scale_ratio; + *in_first_texel = (int)(floor(in_texel_influence_lowerbound + 0.5)); + *in_last_texel = (int)(floor(in_texel_influence_upperbound - 0.5)); +} + // Each scan line uses the same kernel values so we should calculate the kernel // values once and then we can use them for every scan line. -static void stbr__calculate_horizontal_filters(stbr__info* stbr_info, stbr_filter filter, int input_w, int output_w) +static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) { int n, i; - float scale_ratio = (float)output_w / input_w; + float scale_ratio = (float)stbr_info->output_w / stbr_info->input_w; - float out_pixels_radius = stbr__filter_info_table[filter].support * scale_ratio; + float out_pixels_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; - STBR_UNIMPLEMENTED(output_w < input_w); + STBR_UNIMPLEMENTED(stbr_info->output_w < stbr_info->input_w); - for (n = 0; n < output_w; n++) + for (n = 0; n < stbr_info->output_w; n++) { - // What input texels contribute to this output texel? - float out_texel_center = (float)n + 0.5f; - float out_texel_influence_lowerbound = out_texel_center - out_pixels_radius; - float out_texel_influence_upperbound = out_texel_center + out_pixels_radius; + float in_center_of_out; // Center of the current out texel in the in texel space + int in_first_texel, in_last_texel; - float in_center_of_out = out_texel_center / scale_ratio; - float in_texel_influence_lowerbound = out_texel_influence_lowerbound / scale_ratio; - float in_texel_influence_upperbound = out_texel_influence_upperbound / scale_ratio; - - int in_first_texel = (int)(floor(in_texel_influence_lowerbound + 0.5)); - int in_last_texel = (int)(floor(in_texel_influence_upperbound - 0.5)); + stbr__calculate_sample_range(n, out_pixels_radius, scale_ratio, &in_first_texel, &in_last_texel, &in_center_of_out); float total_filter = 0; float filter_scale; STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr_info->kernel_texel_width); STBR_DEBUG_ASSERT(in_first_texel >= 0); - STBR_DEBUG_ASSERT(in_last_texel < input_w); + STBR_DEBUG_ASSERT(in_last_texel < stbr_info->input_w); stbr__get_contributor(stbr_info, n)->n0 = in_first_texel; stbr__get_contributor(stbr_info, n)->n1 = in_last_texel; @@ -266,7 +294,7 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info, stbr_filte for (i = 0; i <= in_last_texel - in_first_texel; i++) { float in_texel_center = (float)(i + in_first_texel) + 0.5f; - total_filter += *stbr__get_coefficient(stbr_info, n, i) = stbr__filter_info_table[filter].kernel(in_center_of_out - in_texel_center); + total_filter += *stbr__get_coefficient(stbr_info, n, i) = stbr__filter_info_table[stbr_info->filter].kernel(in_center_of_out - in_texel_center); } STBR_DEBUG_ASSERT(total_filter > 0); @@ -280,20 +308,112 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info, stbr_filte } } +static float* stbr__get_decode_buffer_index(stbr__info* stbr_info, int x, int c) +{ + STBR_DEBUG_ASSERT(x >= 0 && x < stbr_info->input_w); + STBR_DEBUG_ASSERT(c >= 0 && c < stbr_info->channels); + + return &stbr_info->decode_buffer[x * stbr_info->channels + c]; +} + +static void stbr__decode_scanline(stbr__info* stbr_info, int n) +{ + int x, c; + int channels = stbr_info->channels; + int input_w = stbr_info->input_w; + const void* input_data = stbr_info->input_data; + float* decode_buffer = stbr_info->decode_buffer; + + STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); + + for (x = 0; x < input_w; x++) + { + for (c = 0; c < channels; c++) + { + int buffer_index = x * channels + c; + decode_buffer[buffer_index] = ((float)((const unsigned char*)input_data)[buffer_index]) / 255; + } + } +} + +static void stbr__resample_horizontal(stbr__info* stbr_info, int n) +{ + int x, k, c; + int output_w = stbr_info->output_w; + int kernel_texel_width = stbr_info->kernel_texel_width; + int channels = stbr_info->channels; + float* decode_buffer = stbr_info->decode_buffer; + stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; + float* horizontal_coefficients = stbr_info->horizontal_coefficients; + + int ring_buffer_index; + float* ring_buffer; + + if (stbr_info->ring_buffer_begin_index < 0) + ring_buffer_index = stbr_info->ring_buffer_begin_index = 0; + else + { + ring_buffer_index = (stbr_info->ring_buffer_begin_index + (stbr_info->ring_buffer_last_scanline - stbr_info->ring_buffer_first_scanline) + 1) % stbr_info->kernel_texel_width; + STBR_DEBUG_ASSERT(ring_buffer_index != stbr_info->ring_buffer_begin_index); + } + + ring_buffer = &stbr_info->ring_buffer[ring_buffer_index]; + + for (x = 0; x < output_w; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int out_texel_index = x * channels; + int coefficient_group_index = x * kernel_texel_width; + int coefficient_counter = 0; + + STBR_DEBUG_ASSERT(n1 >= n0); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_group_index + (coefficient_counter++); + int in_texel_index = k * channels; + + if (!horizontal_coefficients[coefficient_index]) + continue; + + for (c = 0; c < channels; c++) + ring_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * horizontal_coefficients[coefficient_index]; + } + } + + stbr_info->ring_buffer_last_scanline = n; +} + +static void stbr__decode_and_resample(stbr__info* stbr_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbr__decode_scanline(stbr_info, n); + + // Now resample it into the ring buffer. + stbr__resample_horizontal(stbr_info, n); + + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. +} + STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, int channels, stbr_type type, stbr_filter filter, void* tempmem, stbr_size_t tempmem_size_in_bytes) { + int y; int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w; int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w; #ifdef STBR_DEBUG_OVERWRITE_TEST #define OVERWRITE_ARRAY_SIZE 64 - unsigned char overwrite_contents_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_output_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_pre[OVERWRITE_ARRAY_SIZE]; stbr_size_t begin_forbidden = width_stride_output * (output_h - 1) + output_w * channels; - memcpy(overwrite_contents_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_output_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); #endif STBR_UNIMPLEMENTED(type != STBR_TYPE_UINT8); @@ -311,6 +431,20 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr__info* stbr_info = (stbr__info*)tempmem; + stbr_info->input_data = input_data; + stbr_info->input_w = input_w; + stbr_info->input_h = input_h; + stbr_info->input_stride_bytes = width_stride_input; + + stbr_info->output_data = output_data; + stbr_info->output_w = output_w; + stbr_info->output_h = output_h; + stbr_info->output_stride_bytes = width_stride_output; + + stbr_info->channels = channels; + stbr_info->type = type; + stbr_info->filter = filter; + stbr_info->total_coefficients = stbr__get_total_coefficients(filter, input_w, output_w); stbr_info->kernel_texel_width = stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0); @@ -319,13 +453,62 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->horizontal_contributors = STBR__NEXT_MEMPTR(stbr_info, sizeof(stbr__info), stbr__contributors); stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, output_w * sizeof(stbr__contributors), float); stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr_info->total_coefficients * sizeof(stbr__contributors), float); + stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), float); #undef STBR__NEXT_MEMPTR - stbr__calculate_horizontal_filters(stbr_info, filter, input_w, output_w); + // This signals that the ring buffer is empty + stbr_info->ring_buffer_begin_index = -1; + + stbr__calculate_horizontal_filters(stbr_info); + + float scale_ratio = (float)output_h / input_h; + float out_scanlines_radius = stbr__filter_info_table[filter].support * scale_ratio; + + for (y = 0; y < output_h; y++) + { + float in_center_of_out; // Center of the current out scanline in the in scanline space + int in_first_scanline, in_last_scanline; + + stbr__calculate_sample_range(y, out_scanlines_radius, scale_ratio, &in_first_scanline, &in_last_scanline, &in_center_of_out); + + STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr_info->kernel_texel_width); + STBR_DEBUG_ASSERT(in_first_scanline >= 0); + STBR_DEBUG_ASSERT(in_last_scanline < input_w); + + if (stbr_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (in_first_scanline > stbr_info->ring_buffer_first_scanline) + { + if (stbr_info->ring_buffer_first_scanline == stbr_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbr_info->ring_buffer_begin_index = -1; + stbr_info->ring_buffer_first_scanline = 0; + stbr_info->ring_buffer_last_scanline = 0; + break; + } + else + stbr_info->ring_buffer_first_scanline++; + } + } + + // Load in new ones. + if (stbr_info->ring_buffer_begin_index < 0) + stbr__decode_and_resample(stbr_info, 0); + + while (in_last_scanline < stbr_info->ring_buffer_last_scanline) + stbr__decode_and_resample(stbr_info, stbr_info->ring_buffer_last_scanline + 1); + + // Now all buffers should be ready to do a row a vertical sampling. + //stbr__resample_vertical(); + } #ifdef STBR_DEBUG_OVERWRITE_TEST - STBR_DEBUG_ASSERT(memcmp(overwrite_contents_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); + STBR_DEBUG_ASSERT(memcmp(overwrite_output_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); + STBR_DEBUG_ASSERT(memcmp(overwrite_tempmem_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); #endif return 1; @@ -343,8 +526,9 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int input_st int decode_buffer_size = input_w * channels * sizeof(float); int contributors_size = output_w * sizeof(stbr__contributors); int coefficients_size = stbr__get_total_coefficients(filter, input_w, output_w) * sizeof(float); + int ring_buffer_size = output_w * channels * sizeof(float) * stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0); - return info_size + decode_buffer_size + contributors_size + coefficients_size; + return info_size + decode_buffer_size + contributors_size + coefficients_size + ring_buffer_size; } #endif // STB_RESAMPLE_IMPLEMENTATION From 9e726bb3e43b21d8b4b3a6412fd62490367401e6 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 24 Jul 2014 14:20:18 -0700 Subject: [PATCH 009/168] The vertical resampling pass. Now all elements of the upscale algorithm are in place. --- stb_resample.h | 170 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 135 insertions(+), 35 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index c3186e1..9747d63 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -180,12 +180,18 @@ typedef struct stbr__contributors* horizontal_contributors; float* horizontal_coefficients; + stbr__contributors vertical_contributors; + float* vertical_coefficients; + float* decode_buffer; + int ring_buffer_length; // The length of an individual entry in the ring buffer. The total number of ring buffers is kernel_texel_width int ring_buffer_first_scanline; int ring_buffer_last_scanline; int ring_buffer_begin_index; float* ring_buffer; + + float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. } stbr__info; @@ -263,11 +269,40 @@ static void stbr__calculate_sample_range(int n, float out_filter_radius, float s *in_last_texel = (int)(floor(in_texel_influence_upperbound - 0.5)); } +static void stbr__calculate_coefficients(stbr__info* stbr_info, int in_first_texel, int in_last_texel, float in_center_of_out, int n, stbr__contributors* contributor, float* coefficient_group) +{ + int i; + float total_filter = 0; + float filter_scale; + + STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr_info->kernel_texel_width); + STBR_DEBUG_ASSERT(in_first_texel >= 0); + STBR_DEBUG_ASSERT(in_last_texel < stbr_info->input_w); + + contributor->n0 = in_first_texel; + contributor->n1 = in_last_texel; + + for (i = 0; i <= in_last_texel - in_first_texel; i++) + { + float in_texel_center = (float)(i + in_first_texel) + 0.5f; + total_filter += coefficient_group[i] = stbr__filter_info_table[stbr_info->filter].kernel(in_center_of_out - in_texel_center); + } + + STBR_DEBUG_ASSERT(total_filter > 0); + STBR_DEBUG_ASSERT(fabs(1 - total_filter) < 0.1f); // Make sure it's not way off. + + // Make sure the sum of all coefficients is 1. + filter_scale = 1 / total_filter; + + for (i = 0; i <= in_last_texel - in_first_texel; i++) + coefficient_group[i] *= filter_scale; +} + // Each scan line uses the same kernel values so we should calculate the kernel // values once and then we can use them for every scan line. static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) { - int n, i; + int n; float scale_ratio = (float)stbr_info->output_w / stbr_info->input_w; float out_pixels_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; @@ -281,30 +316,7 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) stbr__calculate_sample_range(n, out_pixels_radius, scale_ratio, &in_first_texel, &in_last_texel, &in_center_of_out); - float total_filter = 0; - float filter_scale; - - STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr_info->kernel_texel_width); - STBR_DEBUG_ASSERT(in_first_texel >= 0); - STBR_DEBUG_ASSERT(in_last_texel < stbr_info->input_w); - - stbr__get_contributor(stbr_info, n)->n0 = in_first_texel; - stbr__get_contributor(stbr_info, n)->n1 = in_last_texel; - - for (i = 0; i <= in_last_texel - in_first_texel; i++) - { - float in_texel_center = (float)(i + in_first_texel) + 0.5f; - total_filter += *stbr__get_coefficient(stbr_info, n, i) = stbr__filter_info_table[stbr_info->filter].kernel(in_center_of_out - in_texel_center); - } - - STBR_DEBUG_ASSERT(total_filter > 0); - STBR_DEBUG_ASSERT(fabs(1-total_filter) < 0.1f); // Make sure it's not way off. - - // Make sure the sum of all coefficients is 1. - filter_scale = 1 / total_filter; - - for (i = 0; i <= in_last_texel - in_first_texel; i++) - *stbr__get_coefficient(stbr_info, n, i) *= filter_scale; + stbr__calculate_coefficients(stbr_info, in_first_texel, in_last_texel, in_center_of_out, n, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); } } @@ -321,21 +333,31 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) int x, c; int channels = stbr_info->channels; int input_w = stbr_info->input_w; + int input_stride_bytes = stbr_info->input_stride_bytes; const void* input_data = stbr_info->input_data; float* decode_buffer = stbr_info->decode_buffer; + int in_buffer_row_index = n * input_stride_bytes; STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); for (x = 0; x < input_w; x++) { + int texel_index = x * channels; + for (c = 0; c < channels; c++) { - int buffer_index = x * channels + c; - decode_buffer[buffer_index] = ((float)((const unsigned char*)input_data)[buffer_index]) / 255; + int channel_index = x * channels + c; + int in_buffer_index = in_buffer_row_index + channel_index; + decode_buffer[channel_index] = ((float)((const unsigned char*)input_data)[in_buffer_index]) / 255; } } } +static float* stbr__get_ring_buffer_index(float* ring_buffer, int index, int ring_buffer_length) +{ + return &ring_buffer[index * ring_buffer_length]; +} + static void stbr__resample_horizontal(stbr__info* stbr_info, int n) { int x, k, c; @@ -357,7 +379,9 @@ static void stbr__resample_horizontal(stbr__info* stbr_info, int n) STBR_DEBUG_ASSERT(ring_buffer_index != stbr_info->ring_buffer_begin_index); } - ring_buffer = &stbr_info->ring_buffer[ring_buffer_index]; + ring_buffer = stbr__get_ring_buffer_index(stbr_info->ring_buffer, ring_buffer_index, stbr_info->ring_buffer_length); + + memset(ring_buffer, 0, stbr_info->ring_buffer_length); for (x = 0; x < output_w; x++) { @@ -374,12 +398,13 @@ static void stbr__resample_horizontal(stbr__info* stbr_info, int n) { int coefficient_index = coefficient_group_index + (coefficient_counter++); int in_texel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_index]; - if (!horizontal_coefficients[coefficient_index]) + if (!coefficient) continue; for (c = 0; c < channels; c++) - ring_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * horizontal_coefficients[coefficient_index]; + ring_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; } } @@ -388,6 +413,12 @@ static void stbr__resample_horizontal(stbr__info* stbr_info, int n) static void stbr__decode_and_resample(stbr__info* stbr_info, int n) { + if (n >= stbr_info->input_h) + { + STBR_UNIMPLEMENTED("ring buffer overran source height"); + return; + } + // Decode the nth scanline from the source image into the decode buffer. stbr__decode_scanline(stbr_info, n); @@ -397,6 +428,70 @@ static void stbr__decode_and_resample(stbr__info* stbr_info, int n) // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. } +// Get the specified scan line from the ring buffer. +static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_size, int ring_buffer_length) +{ + int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_size; + return stbr__get_ring_buffer_index(ring_buffer, ring_buffer_index, ring_buffer_length); +} + +static void stbr__resample_vertical(stbr__info* stbr_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) +{ + int x, k, c; + int output_w = stbr_info->output_w; + stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; + float* vertical_coefficients = stbr_info->vertical_coefficients; + int channels = stbr_info->channels; + int kernel_texel_width = stbr_info->kernel_texel_width; + void* output_data = stbr_info->output_data; + float* encode_buffer = stbr_info->encode_buffer; + + float* ring_buffer = stbr_info->ring_buffer; + int ring_buffer_begin_index = stbr_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbr_info->ring_buffer_first_scanline; + int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; + int ring_buffer_length = stbr_info->ring_buffer_length; + + STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); + + stbr__calculate_coefficients(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, n, vertical_contributors, vertical_coefficients); + + int n0 = vertical_contributors->n0; + int n1 = vertical_contributors->n1; + + int output_row_index = n * stbr_info->output_stride_bytes; + + STBR_DEBUG_ASSERT(n0 >= in_first_scanline); + STBR_DEBUG_ASSERT(n1 <= in_last_scanline); + + for (x = 0; x < output_w; x++) + { + int in_texel_index = x * channels; + int out_texel_index = output_row_index + x * channels; + int coefficient_counter = 0; + + STBR_DEBUG_ASSERT(n1 >= n0); + + memset(encode_buffer, 0, sizeof(float) * channels); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_texel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_index]; + + if (!coefficient) + continue; + + for (c = 0; c < channels; c++) + encode_buffer[c] += ring_buffer_entry[in_texel_index + c] * coefficient; + } + + for (c = 0; c < channels; c++) + ((unsigned char*)output_data)[out_texel_index + c] = (unsigned char)(encode_buffer[c] * 255); + } +} + STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, int channels, stbr_type type, stbr_filter filter, @@ -447,13 +542,16 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->total_coefficients = stbr__get_total_coefficients(filter, input_w, output_w); stbr_info->kernel_texel_width = stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0); + stbr_info->ring_buffer_length = output_w * channels * sizeof(float); #define STBR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) stbr_info->horizontal_contributors = STBR__NEXT_MEMPTR(stbr_info, sizeof(stbr__info), stbr__contributors); stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, output_w * sizeof(stbr__contributors), float); - stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr_info->total_coefficients * sizeof(stbr__contributors), float); + stbr_info->vertical_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr_info->total_coefficients * sizeof(float), float); + stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr_info->kernel_texel_width * sizeof(float), float); stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), float); + stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, output_w * channels * sizeof(float) * stbr_info->kernel_texel_width, float); #undef STBR__NEXT_MEMPTR @@ -497,13 +595,13 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input // Load in new ones. if (stbr_info->ring_buffer_begin_index < 0) - stbr__decode_and_resample(stbr_info, 0); + stbr__decode_and_resample(stbr_info, in_first_scanline); while (in_last_scanline < stbr_info->ring_buffer_last_scanline) stbr__decode_and_resample(stbr_info, stbr_info->ring_buffer_last_scanline + 1); // Now all buffers should be ready to do a row a vertical sampling. - //stbr__resample_vertical(); + stbr__resample_vertical(stbr_info, y, in_first_scanline, in_last_scanline, in_center_of_out); } #ifdef STBR_DEBUG_OVERWRITE_TEST @@ -525,10 +623,12 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int input_st int info_size = sizeof(stbr__info); int decode_buffer_size = input_w * channels * sizeof(float); int contributors_size = output_w * sizeof(stbr__contributors); - int coefficients_size = stbr__get_total_coefficients(filter, input_w, output_w) * sizeof(float); + int horizontal_coefficients_size = stbr__get_total_coefficients(filter, input_w, output_w) * sizeof(float); + int vertical_coefficients_size = stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0) * sizeof(float); int ring_buffer_size = output_w * channels * sizeof(float) * stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0); + int encode_buffer_size = channels * sizeof(float); - return info_size + decode_buffer_size + contributors_size + coefficients_size + ring_buffer_size; + return info_size + decode_buffer_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + ring_buffer_size + encode_buffer_size; } #endif // STB_RESAMPLE_IMPLEMENTATION From 8ac052ac8a8a9ac0dfbdabe77055509ba15fe20a Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 24 Jul 2014 15:02:39 -0700 Subject: [PATCH 010/168] Avoid gaps between box filter kernels. --- stb_resample.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stb_resample.h b/stb_resample.h index 9747d63..c95531d 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -197,7 +197,7 @@ typedef struct float stbr__filter_nearest(float x) { - if (fabs(x) < 0.5) + if (fabs(x) <= 0.5) return 1; else return 0; From 7d8faf5727e4e70cccf5cbf0e783717d02db6328 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 24 Jul 2014 15:02:54 -0700 Subject: [PATCH 011/168] Remove unused functions. --- stb_resample.h | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index c95531d..5c3e9c8 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -225,23 +225,6 @@ int stbr__get_total_coefficients(stbr_filter filter, int input_w, int output_w) return output_w * stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0); } -// i0 is a texel in [0, n0-1] -// What's the nearest texel center to i0's center in [0, n1-1] ? -// Remapping [0, n0-1] to [0, n1-1] gives (i0 + 0.5)*n1/n0 but we want to avoid -// floating point math so we rearrange it as (n1*i0 + n1/2)/n0 -stbr_inline static int stbr__nearest_texel(int i0, int n0, int n1) -{ - return (n1*i0 + n1/2) / n0; -} - -stbr_inline static stbr_size_t stbr__texel_index(int x, int y, int c, int width_stride, int num_c, int w, int h) -{ - STBR_DEBUG_ASSERT(x >= 0 && x < w); - STBR_DEBUG_ASSERT(y >= 0 && y < h); - - return y*width_stride + x*num_c + c; -} - stbr_inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_info, int n) { STBR_DEBUG_ASSERT(n >= 0 && n < stbr_info->output_w); From 297266b27bb5a4efbce6e5ad34e2c1bbab216d86 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 24 Jul 2014 19:10:45 -0700 Subject: [PATCH 012/168] Starting to implement downsampling. --- stb_resample.h | 130 ++++++++++++++++++++++++++++++++++++------------ tests/resample_test.cpp | 4 +- 2 files changed, 101 insertions(+), 33 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 5c3e9c8..0ecf7a4 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -152,10 +152,12 @@ typedef struct float support; } stbr__filter_info; +// When upsampling, the contributors are which source texels contribute. +// When downsampling, the contributors are which destination texels are contributed to. typedef struct { - int n0; // First contributing source texel - int n1; // Last contributing source texel + int n0; // First contributing texel + int n1; // Last contributing texel } stbr__contributors; typedef struct @@ -177,6 +179,7 @@ typedef struct int total_coefficients; int kernel_texel_width; + int total_horizontal_contributors; stbr__contributors* horizontal_contributors; float* horizontal_coefficients; @@ -195,7 +198,18 @@ typedef struct } stbr__info; -float stbr__filter_nearest(float x) +static stbr_inline int stbr__min(int a, int b) +{ + return a < b ? a : b; +} + +static stbr_inline int stbr__max(int a, int b) +{ + return a > b ? a : b; +} + + +static float stbr__filter_nearest(float x) { if (fabs(x) <= 0.5) return 1; @@ -203,31 +217,29 @@ float stbr__filter_nearest(float x) return 0; } -stbr__filter_info stbr__filter_info_table[] = { +static stbr__filter_info stbr__filter_info_table[] = { { NULL, 0.0f }, { stbr__filter_nearest, 0.5f }, }; // This is the maximum number of input samples that can affect an output sample // with the given filter -int stbr__get_filter_texel_width(stbr_filter filter, int upsample) +stbr_inline static int stbr__get_filter_texel_width(stbr_filter filter) { - STBR_UNIMPLEMENTED(!upsample); - STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); return (int)ceil(stbr__filter_info_table[filter].support * 2); } -int stbr__get_total_coefficients(stbr_filter filter, int input_w, int output_w) +stbr_inline static int stbr__get_total_coefficients(stbr_filter filter, int input_w, int output_w) { - return output_w * stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0); + return stbr__max(output_w, input_w) * stbr__get_filter_texel_width(filter); } stbr_inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_info, int n) { - STBR_DEBUG_ASSERT(n >= 0 && n < stbr_info->output_w); + STBR_DEBUG_ASSERT(n >= 0 && n < stbr_info->total_horizontal_contributors); return &stbr_info->horizontal_contributors[n]; } @@ -237,9 +249,8 @@ stbr_inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, in } // What input texels contribute to this output texel? -static void stbr__calculate_sample_range(int n, float out_filter_radius, float scale_ratio, int* in_first_texel, int* in_last_texel, float* in_center_of_out) +static void stbr__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, int* in_first_texel, int* in_last_texel, float* in_center_of_out) { - // What input texels contribute to this output texel? float out_texel_center = (float)n + 0.5f; float out_texel_influence_lowerbound = out_texel_center - out_filter_radius; float out_texel_influence_upperbound = out_texel_center + out_filter_radius; @@ -252,7 +263,22 @@ static void stbr__calculate_sample_range(int n, float out_filter_radius, float s *in_last_texel = (int)(floor(in_texel_influence_upperbound - 0.5)); } -static void stbr__calculate_coefficients(stbr__info* stbr_info, int in_first_texel, int in_last_texel, float in_center_of_out, int n, stbr__contributors* contributor, float* coefficient_group) +// What output texels does this input texel contribute to? +static void stbr__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, int* out_first_texel, int* out_last_texel, float* out_center_of_in) +{ + float in_texel_center = (float)n + 0.5f; + float in_texel_influence_lowerbound = in_texel_center - in_pixels_radius; + float in_texel_influence_upperbound = in_texel_center + in_pixels_radius; + + float out_texel_influence_lowerbound = in_texel_influence_lowerbound * scale_ratio; + float out_texel_influence_upperbound = in_texel_influence_upperbound * scale_ratio; + + *out_center_of_in = in_texel_center * scale_ratio; + *out_last_texel = (int)(floor(out_texel_influence_lowerbound + 0.5)); + *out_first_texel = (int)(floor(out_texel_influence_upperbound - 0.5)); +} + +static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_first_texel, int in_last_texel, float in_center_of_out, int n, stbr__contributors* contributor, float* coefficient_group) { int i; float total_filter = 0; @@ -260,7 +286,7 @@ static void stbr__calculate_coefficients(stbr__info* stbr_info, int in_first_tex STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr_info->kernel_texel_width); STBR_DEBUG_ASSERT(in_first_texel >= 0); - STBR_DEBUG_ASSERT(in_last_texel < stbr_info->input_w); + STBR_DEBUG_ASSERT(in_last_texel < stbr_info->total_horizontal_contributors); contributor->n0 = in_first_texel; contributor->n1 = in_last_texel; @@ -281,6 +307,26 @@ static void stbr__calculate_coefficients(stbr__info* stbr_info, int in_first_tex coefficient_group[i] *= filter_scale; } +static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, int out_first_texel, int out_last_texel, float out_center_of_in, int n, stbr__contributors* contributor, float* coefficient_group) +{ + int i; + + float scale_ratio = (float)stbr_info->output_w / stbr_info->input_w; + + STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr_info->kernel_texel_width); + STBR_DEBUG_ASSERT(out_first_texel >= 0); + STBR_DEBUG_ASSERT(out_last_texel < stbr_info->total_horizontal_contributors); + + contributor->n0 = out_first_texel; + contributor->n1 = out_last_texel; + + for (i = 0; i <= out_last_texel - out_first_texel; i++) + { + float in_texel_center = (float)(i + out_first_texel) + 0.5f; + coefficient_group[i] = stbr__filter_info_table[stbr_info->filter].kernel(out_center_of_in - in_texel_center) * scale_ratio; + } +} + // Each scan line uses the same kernel values so we should calculate the kernel // values once and then we can use them for every scan line. static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) @@ -288,18 +334,37 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) int n; float scale_ratio = (float)stbr_info->output_w / stbr_info->input_w; - float out_pixels_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; + int total_contributors = stbr_info->total_horizontal_contributors; - STBR_UNIMPLEMENTED(stbr_info->output_w < stbr_info->input_w); - - for (n = 0; n < stbr_info->output_w; n++) + if (stbr_info->output_w > stbr_info->input_w) { - float in_center_of_out; // Center of the current out texel in the in texel space - int in_first_texel, in_last_texel; + float out_pixels_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; - stbr__calculate_sample_range(n, out_pixels_radius, scale_ratio, &in_first_texel, &in_last_texel, &in_center_of_out); + // Looping through out texels + for (n = 0; n < total_contributors; n++) + { + float in_center_of_out; // Center of the current out texel in the in texel space + int in_first_texel, in_last_texel; - stbr__calculate_coefficients(stbr_info, in_first_texel, in_last_texel, in_center_of_out, n, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); + stbr__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, &in_first_texel, &in_last_texel, &in_center_of_out); + + stbr__calculate_coefficients_upsample(stbr_info, in_first_texel, in_last_texel, in_center_of_out, n, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); + } + } + else + { + float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; + + // Looping through in texels + for (n = 0; n < total_contributors; n++) + { + float out_center_of_in; // Center of the current out texel in the in texel space + int out_first_texel, out_last_texel; + + stbr__calculate_sample_range_downsample(n, in_pixels_radius, scale_ratio, &out_first_texel, &out_last_texel, &out_center_of_in); + + stbr__calculate_coefficients_downsample(stbr_info, out_first_texel, out_last_texel, out_center_of_in, n, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); + } } } @@ -343,6 +408,7 @@ static float* stbr__get_ring_buffer_index(float* ring_buffer, int index, int rin static void stbr__resample_horizontal(stbr__info* stbr_info, int n) { + STBR_UNIMPLEMENTED(stbr_info->output_w < stbr_info->input_w); int x, k, c; int output_w = stbr_info->output_w; int kernel_texel_width = stbr_info->kernel_texel_width; @@ -420,6 +486,7 @@ static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffe static void stbr__resample_vertical(stbr__info* stbr_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) { + STBR_UNIMPLEMENTED(stbr_info->output_w < stbr_info->input_w); int x, k, c; int output_w = stbr_info->output_w; stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; @@ -437,7 +504,7 @@ static void stbr__resample_vertical(stbr__info* stbr_info, int n, int in_first_s STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); - stbr__calculate_coefficients(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, n, vertical_contributors, vertical_coefficients); + STBR_UNIMPLEMENTED("stbr__calculate_coefficients(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, n, vertical_contributors, vertical_coefficients)"); int n0 = vertical_contributors->n0; int n1 = vertical_contributors->n1; @@ -524,13 +591,14 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->filter = filter; stbr_info->total_coefficients = stbr__get_total_coefficients(filter, input_w, output_w); - stbr_info->kernel_texel_width = stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0); + stbr_info->kernel_texel_width = stbr__get_filter_texel_width(filter); stbr_info->ring_buffer_length = output_w * channels * sizeof(float); + stbr_info->total_horizontal_contributors = stbr__max(input_w, output_w); #define STBR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) stbr_info->horizontal_contributors = STBR__NEXT_MEMPTR(stbr_info, sizeof(stbr__info), stbr__contributors); - stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, output_w * sizeof(stbr__contributors), float); + stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, stbr_info->total_horizontal_contributors * sizeof(stbr__contributors), float); stbr_info->vertical_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr_info->total_coefficients * sizeof(float), float); stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr_info->kernel_texel_width * sizeof(float), float); stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), float); @@ -548,10 +616,10 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input for (y = 0; y < output_h; y++) { - float in_center_of_out; // Center of the current out scanline in the in scanline space - int in_first_scanline, in_last_scanline; + float in_center_of_out = 0; // Center of the current out scanline in the in scanline space + int in_first_scanline = 0, in_last_scanline = 0; - stbr__calculate_sample_range(y, out_scanlines_radius, scale_ratio, &in_first_scanline, &in_last_scanline, &in_center_of_out); + STBR_UNIMPLEMENTED("stbr__calculate_sample_range(y, out_scanlines_radius, scale_ratio, &in_first_scanline, &in_last_scanline, &in_center_of_out)"); STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr_info->kernel_texel_width); STBR_DEBUG_ASSERT(in_first_scanline >= 0); @@ -605,10 +673,10 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int input_st int info_size = sizeof(stbr__info); int decode_buffer_size = input_w * channels * sizeof(float); - int contributors_size = output_w * sizeof(stbr__contributors); + int contributors_size = stbr__max(output_w, input_w) * sizeof(stbr__contributors); int horizontal_coefficients_size = stbr__get_total_coefficients(filter, input_w, output_w) * sizeof(float); - int vertical_coefficients_size = stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0) * sizeof(float); - int ring_buffer_size = output_w * channels * sizeof(float) * stbr__get_filter_texel_width(filter, output_w > input_w ? 1 : 0); + int vertical_coefficients_size = stbr__get_filter_texel_width(filter) * sizeof(float); + int ring_buffer_size = output_w * channels * sizeof(float) * stbr__get_filter_texel_width(filter); int encode_buffer_size = channels * sizeof(float); return info_size + decode_buffer_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + ring_buffer_size + encode_buffer_size; diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 2ee2ffb..f47068e 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -39,8 +39,8 @@ int main(int argc, char** argv) return 1; } - out_w = 1024; - out_h = 1024; + out_w = 256; + out_h = 256; out_stride = (out_w + 10) * n; output_data = (unsigned char*)malloc(out_stride * out_h); From fa69bc8551a837e59098600493b2d2da9784cfef Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 24 Jul 2014 22:09:08 -0700 Subject: [PATCH 013/168] Basic downsampling algorithm works for uniform sampling. --- stb_resample.h | 361 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 291 insertions(+), 70 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 0ecf7a4..dcfe614 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -188,6 +188,8 @@ typedef struct float* decode_buffer; + float* horizontal_buffer; + int ring_buffer_length; // The length of an individual entry in the ring buffer. The total number of ring buffers is kernel_texel_width int ring_buffer_first_scanline; int ring_buffer_last_scanline; @@ -401,12 +403,36 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) } } -static float* stbr__get_ring_buffer_index(float* ring_buffer, int index, int ring_buffer_length) +static float* stbr__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) { return &ring_buffer[index * ring_buffer_length]; } -static void stbr__resample_horizontal(stbr__info* stbr_info, int n) +static float* stbr__add_empty_ring_buffer_entry(stbr__info* stbr_info, int n) +{ + int ring_buffer_index; + float* ring_buffer; + + if (stbr_info->ring_buffer_begin_index < 0) + { + ring_buffer_index = stbr_info->ring_buffer_begin_index = 0; + stbr_info->ring_buffer_first_scanline = n; + } + else + { + ring_buffer_index = (stbr_info->ring_buffer_begin_index + (stbr_info->ring_buffer_last_scanline - stbr_info->ring_buffer_first_scanline) + 1) % stbr_info->kernel_texel_width; + STBR_DEBUG_ASSERT(ring_buffer_index != stbr_info->ring_buffer_begin_index); + } + + ring_buffer = stbr__get_ring_buffer_entry(stbr_info->ring_buffer, ring_buffer_index, stbr_info->ring_buffer_length); + memset(ring_buffer, 0, stbr_info->ring_buffer_length); + + stbr_info->ring_buffer_last_scanline = n; + + return ring_buffer; +} + +static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n) { STBR_UNIMPLEMENTED(stbr_info->output_w < stbr_info->input_w); int x, k, c; @@ -417,20 +443,7 @@ static void stbr__resample_horizontal(stbr__info* stbr_info, int n) stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; float* horizontal_coefficients = stbr_info->horizontal_coefficients; - int ring_buffer_index; - float* ring_buffer; - - if (stbr_info->ring_buffer_begin_index < 0) - ring_buffer_index = stbr_info->ring_buffer_begin_index = 0; - else - { - ring_buffer_index = (stbr_info->ring_buffer_begin_index + (stbr_info->ring_buffer_last_scanline - stbr_info->ring_buffer_first_scanline) + 1) % stbr_info->kernel_texel_width; - STBR_DEBUG_ASSERT(ring_buffer_index != stbr_info->ring_buffer_begin_index); - } - - ring_buffer = stbr__get_ring_buffer_index(stbr_info->ring_buffer, ring_buffer_index, stbr_info->ring_buffer_length); - - memset(ring_buffer, 0, stbr_info->ring_buffer_length); + float* ring_buffer = stbr__add_empty_ring_buffer_entry(stbr_info, n); for (x = 0; x < output_w; x++) { @@ -456,11 +469,55 @@ static void stbr__resample_horizontal(stbr__info* stbr_info, int n) ring_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; } } - - stbr_info->ring_buffer_last_scanline = n; } -static void stbr__decode_and_resample(stbr__info* stbr_info, int n) +static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n) +{ + int x, k, c; + int input_w = stbr_info->input_w; + int kernel_texel_width = stbr_info->kernel_texel_width; + int channels = stbr_info->channels; + float* decode_buffer = stbr_info->decode_buffer; + stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; + float* horizontal_coefficients = stbr_info->horizontal_coefficients; + + float* horizontal_buffer = stbr_info->horizontal_buffer; + + STBR_DEBUG_ASSERT(stbr_info->output_h < stbr_info->input_h); + + memset(horizontal_buffer, 0, stbr_info->output_w * channels * sizeof(float)); + + for (x = 0; x < input_w; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_texel_index = x * channels; + int coefficient_group_index = x * kernel_texel_width; + int coefficient_counter = 0; + + STBR_DEBUG_ASSERT(n1 >= n0); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_group_index + (coefficient_counter++); + int out_texel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_index]; + + if (!coefficient) + continue; + + for (c = 0; c < channels; c++) + { + horizontal_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; + + STBR_DEBUG_ASSERT(horizontal_buffer[out_texel_index + c] <= 1.0); // This would indicate that the sum of kernels for this texel doesn't add to 1. + } + } + } +} + +static void stbr__decode_and_resample_upsample(stbr__info* stbr_info, int n) { if (n >= stbr_info->input_h) { @@ -472,21 +529,37 @@ static void stbr__decode_and_resample(stbr__info* stbr_info, int n) stbr__decode_scanline(stbr_info, n); // Now resample it into the ring buffer. - stbr__resample_horizontal(stbr_info, n); + stbr__resample_horizontal_upsample(stbr_info, n); // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. } +static void stbr__decode_and_resample_downsample(stbr__info* stbr_info, int n) +{ + if (n >= stbr_info->input_h) + { + STBR_UNIMPLEMENTED("ring buffer overran source height"); + return; + } + + // Decode the nth scanline from the source image into the decode buffer. + stbr__decode_scanline(stbr_info, n); + + // Now resample it into the horizontal buffer. + stbr__resample_horizontal_downsample(stbr_info, n); + + // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. +} + // Get the specified scan line from the ring buffer. static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_size, int ring_buffer_length) { int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_size; - return stbr__get_ring_buffer_index(ring_buffer, ring_buffer_index, ring_buffer_length); + return stbr__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); } -static void stbr__resample_vertical(stbr__info* stbr_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) +static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) { - STBR_UNIMPLEMENTED(stbr_info->output_w < stbr_info->input_w); int x, k, c; int output_w = stbr_info->output_w; stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; @@ -511,6 +584,7 @@ static void stbr__resample_vertical(stbr__info* stbr_info, int n, int in_first_s int output_row_index = n * stbr_info->output_stride_bytes; + STBR_DEBUG_ASSERT(stbr_info->output_w > stbr_info->input_w); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); STBR_DEBUG_ASSERT(n1 <= in_last_scanline); @@ -542,12 +616,196 @@ static void stbr__resample_vertical(stbr__info* stbr_info, int n, int in_first_s } } +static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) +{ + int x, k, c; + int output_w = stbr_info->output_w; + stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; + float* vertical_coefficients = stbr_info->vertical_coefficients; + int channels = stbr_info->channels; + int kernel_texel_width = stbr_info->kernel_texel_width; + void* output_data = stbr_info->output_data; + float* horizontal_buffer = stbr_info->horizontal_buffer; + + float* ring_buffer = stbr_info->ring_buffer; + int ring_buffer_begin_index = stbr_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbr_info->ring_buffer_first_scanline; + int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; + int ring_buffer_length = stbr_info->ring_buffer_length; + + stbr__calculate_coefficients_downsample(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, n, vertical_contributors, vertical_coefficients); + + int n0 = vertical_contributors->n0; + int n1 = vertical_contributors->n1; + + STBR_DEBUG_ASSERT(stbr_info->output_w < stbr_info->input_w); + STBR_DEBUG_ASSERT(n0 >= in_first_scanline); + STBR_DEBUG_ASSERT(n1 <= in_last_scanline); + + for (x = 0; x < output_w; x++) + { + int in_texel_index = x * channels; + int coefficient_counter = 0; + + STBR_DEBUG_ASSERT(n1 >= n0); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_texel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_index]; + + if (!coefficient) + continue; + + for (c = 0; c < channels; c++) + { + int index = in_texel_index + c; + ring_buffer_entry[index] += horizontal_buffer[index] * coefficient; + + STBR_DEBUG_ASSERT(ring_buffer_entry[index] <= 1.0); // This would indicate that the sum of kernels for this texel doesn't add to 1. + } + } + } +} + +static void stbr__buffer_loop_upsample(stbr__info* stbr_info) +{ + int y; + float scale_ratio = (float)stbr_info->output_h / stbr_info->input_h; + float out_scanlines_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; + + STBR_DEBUG_ASSERT(stbr_info->output_h > stbr_info->input_h); + + for (y = 0; y < stbr_info->output_h; y++) + { + float in_center_of_out = 0; // Center of the current out scanline in the in scanline space + int in_first_scanline = 0, in_last_scanline = 0; + + STBR_UNIMPLEMENTED("stbr__calculate_sample_range(y, out_scanlines_radius, scale_ratio, &in_first_scanline, &in_last_scanline, &in_center_of_out)"); + + STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr_info->kernel_texel_width); + STBR_DEBUG_ASSERT(in_first_scanline >= 0); + STBR_DEBUG_ASSERT(in_last_scanline < stbr_info->input_w); + + if (stbr_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (in_first_scanline > stbr_info->ring_buffer_first_scanline) + { + if (stbr_info->ring_buffer_first_scanline == stbr_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbr_info->ring_buffer_begin_index = -1; + stbr_info->ring_buffer_first_scanline = 0; + stbr_info->ring_buffer_last_scanline = 0; + break; + } + else + stbr_info->ring_buffer_first_scanline++; + } + } + + // Load in new ones. + if (stbr_info->ring_buffer_begin_index < 0) + stbr__decode_and_resample_upsample(stbr_info, in_first_scanline); + + while (in_last_scanline < stbr_info->ring_buffer_last_scanline) + stbr__decode_and_resample_upsample(stbr_info, stbr_info->ring_buffer_last_scanline + 1); + + // Now all buffers should be ready to write a row of vertical sampling. + stbr__resample_vertical_upsample(stbr_info, y, in_first_scanline, in_last_scanline, in_center_of_out); + } +} + +static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_scanline) +{ + int output_stride_bytes = stbr_info->output_stride_bytes; + int channels = stbr_info->channels; + int output_w = stbr_info->output_w; + void* output_data = stbr_info->output_data; + + float* ring_buffer = stbr_info->ring_buffer; + int ring_buffer_length = stbr_info->ring_buffer_length; + + if (stbr_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (first_necessary_scanline > stbr_info->ring_buffer_first_scanline || first_necessary_scanline < 0) + { + int x, c; + int output_row = stbr_info->ring_buffer_first_scanline * output_stride_bytes; + float* ring_buffer_entry = stbr__get_ring_buffer_entry(ring_buffer, stbr_info->ring_buffer_begin_index, ring_buffer_length); + + STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); + + for (x = 0; x < output_w; x++) + { + int texel_index = x * channels; + int ring_texel_index = texel_index; + int output_texel_index = output_row + texel_index; + for (c = 0; c < channels; c++) + ((unsigned char*)output_data)[output_texel_index + c] = (unsigned char)(ring_buffer_entry[ring_texel_index + c] * 255); + } + + if (stbr_info->ring_buffer_first_scanline == stbr_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbr_info->ring_buffer_begin_index = -1; + stbr_info->ring_buffer_first_scanline = 0; + stbr_info->ring_buffer_last_scanline = 0; + break; + } + else + stbr_info->ring_buffer_first_scanline++; + } + } +} + +static void stbr__buffer_loop_downsample(stbr__info* stbr_info) +{ + int y; + float scale_ratio = (float)stbr_info->output_h / stbr_info->input_h; + float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; + + STBR_DEBUG_ASSERT(stbr_info->input_h > stbr_info->output_h); + + for (y = 0; y < stbr_info->input_h; y++) + { + float out_center_of_in; // Center of the current out scanline in the in scanline space + int out_first_scanline, out_last_scanline; + + stbr__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, &out_first_scanline, &out_last_scanline, &out_center_of_in); + + STBR_DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbr_info->kernel_texel_width); + STBR_DEBUG_ASSERT(out_first_scanline >= 0); + STBR_DEBUG_ASSERT(out_last_scanline < stbr_info->output_h); + + stbr__empty_ring_buffer(stbr_info, out_first_scanline); + + stbr__decode_and_resample_downsample(stbr_info, y); + + // Load in new ones. + if (stbr_info->ring_buffer_begin_index < 0) + stbr__add_empty_ring_buffer_entry(stbr_info, out_last_scanline); + + while (out_last_scanline < stbr_info->ring_buffer_last_scanline) + stbr__add_empty_ring_buffer_entry(stbr_info, stbr_info->ring_buffer_last_scanline + 1); + + // Now the horizontal buffer is ready to write to all ring buffer rows. + stbr__resample_vertical_downsample(stbr_info, y, out_first_scanline, out_last_scanline, out_center_of_in); + } + + stbr__empty_ring_buffer(stbr_info, -1); +} + STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, int channels, stbr_type type, stbr_filter filter, void* tempmem, stbr_size_t tempmem_size_in_bytes) { - int y; int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w; int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w; @@ -601,7 +859,8 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, stbr_info->total_horizontal_contributors * sizeof(stbr__contributors), float); stbr_info->vertical_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr_info->total_coefficients * sizeof(float), float); stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr_info->kernel_texel_width * sizeof(float), float); - stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), float); + stbr_info->horizontal_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), float); + stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->horizontal_buffer, output_w * channels * sizeof(float), float); stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, output_w * channels * sizeof(float) * stbr_info->kernel_texel_width, float); #undef STBR__NEXT_MEMPTR @@ -611,49 +870,10 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr__calculate_horizontal_filters(stbr_info); - float scale_ratio = (float)output_h / input_h; - float out_scanlines_radius = stbr__filter_info_table[filter].support * scale_ratio; - - for (y = 0; y < output_h; y++) - { - float in_center_of_out = 0; // Center of the current out scanline in the in scanline space - int in_first_scanline = 0, in_last_scanline = 0; - - STBR_UNIMPLEMENTED("stbr__calculate_sample_range(y, out_scanlines_radius, scale_ratio, &in_first_scanline, &in_last_scanline, &in_center_of_out)"); - - STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr_info->kernel_texel_width); - STBR_DEBUG_ASSERT(in_first_scanline >= 0); - STBR_DEBUG_ASSERT(in_last_scanline < input_w); - - if (stbr_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (in_first_scanline > stbr_info->ring_buffer_first_scanline) - { - if (stbr_info->ring_buffer_first_scanline == stbr_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbr_info->ring_buffer_begin_index = -1; - stbr_info->ring_buffer_first_scanline = 0; - stbr_info->ring_buffer_last_scanline = 0; - break; - } - else - stbr_info->ring_buffer_first_scanline++; - } - } - - // Load in new ones. - if (stbr_info->ring_buffer_begin_index < 0) - stbr__decode_and_resample(stbr_info, in_first_scanline); - - while (in_last_scanline < stbr_info->ring_buffer_last_scanline) - stbr__decode_and_resample(stbr_info, stbr_info->ring_buffer_last_scanline + 1); - - // Now all buffers should be ready to do a row a vertical sampling. - stbr__resample_vertical(stbr_info, y, in_first_scanline, in_last_scanline, in_center_of_out); - } + if (stbr_info->output_h >= stbr_info->input_h) + stbr__buffer_loop_upsample(stbr_info); + else + stbr__buffer_loop_downsample(stbr_info); #ifdef STBR_DEBUG_OVERWRITE_TEST STBR_DEBUG_ASSERT(memcmp(overwrite_output_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); @@ -672,14 +892,15 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int input_st STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); int info_size = sizeof(stbr__info); - int decode_buffer_size = input_w * channels * sizeof(float); int contributors_size = stbr__max(output_w, input_w) * sizeof(stbr__contributors); int horizontal_coefficients_size = stbr__get_total_coefficients(filter, input_w, output_w) * sizeof(float); int vertical_coefficients_size = stbr__get_filter_texel_width(filter) * sizeof(float); + int decode_buffer_size = input_w * channels * sizeof(float); + int horizontal_buffer_size = output_w * channels * sizeof(float); int ring_buffer_size = output_w * channels * sizeof(float) * stbr__get_filter_texel_width(filter); int encode_buffer_size = channels * sizeof(float); - return info_size + decode_buffer_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + ring_buffer_size + encode_buffer_size; + return info_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size; } #endif // STB_RESAMPLE_IMPLEMENTATION From dbb7480f12a96cf480e59fe0be4778e63c936739 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 24 Jul 2014 22:30:35 -0700 Subject: [PATCH 014/168] Fix nonuniform downsampling. --- stb_resample.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index dcfe614..2d57c45 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -309,12 +309,10 @@ static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_ coefficient_group[i] *= filter_scale; } -static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, int out_first_texel, int out_last_texel, float out_center_of_in, int n, stbr__contributors* contributor, float* coefficient_group) +static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float scale_ratio, int out_first_texel, int out_last_texel, float out_center_of_in, int n, stbr__contributors* contributor, float* coefficient_group) { int i; - float scale_ratio = (float)stbr_info->output_w / stbr_info->input_w; - STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr_info->kernel_texel_width); STBR_DEBUG_ASSERT(out_first_texel >= 0); STBR_DEBUG_ASSERT(out_last_texel < stbr_info->total_horizontal_contributors); @@ -365,7 +363,7 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) stbr__calculate_sample_range_downsample(n, in_pixels_radius, scale_ratio, &out_first_texel, &out_last_texel, &out_center_of_in); - stbr__calculate_coefficients_downsample(stbr_info, out_first_texel, out_last_texel, out_center_of_in, n, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); + stbr__calculate_coefficients_downsample(stbr_info, scale_ratio, out_first_texel, out_last_texel, out_center_of_in, n, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); } } } @@ -633,7 +631,7 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; int ring_buffer_length = stbr_info->ring_buffer_length; - stbr__calculate_coefficients_downsample(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, n, vertical_contributors, vertical_coefficients); + stbr__calculate_coefficients_downsample(stbr_info, (float)stbr_info->output_h / stbr_info->input_h, in_first_scanline, in_last_scanline, in_center_of_out, n, vertical_contributors, vertical_coefficients); int n0 = vertical_contributors->n0; int n1 = vertical_contributors->n1; From 178e301ea4a5546825dc3d431e5f6ed20f0fa389 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 24 Jul 2014 22:54:35 -0700 Subject: [PATCH 015/168] Fix upsampling, avoid dereferencing in an inner loop. --- stb_resample.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 2d57c45..9a23e80 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -285,6 +285,7 @@ static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_ int i; float total_filter = 0; float filter_scale; + stbr_filter filter = stbr_info->filter; STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr_info->kernel_texel_width); STBR_DEBUG_ASSERT(in_first_texel >= 0); @@ -296,7 +297,7 @@ static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_ for (i = 0; i <= in_last_texel - in_first_texel; i++) { float in_texel_center = (float)(i + in_first_texel) + 0.5f; - total_filter += coefficient_group[i] = stbr__filter_info_table[stbr_info->filter].kernel(in_center_of_out - in_texel_center); + total_filter += coefficient_group[i] = stbr__filter_info_table[filter].kernel(in_center_of_out - in_texel_center); } STBR_DEBUG_ASSERT(total_filter > 0); @@ -312,6 +313,7 @@ static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_ static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float scale_ratio, int out_first_texel, int out_last_texel, float out_center_of_in, int n, stbr__contributors* contributor, float* coefficient_group) { int i; + stbr_filter filter = stbr_info->filter; STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr_info->kernel_texel_width); STBR_DEBUG_ASSERT(out_first_texel >= 0); @@ -323,7 +325,7 @@ static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float for (i = 0; i <= out_last_texel - out_first_texel; i++) { float in_texel_center = (float)(i + out_first_texel) + 0.5f; - coefficient_group[i] = stbr__filter_info_table[stbr_info->filter].kernel(out_center_of_in - in_texel_center) * scale_ratio; + coefficient_group[i] = stbr__filter_info_table[filter].kernel(out_center_of_in - in_texel_center) * scale_ratio; } } @@ -575,7 +577,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); - STBR_UNIMPLEMENTED("stbr__calculate_coefficients(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, n, vertical_contributors, vertical_coefficients)"); + stbr__calculate_coefficients_upsample(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, n, vertical_contributors, vertical_coefficients); int n0 = vertical_contributors->n0; int n1 = vertical_contributors->n1; @@ -680,7 +682,7 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) float in_center_of_out = 0; // Center of the current out scanline in the in scanline space int in_first_scanline = 0, in_last_scanline = 0; - STBR_UNIMPLEMENTED("stbr__calculate_sample_range(y, out_scanlines_radius, scale_ratio, &in_first_scanline, &in_last_scanline, &in_center_of_out)"); + stbr__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, &in_first_scanline, &in_last_scanline, &in_center_of_out); STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr_info->kernel_texel_width); STBR_DEBUG_ASSERT(in_first_scanline >= 0); From 736596ba093a81912e75d82eab3124a7af479579 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 24 Jul 2014 23:27:29 -0700 Subject: [PATCH 016/168] Fix non uniform scaling where out_w > in_w && out_h < in_h. --- stb_resample.h | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 9a23e80..905eea3 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -432,7 +432,7 @@ static float* stbr__add_empty_ring_buffer_entry(stbr__info* stbr_info, int n) return ring_buffer; } -static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n) +static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, float* output_buffer) { STBR_UNIMPLEMENTED(stbr_info->output_w < stbr_info->input_w); int x, k, c; @@ -443,8 +443,6 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n) stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; float* horizontal_coefficients = stbr_info->horizontal_coefficients; - float* ring_buffer = stbr__add_empty_ring_buffer_entry(stbr_info, n); - for (x = 0; x < output_w; x++) { int n0 = horizontal_contributors[x].n0; @@ -466,7 +464,11 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n) continue; for (c = 0; c < channels; c++) - ring_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; + { + output_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; + + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + c] <= 1.0f); + } } } } @@ -483,9 +485,7 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n) float* horizontal_buffer = stbr_info->horizontal_buffer; - STBR_DEBUG_ASSERT(stbr_info->output_h < stbr_info->input_h); - - memset(horizontal_buffer, 0, stbr_info->output_w * channels * sizeof(float)); + STBR_DEBUG_ASSERT(stbr_info->output_w < stbr_info->input_w); for (x = 0; x < input_w; x++) { @@ -529,7 +529,7 @@ static void stbr__decode_and_resample_upsample(stbr__info* stbr_info, int n) stbr__decode_scanline(stbr_info, n); // Now resample it into the ring buffer. - stbr__resample_horizontal_upsample(stbr_info, n); + stbr__resample_horizontal_upsample(stbr_info, n, stbr__add_empty_ring_buffer_entry(stbr_info, n)); // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. } @@ -545,8 +545,13 @@ static void stbr__decode_and_resample_downsample(stbr__info* stbr_info, int n) // Decode the nth scanline from the source image into the decode buffer. stbr__decode_scanline(stbr_info, n); + memset(stbr_info->horizontal_buffer, 0, stbr_info->output_w * stbr_info->channels * sizeof(float)); + // Now resample it into the horizontal buffer. - stbr__resample_horizontal_downsample(stbr_info, n); + if (stbr_info->output_w > stbr_info->input_w) + stbr__resample_horizontal_upsample(stbr_info, n, stbr_info->horizontal_buffer); + else + stbr__resample_horizontal_downsample(stbr_info, n); // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. } @@ -638,7 +643,7 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int int n0 = vertical_contributors->n0; int n1 = vertical_contributors->n1; - STBR_DEBUG_ASSERT(stbr_info->output_w < stbr_info->input_w); + STBR_DEBUG_ASSERT(stbr_info->output_h < stbr_info->input_h); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); STBR_DEBUG_ASSERT(n1 <= in_last_scanline); From 666c025710c85ebada2762913169409a5ebefb0d Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 24 Jul 2014 23:32:25 -0700 Subject: [PATCH 017/168] Fix non uniform scaling where out_w < in_w && out_h > in_h. --- stb_resample.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 905eea3..cd9122f 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -473,7 +473,7 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo } } -static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n) +static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, float* output_buffer) { int x, k, c; int input_w = stbr_info->input_w; @@ -483,8 +483,6 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n) stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; float* horizontal_coefficients = stbr_info->horizontal_coefficients; - float* horizontal_buffer = stbr_info->horizontal_buffer; - STBR_DEBUG_ASSERT(stbr_info->output_w < stbr_info->input_w); for (x = 0; x < input_w; x++) @@ -509,9 +507,9 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n) for (c = 0; c < channels; c++) { - horizontal_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; + output_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; - STBR_DEBUG_ASSERT(horizontal_buffer[out_texel_index + c] <= 1.0); // This would indicate that the sum of kernels for this texel doesn't add to 1. + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + c] <= 1.0); // This would indicate that the sum of kernels for this texel doesn't add to 1. } } } @@ -529,7 +527,10 @@ static void stbr__decode_and_resample_upsample(stbr__info* stbr_info, int n) stbr__decode_scanline(stbr_info, n); // Now resample it into the ring buffer. - stbr__resample_horizontal_upsample(stbr_info, n, stbr__add_empty_ring_buffer_entry(stbr_info, n)); + if (stbr_info->output_w > stbr_info->input_w) + stbr__resample_horizontal_upsample(stbr_info, n, stbr__add_empty_ring_buffer_entry(stbr_info, n)); + else + stbr__resample_horizontal_downsample(stbr_info, n, stbr__add_empty_ring_buffer_entry(stbr_info, n)); // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. } @@ -551,7 +552,7 @@ static void stbr__decode_and_resample_downsample(stbr__info* stbr_info, int n) if (stbr_info->output_w > stbr_info->input_w) stbr__resample_horizontal_upsample(stbr_info, n, stbr_info->horizontal_buffer); else - stbr__resample_horizontal_downsample(stbr_info, n); + stbr__resample_horizontal_downsample(stbr_info, n, stbr_info->horizontal_buffer); // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. } @@ -589,7 +590,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int output_row_index = n * stbr_info->output_stride_bytes; - STBR_DEBUG_ASSERT(stbr_info->output_w > stbr_info->input_w); + STBR_DEBUG_ASSERT(stbr_info->output_h > stbr_info->input_h); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); STBR_DEBUG_ASSERT(n1 <= in_last_scanline); From 27926e78b808f98565eae2346dbe280cba797c6b Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 24 Jul 2014 23:50:14 -0700 Subject: [PATCH 018/168] Make consistent tests for whether we're doing upsampling or downsampling of width and height. Don't request memory for horizontal buffer or encode buffer if we don't need it. --- stb_resample.h | 65 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index cd9122f..e626e9c 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -224,6 +224,26 @@ static stbr__filter_info stbr__filter_info_table[] = { { stbr__filter_nearest, 0.5f }, }; +stbr_inline static int stbr__use_width_upsampling_noinfo(int output_w, int input_w) +{ + return output_w > input_w; +} + +stbr_inline static int stbr__use_height_upsampling_noinfo(int output_h, int input_h) +{ + return output_h > input_h; +} + +stbr_inline static int stbr__use_width_upsampling(stbr__info* stbr_info) +{ + return stbr__use_width_upsampling_noinfo(stbr_info->output_w, stbr_info->input_w); +} + +stbr_inline static int stbr__use_height_upsampling(stbr__info* stbr_info) +{ + return stbr__use_height_upsampling_noinfo(stbr_info->output_h, stbr_info->input_h); +} + // This is the maximum number of input samples that can affect an output sample // with the given filter stbr_inline static int stbr__get_filter_texel_width(stbr_filter filter) @@ -338,7 +358,7 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) int total_contributors = stbr_info->total_horizontal_contributors; - if (stbr_info->output_w > stbr_info->input_w) + if (stbr__use_width_upsampling(stbr_info)) { float out_pixels_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; @@ -434,7 +454,6 @@ static float* stbr__add_empty_ring_buffer_entry(stbr__info* stbr_info, int n) static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, float* output_buffer) { - STBR_UNIMPLEMENTED(stbr_info->output_w < stbr_info->input_w); int x, k, c; int output_w = stbr_info->output_w; int kernel_texel_width = stbr_info->kernel_texel_width; @@ -483,7 +502,7 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, f stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; float* horizontal_coefficients = stbr_info->horizontal_coefficients; - STBR_DEBUG_ASSERT(stbr_info->output_w < stbr_info->input_w); + STBR_DEBUG_ASSERT(!stbr__use_width_upsampling(stbr_info)); for (x = 0; x < input_w; x++) { @@ -527,7 +546,7 @@ static void stbr__decode_and_resample_upsample(stbr__info* stbr_info, int n) stbr__decode_scanline(stbr_info, n); // Now resample it into the ring buffer. - if (stbr_info->output_w > stbr_info->input_w) + if (stbr__use_width_upsampling(stbr_info)) stbr__resample_horizontal_upsample(stbr_info, n, stbr__add_empty_ring_buffer_entry(stbr_info, n)); else stbr__resample_horizontal_downsample(stbr_info, n, stbr__add_empty_ring_buffer_entry(stbr_info, n)); @@ -549,7 +568,7 @@ static void stbr__decode_and_resample_downsample(stbr__info* stbr_info, int n) memset(stbr_info->horizontal_buffer, 0, stbr_info->output_w * stbr_info->channels * sizeof(float)); // Now resample it into the horizontal buffer. - if (stbr_info->output_w > stbr_info->input_w) + if (stbr__use_width_upsampling(stbr_info)) stbr__resample_horizontal_upsample(stbr_info, n, stbr_info->horizontal_buffer); else stbr__resample_horizontal_downsample(stbr_info, n, stbr_info->horizontal_buffer); @@ -590,7 +609,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int output_row_index = n * stbr_info->output_stride_bytes; - STBR_DEBUG_ASSERT(stbr_info->output_h > stbr_info->input_h); + STBR_DEBUG_ASSERT(stbr__use_height_upsampling(stbr_info)); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); STBR_DEBUG_ASSERT(n1 <= in_last_scanline); @@ -644,7 +663,7 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int int n0 = vertical_contributors->n0; int n1 = vertical_contributors->n1; - STBR_DEBUG_ASSERT(stbr_info->output_h < stbr_info->input_h); + STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); STBR_DEBUG_ASSERT(n1 <= in_last_scanline); @@ -681,7 +700,7 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) float scale_ratio = (float)stbr_info->output_h / stbr_info->input_h; float out_scanlines_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; - STBR_DEBUG_ASSERT(stbr_info->output_h > stbr_info->input_h); + STBR_DEBUG_ASSERT(stbr__use_height_upsampling(stbr_info)); for (y = 0; y < stbr_info->output_h; y++) { @@ -776,7 +795,7 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) float scale_ratio = (float)stbr_info->output_h / stbr_info->input_h; float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; - STBR_DEBUG_ASSERT(stbr_info->input_h > stbr_info->output_h); + STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); for (y = 0; y < stbr_info->input_h; y++) { @@ -865,9 +884,19 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, stbr_info->total_horizontal_contributors * sizeof(stbr__contributors), float); stbr_info->vertical_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr_info->total_coefficients * sizeof(float), float); stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr_info->kernel_texel_width * sizeof(float), float); - stbr_info->horizontal_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), float); - stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->horizontal_buffer, output_w * channels * sizeof(float), float); - stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, output_w * channels * sizeof(float) * stbr_info->kernel_texel_width, float); + + if (stbr__use_height_upsampling(stbr_info)) + { + stbr_info->horizontal_buffer = NULL; + stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), float); + stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, output_w * channels * sizeof(float) * stbr_info->kernel_texel_width, float); + } + else + { + stbr_info->horizontal_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), float); + stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->horizontal_buffer, output_w * channels * sizeof(float), float); + stbr_info->encode_buffer = NULL; + } #undef STBR__NEXT_MEMPTR @@ -876,7 +905,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr__calculate_horizontal_filters(stbr_info); - if (stbr_info->output_h >= stbr_info->input_h) + if (stbr__use_height_upsampling(stbr_info)) stbr__buffer_loop_upsample(stbr_info); else stbr__buffer_loop_downsample(stbr_info); @@ -906,6 +935,16 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int input_st int ring_buffer_size = output_w * channels * sizeof(float) * stbr__get_filter_texel_width(filter); int encode_buffer_size = channels * sizeof(float); + if (stbr__use_height_upsampling_noinfo(output_h, input_h)) + // The horizontal buffer is for when we're downsampling the height and we + // can't output the result of sampling the decode buffer directly into the + // ring buffers. + horizontal_buffer_size = 0; + else + // The encode buffer is to retain precision in the height upsampling method + // and isn't used when height downsampling. + encode_buffer_size = 0; + return info_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size; } From 81c1ddf110efb97f58b58193caa235af164ffe15 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Fri, 25 Jul 2014 00:00:40 -0700 Subject: [PATCH 019/168] Keeping a list of suggestions so I don't forget them. --- docs/stb_resample_ideas.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/stb_resample_ideas.txt b/docs/stb_resample_ideas.txt index a9842d3..c67621b 100644 --- a/docs/stb_resample_ideas.txt +++ b/docs/stb_resample_ideas.txt @@ -190,3 +190,10 @@ Cubic sampling function for seperable cubic: f(x) = 0 otherwise "a" is configurable, try -1/2 (from http://pixinsight.com/forum/index.php?topic=556.0 ) + + +Wish list: + s0, t0, s1, t1 vs scale_x, scale_y, offset_x, offset_y - What's the best interface? + Separate wrap modes and filter modes per axis + + From 62ff271c7a66a64ee41e28ee7c46a65f416b40a1 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Fri, 25 Jul 2014 00:08:23 -0700 Subject: [PATCH 020/168] I put it in the to do list and now I can close my browser tab. --- docs/stb_resample_ideas.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/stb_resample_ideas.txt b/docs/stb_resample_ideas.txt index c67621b..dbc4442 100644 --- a/docs/stb_resample_ideas.txt +++ b/docs/stb_resample_ideas.txt @@ -195,5 +195,6 @@ Cubic sampling function for seperable cubic: Wish list: s0, t0, s1, t1 vs scale_x, scale_y, offset_x, offset_y - What's the best interface? Separate wrap modes and filter modes per axis + Alpha test coverage respecting resize (FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp) From 7abd4ccf3445fab20df58f6ca79f22d9f4e49cc9 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 26 Jul 2014 11:51:02 -0700 Subject: [PATCH 021/168] Support for filters with larger support. Initial support for edge behavior. --- docs/stb_resample_ideas.txt | 1 + stb_resample.h | 263 +++++++++++++++++++++++++------------------- tests/resample_test.cpp | 4 +- 3 files changed, 154 insertions(+), 114 deletions(-) diff --git a/docs/stb_resample_ideas.txt b/docs/stb_resample_ideas.txt index dbc4442..96b3fac 100644 --- a/docs/stb_resample_ideas.txt +++ b/docs/stb_resample_ideas.txt @@ -196,5 +196,6 @@ Wish list: s0, t0, s1, t1 vs scale_x, scale_y, offset_x, offset_y - What's the best interface? Separate wrap modes and filter modes per axis Alpha test coverage respecting resize (FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp) + Edge: Clamp, ignore, wrap, reflect diff --git a/stb_resample.h b/stb_resample.h index e626e9c..b204365 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -28,6 +28,7 @@ Initial implementation by Jorge L Rodriguez typedef enum { STBR_FILTER_NEAREST = 1, + STBR_FILTER_LINEAR = 2, } stbr_filter; typedef enum @@ -71,7 +72,7 @@ extern "C" { STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, //int channels, int alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, - int channels, stbr_type type, stbr_filter filter, + int channels, stbr_type type, stbr_filter filter, stbr_edge edge, void* tempmem, stbr_size_t tempmem_size_in_bytes); @@ -175,22 +176,20 @@ typedef struct int channels; stbr_type type; stbr_filter filter; + stbr_edge edge; - int total_coefficients; - int kernel_texel_width; - - int total_horizontal_contributors; stbr__contributors* horizontal_contributors; float* horizontal_coefficients; stbr__contributors vertical_contributors; float* vertical_coefficients; + int decode_buffer_texels; float* decode_buffer; float* horizontal_buffer; - int ring_buffer_length; // The length of an individual entry in the ring buffer. The total number of ring buffers is kernel_texel_width + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbr__get_filter_texel_width(filter) int ring_buffer_first_scanline; int ring_buffer_last_scanline; int ring_buffer_begin_index; @@ -219,9 +218,20 @@ static float stbr__filter_nearest(float x) return 0; } +static float stbr__filter_linear(float x) +{ + x = (float)fabs(x); + + if (x <= 1.0f) + return 1 - x; + else + return 0; +} + static stbr__filter_info stbr__filter_info_table[] = { { NULL, 0.0f }, { stbr__filter_nearest, 0.5f }, + { stbr__filter_linear, 1.0f }, }; stbr_inline static int stbr__use_width_upsampling_noinfo(int output_w, int input_w) @@ -254,20 +264,51 @@ stbr_inline static int stbr__get_filter_texel_width(stbr_filter filter) return (int)ceil(stbr__filter_info_table[filter].support * 2); } +// This is how much to expand buffers to account for filters seeking outside +// the image boundaries. +stbr_inline static int stbr__get_filter_texel_margin(stbr_filter filter) +{ + return stbr__get_filter_texel_width(filter) / 2; +} + +stbr_inline static int stbr__get_horizontal_contributors(stbr_filter filter, int input_w, int output_w) +{ + if (stbr__use_width_upsampling_noinfo(output_w, input_w)) + return output_w; + else + return (input_w + stbr__get_filter_texel_margin(filter) * 2); +} + stbr_inline static int stbr__get_total_coefficients(stbr_filter filter, int input_w, int output_w) { - return stbr__max(output_w, input_w) * stbr__get_filter_texel_width(filter); + return stbr__get_horizontal_contributors(filter, input_w, output_w) * stbr__get_filter_texel_width(filter); } stbr_inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_info, int n) { - STBR_DEBUG_ASSERT(n >= 0 && n < stbr_info->total_horizontal_contributors); + STBR_DEBUG_ASSERT(n >= 0 && n < stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); return &stbr_info->horizontal_contributors[n]; } stbr_inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, int c) { - return &stbr_info->horizontal_coefficients[stbr_info->kernel_texel_width*n + c]; + return &stbr_info->horizontal_coefficients[stbr__get_filter_texel_width(stbr_info->filter)*n + c]; +} + +stbr_inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) +{ + STBR_UNIMPLEMENTED(edge != STBR_EDGE_CLAMP); + + switch (edge) + { + default: + case STBR_EDGE_CLAMP: + if (n < 0) + return 0; + if (n >= max) + return max - 1; + return n; + } } // What input texels contribute to this output texel? @@ -296,20 +337,19 @@ static void stbr__calculate_sample_range_downsample(int n, float in_pixels_radiu float out_texel_influence_upperbound = in_texel_influence_upperbound * scale_ratio; *out_center_of_in = in_texel_center * scale_ratio; - *out_last_texel = (int)(floor(out_texel_influence_lowerbound + 0.5)); - *out_first_texel = (int)(floor(out_texel_influence_upperbound - 0.5)); + *out_first_texel = (int)(floor(out_texel_influence_lowerbound + 0.5)); + *out_last_texel = (int)(floor(out_texel_influence_upperbound - 0.5)); } -static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_first_texel, int in_last_texel, float in_center_of_out, int n, stbr__contributors* contributor, float* coefficient_group) +static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_first_texel, int in_last_texel, float in_center_of_out, stbr__contributors* contributor, float* coefficient_group) { int i; float total_filter = 0; float filter_scale; stbr_filter filter = stbr_info->filter; - STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr_info->kernel_texel_width); - STBR_DEBUG_ASSERT(in_first_texel >= 0); - STBR_DEBUG_ASSERT(in_last_texel < stbr_info->total_horizontal_contributors); + STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr__get_filter_texel_width(filter)); + STBR_DEBUG_ASSERT(in_last_texel < stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); contributor->n0 = in_first_texel; contributor->n1 = in_last_texel; @@ -330,14 +370,13 @@ static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_ coefficient_group[i] *= filter_scale; } -static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float scale_ratio, int out_first_texel, int out_last_texel, float out_center_of_in, int n, stbr__contributors* contributor, float* coefficient_group) +static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float scale_ratio, int out_first_texel, int out_last_texel, float out_center_of_in, stbr__contributors* contributor, float* coefficient_group) { int i; stbr_filter filter = stbr_info->filter; - STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr_info->kernel_texel_width); - STBR_DEBUG_ASSERT(out_first_texel >= 0); - STBR_DEBUG_ASSERT(out_last_texel < stbr_info->total_horizontal_contributors); + STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr__get_filter_texel_width(filter)); + STBR_DEBUG_ASSERT(out_last_texel < stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); contributor->n0 = out_first_texel; contributor->n1 = out_last_texel; @@ -356,7 +395,7 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) int n; float scale_ratio = (float)stbr_info->output_w / stbr_info->input_w; - int total_contributors = stbr_info->total_horizontal_contributors; + int total_contributors = stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); if (stbr__use_width_upsampling(stbr_info)) { @@ -370,7 +409,7 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) stbr__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, &in_first_texel, &in_last_texel, &in_center_of_out); - stbr__calculate_coefficients_upsample(stbr_info, in_first_texel, in_last_texel, in_center_of_out, n, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); + stbr__calculate_coefficients_upsample(stbr_info, in_first_texel, in_last_texel, in_center_of_out, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); } } else @@ -382,20 +421,20 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) { float out_center_of_in; // Center of the current out texel in the in texel space int out_first_texel, out_last_texel; + int n_adjusted = n - stbr__get_filter_texel_margin(stbr_info->filter); - stbr__calculate_sample_range_downsample(n, in_pixels_radius, scale_ratio, &out_first_texel, &out_last_texel, &out_center_of_in); + stbr__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, &out_first_texel, &out_last_texel, &out_center_of_in); - stbr__calculate_coefficients_downsample(stbr_info, scale_ratio, out_first_texel, out_last_texel, out_center_of_in, n, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); + stbr__calculate_coefficients_downsample(stbr_info, scale_ratio, out_first_texel, out_last_texel, out_center_of_in, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); } } } -static float* stbr__get_decode_buffer_index(stbr__info* stbr_info, int x, int c) +static float* stbr__get_decode_buffer(stbr__info* stbr_info) { - STBR_DEBUG_ASSERT(x >= 0 && x < stbr_info->input_w); - STBR_DEBUG_ASSERT(c >= 0 && c < stbr_info->channels); - - return &stbr_info->decode_buffer[x * stbr_info->channels + c]; + // The 0 index of the decode buffer starts after the margin. This makes + // it okay to use negative indexes on the decode buffer. + return &stbr_info->decode_buffer[stbr__get_filter_texel_margin(stbr_info->filter) * stbr_info->channels]; } static void stbr__decode_scanline(stbr__info* stbr_info, int n) @@ -405,21 +444,20 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) int input_w = stbr_info->input_w; int input_stride_bytes = stbr_info->input_stride_bytes; const void* input_data = stbr_info->input_data; - float* decode_buffer = stbr_info->decode_buffer; - int in_buffer_row_index = n * input_stride_bytes; + float* decode_buffer = stbr__get_decode_buffer(stbr_info); + stbr_edge edge = stbr_info->edge; + int in_buffer_row_index = stbr__edge_wrap(edge, n, stbr_info->input_h) * input_stride_bytes; + int max_x = input_w + stbr__get_filter_texel_margin(stbr_info->filter); STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); - for (x = 0; x < input_w; x++) + for (x = -stbr__get_filter_texel_margin(stbr_info->filter); x < max_x; x++) { - int texel_index = x * channels; + int decode_texel_index = x * channels; + int input_texel_index = in_buffer_row_index + stbr__edge_wrap(edge, x, input_w) * channels; for (c = 0; c < channels; c++) - { - int channel_index = x * channels + c; - int in_buffer_index = in_buffer_row_index + channel_index; - decode_buffer[channel_index] = ((float)((const unsigned char*)input_data)[in_buffer_index]) / 255; - } + decode_buffer[decode_texel_index + c] = ((float)((const unsigned char*)input_data)[input_texel_index + c]) / 255; } } @@ -440,12 +478,12 @@ static float* stbr__add_empty_ring_buffer_entry(stbr__info* stbr_info, int n) } else { - ring_buffer_index = (stbr_info->ring_buffer_begin_index + (stbr_info->ring_buffer_last_scanline - stbr_info->ring_buffer_first_scanline) + 1) % stbr_info->kernel_texel_width; + ring_buffer_index = (stbr_info->ring_buffer_begin_index + (stbr_info->ring_buffer_last_scanline - stbr_info->ring_buffer_first_scanline) + 1) % stbr__get_filter_texel_width(stbr_info->filter); STBR_DEBUG_ASSERT(ring_buffer_index != stbr_info->ring_buffer_begin_index); } - ring_buffer = stbr__get_ring_buffer_entry(stbr_info->ring_buffer, ring_buffer_index, stbr_info->ring_buffer_length); - memset(ring_buffer, 0, stbr_info->ring_buffer_length); + ring_buffer = stbr__get_ring_buffer_entry(stbr_info->ring_buffer, ring_buffer_index, stbr_info->ring_buffer_length_bytes / sizeof(float)); + memset(ring_buffer, 0, stbr_info->ring_buffer_length_bytes); stbr_info->ring_buffer_last_scanline = n; @@ -456,9 +494,9 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo { int x, k, c; int output_w = stbr_info->output_w; - int kernel_texel_width = stbr_info->kernel_texel_width; + int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter); int channels = stbr_info->channels; - float* decode_buffer = stbr_info->decode_buffer; + float* decode_buffer = stbr__get_decode_buffer(stbr_info); stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; float* horizontal_coefficients = stbr_info->horizontal_coefficients; @@ -472,6 +510,10 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo int coefficient_counter = 0; STBR_DEBUG_ASSERT(n1 >= n0); + STBR_DEBUG_ASSERT(n0 >= -stbr__get_filter_texel_margin(stbr_info->filter)); + STBR_DEBUG_ASSERT(n1 >= -stbr__get_filter_texel_margin(stbr_info->filter)); + STBR_DEBUG_ASSERT(n0 < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter)); + STBR_DEBUG_ASSERT(n1 < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter)); for (k = n0; k <= n1; k++) { @@ -479,9 +521,6 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo int in_texel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_index]; - if (!coefficient) - continue; - for (c = 0; c < channels; c++) { output_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; @@ -496,34 +535,36 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, f { int x, k, c; int input_w = stbr_info->input_w; - int kernel_texel_width = stbr_info->kernel_texel_width; + int output_w = stbr_info->output_w; + int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter); int channels = stbr_info->channels; - float* decode_buffer = stbr_info->decode_buffer; + float* decode_buffer = stbr__get_decode_buffer(stbr_info); stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; float* horizontal_coefficients = stbr_info->horizontal_coefficients; + int filter_texel_margin = stbr__get_filter_texel_margin(stbr_info->filter); + int max_x = input_w + filter_texel_margin * 2; STBR_DEBUG_ASSERT(!stbr__use_width_upsampling(stbr_info)); - for (x = 0; x < input_w; x++) + for (x = 0; x < max_x; x++) { int n0 = horizontal_contributors[x].n0; int n1 = horizontal_contributors[x].n1; - int in_texel_index = x * channels; - int coefficient_group_index = x * kernel_texel_width; - int coefficient_counter = 0; + int in_x = x - filter_texel_margin; + int in_texel_index = in_x * channels; + int max_n = stbr__min(n1, output_w-1); + int coefficient_group = x*kernel_texel_width; STBR_DEBUG_ASSERT(n1 >= n0); - for (k = n0; k <= n1; k++) + // Using min and max to avoid writing into invalid texels. + for (k = stbr__max(n0, 0); k <= max_n; k++) { - int coefficient_index = coefficient_group_index + (coefficient_counter++); + int coefficient_index = (k - n0) + coefficient_group; int out_texel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_index]; - if (!coefficient) - continue; - for (c = 0; c < channels; c++) { output_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; @@ -536,12 +577,6 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, f static void stbr__decode_and_resample_upsample(stbr__info* stbr_info, int n) { - if (n >= stbr_info->input_h) - { - STBR_UNIMPLEMENTED("ring buffer overran source height"); - return; - } - // Decode the nth scanline from the source image into the decode buffer. stbr__decode_scanline(stbr_info, n); @@ -556,12 +591,6 @@ static void stbr__decode_and_resample_upsample(stbr__info* stbr_info, int n) static void stbr__decode_and_resample_downsample(stbr__info* stbr_info, int n) { - if (n >= stbr_info->input_h) - { - STBR_UNIMPLEMENTED("ring buffer overran source height"); - return; - } - // Decode the nth scanline from the source image into the decode buffer. stbr__decode_scanline(stbr_info, n); @@ -590,7 +619,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; float* vertical_coefficients = stbr_info->vertical_coefficients; int channels = stbr_info->channels; - int kernel_texel_width = stbr_info->kernel_texel_width; + int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter); void* output_data = stbr_info->output_data; float* encode_buffer = stbr_info->encode_buffer; @@ -598,11 +627,11 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int ring_buffer_begin_index = stbr_info->ring_buffer_begin_index; int ring_buffer_first_scanline = stbr_info->ring_buffer_first_scanline; int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; - int ring_buffer_length = stbr_info->ring_buffer_length; + int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); - stbr__calculate_coefficients_upsample(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, n, vertical_contributors, vertical_coefficients); + stbr__calculate_coefficients_upsample(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); int n0 = vertical_contributors->n0; int n1 = vertical_contributors->n1; @@ -629,9 +658,6 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_texel_width, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_index]; - if (!coefficient) - continue; - for (c = 0; c < channels; c++) encode_buffer[c] += ring_buffer_entry[in_texel_index + c] * coefficient; } @@ -645,10 +671,11 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int { int x, k, c; int output_w = stbr_info->output_w; + int output_h = stbr_info->output_h; stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; float* vertical_coefficients = stbr_info->vertical_coefficients; int channels = stbr_info->channels; - int kernel_texel_width = stbr_info->kernel_texel_width; + int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter); void* output_data = stbr_info->output_data; float* horizontal_buffer = stbr_info->horizontal_buffer; @@ -656,9 +683,9 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int int ring_buffer_begin_index = stbr_info->ring_buffer_begin_index; int ring_buffer_first_scanline = stbr_info->ring_buffer_first_scanline; int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; - int ring_buffer_length = stbr_info->ring_buffer_length; + int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); - stbr__calculate_coefficients_downsample(stbr_info, (float)stbr_info->output_h / stbr_info->input_h, in_first_scanline, in_last_scanline, in_center_of_out, n, vertical_contributors, vertical_coefficients); + stbr__calculate_coefficients_downsample(stbr_info, (float)stbr_info->output_h / stbr_info->input_h, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); int n0 = vertical_contributors->n0; int n1 = vertical_contributors->n1; @@ -670,19 +697,17 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int for (x = 0; x < output_w; x++) { int in_texel_index = x * channels; - int coefficient_counter = 0; + int max_n = stbr__min(n1, output_h-1); STBR_DEBUG_ASSERT(n1 >= n0); - for (k = n0; k <= n1; k++) + // Using min and max to avoid writing into ring buffers that will be thrown out. + for (k = stbr__max(n0, 0); k <= max_n; k++) { - int coefficient_index = coefficient_counter++; + int coefficient_index = k - n0; float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_texel_width, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_index]; - if (!coefficient) - continue; - for (c = 0; c < channels; c++) { int index = in_texel_index + c; @@ -709,9 +734,9 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) stbr__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, &in_first_scanline, &in_last_scanline, &in_center_of_out); - STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr_info->kernel_texel_width); - STBR_DEBUG_ASSERT(in_first_scanline >= 0); - STBR_DEBUG_ASSERT(in_last_scanline < stbr_info->input_w); + STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr__get_filter_texel_width(stbr_info->filter)); + STBR_DEBUG_ASSERT(in_first_scanline >= -stbr__get_filter_texel_margin(stbr_info->filter)); + STBR_DEBUG_ASSERT(in_last_scanline < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter)); if (stbr_info->ring_buffer_begin_index >= 0) { @@ -728,7 +753,10 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) break; } else + { stbr_info->ring_buffer_first_scanline++; + stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_texel_width(stbr_info->filter); + } } } @@ -736,7 +764,7 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) if (stbr_info->ring_buffer_begin_index < 0) stbr__decode_and_resample_upsample(stbr_info, in_first_scanline); - while (in_last_scanline < stbr_info->ring_buffer_last_scanline) + while (in_last_scanline > stbr_info->ring_buffer_last_scanline) stbr__decode_and_resample_upsample(stbr_info, stbr_info->ring_buffer_last_scanline + 1); // Now all buffers should be ready to write a row of vertical sampling. @@ -752,7 +780,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s void* output_data = stbr_info->output_data; float* ring_buffer = stbr_info->ring_buffer; - int ring_buffer_length = stbr_info->ring_buffer_length; + int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); if (stbr_info->ring_buffer_begin_index >= 0) { @@ -765,13 +793,16 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); - for (x = 0; x < output_w; x++) + if (stbr_info->ring_buffer_first_scanline >= 0 && stbr_info->ring_buffer_first_scanline < stbr_info->output_h) { - int texel_index = x * channels; - int ring_texel_index = texel_index; - int output_texel_index = output_row + texel_index; - for (c = 0; c < channels; c++) - ((unsigned char*)output_data)[output_texel_index + c] = (unsigned char)(ring_buffer_entry[ring_texel_index + c] * 255); + for (x = 0; x < output_w; x++) + { + int texel_index = x * channels; + int ring_texel_index = texel_index; + int output_texel_index = output_row + texel_index; + for (c = 0; c < channels; c++) + ((unsigned char*)output_data)[output_texel_index + c] = (unsigned char)(ring_buffer_entry[ring_texel_index + c] * 255); + } } if (stbr_info->ring_buffer_first_scanline == stbr_info->ring_buffer_last_scanline) @@ -784,7 +815,10 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s break; } else + { stbr_info->ring_buffer_first_scanline++; + stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_texel_width(stbr_info->filter); + } } } } @@ -804,9 +838,9 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) stbr__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, &out_first_scanline, &out_last_scanline, &out_center_of_in); - STBR_DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbr_info->kernel_texel_width); - STBR_DEBUG_ASSERT(out_first_scanline >= 0); - STBR_DEBUG_ASSERT(out_last_scanline < stbr_info->output_h); + STBR_DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbr__get_filter_texel_width(stbr_info->filter)); + STBR_DEBUG_ASSERT(out_first_scanline >= -stbr__get_filter_texel_margin(stbr_info->filter)); + STBR_DEBUG_ASSERT(out_last_scanline < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter)); stbr__empty_ring_buffer(stbr_info, out_first_scanline); @@ -814,9 +848,9 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) // Load in new ones. if (stbr_info->ring_buffer_begin_index < 0) - stbr__add_empty_ring_buffer_entry(stbr_info, out_last_scanline); + stbr__add_empty_ring_buffer_entry(stbr_info, out_first_scanline); - while (out_last_scanline < stbr_info->ring_buffer_last_scanline) + while (out_last_scanline > stbr_info->ring_buffer_last_scanline) stbr__add_empty_ring_buffer_entry(stbr_info, stbr_info->ring_buffer_last_scanline + 1); // Now the horizontal buffer is ready to write to all ring buffer rows. @@ -828,7 +862,7 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, - int channels, stbr_type type, stbr_filter filter, + int channels, stbr_type type, stbr_filter filter, stbr_edge edge, void* tempmem, stbr_size_t tempmem_size_in_bytes) { int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w; @@ -872,30 +906,33 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->channels = channels; stbr_info->type = type; stbr_info->filter = filter; + stbr_info->edge = edge; - stbr_info->total_coefficients = stbr__get_total_coefficients(filter, input_w, output_w); - stbr_info->kernel_texel_width = stbr__get_filter_texel_width(filter); - stbr_info->ring_buffer_length = output_w * channels * sizeof(float); - stbr_info->total_horizontal_contributors = stbr__max(input_w, output_w); + stbr_info->ring_buffer_length_bytes = output_w * channels * sizeof(float); + stbr_info->decode_buffer_texels = input_w + stbr__get_filter_texel_margin(filter) * 2; #define STBR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) stbr_info->horizontal_contributors = STBR__NEXT_MEMPTR(stbr_info, sizeof(stbr__info), stbr__contributors); - stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, stbr_info->total_horizontal_contributors * sizeof(stbr__contributors), float); - stbr_info->vertical_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr_info->total_coefficients * sizeof(float), float); - stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr_info->kernel_texel_width * sizeof(float), float); + stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, stbr__get_horizontal_contributors(filter, input_w, output_w) * sizeof(stbr__contributors), float); + stbr_info->vertical_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr__get_total_coefficients(filter, input_w, output_w) * sizeof(float), float); + stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr__get_filter_texel_width(filter) * sizeof(float), float); if (stbr__use_height_upsampling(stbr_info)) { stbr_info->horizontal_buffer = NULL; - stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), float); - stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, output_w * channels * sizeof(float) * stbr_info->kernel_texel_width, float); + stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, stbr_info->decode_buffer_texels * channels * sizeof(float), float); + stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width(filter), float); + + STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->encode_buffer, stbr_info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } else { - stbr_info->horizontal_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), float); + stbr_info->horizontal_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, stbr_info->decode_buffer_texels * channels * sizeof(float), float); stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->horizontal_buffer, output_w * channels * sizeof(float), float); stbr_info->encode_buffer = NULL; + + STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width(filter), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } #undef STBR__NEXT_MEMPTR @@ -926,11 +963,13 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int input_st STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); + int texel_margin = stbr__get_filter_texel_margin(filter); + int info_size = sizeof(stbr__info); - int contributors_size = stbr__max(output_w, input_w) * sizeof(stbr__contributors); + int contributors_size = stbr__get_horizontal_contributors(filter, input_w, output_w) * sizeof(stbr__contributors); int horizontal_coefficients_size = stbr__get_total_coefficients(filter, input_w, output_w) * sizeof(float); int vertical_coefficients_size = stbr__get_filter_texel_width(filter) * sizeof(float); - int decode_buffer_size = input_w * channels * sizeof(float); + int decode_buffer_size = (input_w + texel_margin*2) * channels * sizeof(float); int horizontal_buffer_size = output_w * channels * sizeof(float); int ring_buffer_size = output_w * channels * sizeof(float) * stbr__get_filter_texel_width(filter); int encode_buffer_size = channels * sizeof(float); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index f47068e..f2568db 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -48,7 +48,7 @@ int main(int argc, char** argv) int in_w = 512; int in_h = 512; - size_t memory_required = stbr_calculate_memory(in_w, in_h, w*n, out_w, out_h, out_stride, n, STBR_FILTER_NEAREST); + size_t memory_required = stbr_calculate_memory(in_w, in_h, w*n, out_w, out_h, out_stride, n, STBR_FILTER_LINEAR); void* extra_memory = malloc(memory_required); // Cut out the outside 64 pixels all around to test the stride. @@ -56,7 +56,7 @@ int main(int argc, char** argv) STBR_ASSERT(in_w + border <= w); STBR_ASSERT(in_h + border <= h); - stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_NEAREST, extra_memory, memory_required); + stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_LINEAR, STBR_EDGE_CLAMP, extra_memory, memory_required); free(extra_memory); From a95da9ee1d76702d0e5f43f24339b964937a30e9 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 26 Jul 2014 12:04:39 -0700 Subject: [PATCH 022/168] Unroll the multiply-add loops. At the cost of a function pointer dereference we get a whole lot of conditionals eliminated. Should be a solid win once the debug asserts are gone. --- stb_resample.h | 114 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 90 insertions(+), 24 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index b204365..f706b52 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -490,9 +490,84 @@ static float* stbr__add_empty_ring_buffer_entry(stbr__info* stbr_info, int n) return ring_buffer; } +typedef void(*stbr__output_decode_coefficients)(float* output_buffer, int out_texel_index, float* decode_buffer, int decode_texel_index, int channels, float coefficient); + +static void stbr__output_decode_coefficients_1(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) +{ + STBR_DEBUG_ASSERT(channels == 1); + + output_buffer[out_texel_index] += input_buffer[input_texel_index] * coefficient; + + STBR_DEBUG_ASSERT(output_buffer[out_texel_index] <= 1.0f); +} + +static void stbr__output_decode_coefficients_2(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) +{ + STBR_DEBUG_ASSERT(channels == 2); + + output_buffer[out_texel_index ] += input_buffer[input_texel_index ] * coefficient; + output_buffer[out_texel_index + 1] += input_buffer[input_texel_index + 1] * coefficient; + + STBR_DEBUG_ASSERT(output_buffer[out_texel_index ] <= 1.0f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index+1] <= 1.0f); +} + +static void stbr__output_decode_coefficients_3(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) +{ + STBR_DEBUG_ASSERT(channels == 3); + + output_buffer[out_texel_index ] += input_buffer[input_texel_index ] * coefficient; + output_buffer[out_texel_index + 1] += input_buffer[input_texel_index + 1] * coefficient; + output_buffer[out_texel_index + 2] += input_buffer[input_texel_index + 2] * coefficient; + + STBR_DEBUG_ASSERT(output_buffer[out_texel_index ] <= 1.0f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 1] <= 1.0f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 2] <= 1.0f); +} + +static void stbr__output_decode_coefficients_4(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) +{ + STBR_DEBUG_ASSERT(channels == 4); + + output_buffer[out_texel_index ] += input_buffer[input_texel_index ] * coefficient; + output_buffer[out_texel_index + 1] += input_buffer[input_texel_index + 1] * coefficient; + output_buffer[out_texel_index + 2] += input_buffer[input_texel_index + 2] * coefficient; + output_buffer[out_texel_index + 3] += input_buffer[input_texel_index + 3] * coefficient; + + STBR_DEBUG_ASSERT(output_buffer[out_texel_index ] <= 1.0f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 1] <= 1.0f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 2] <= 1.0f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 3] <= 1.0f); +} + +static void stbr__output_decode_coefficients_n(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) +{ + int c; + for (c = 0; c < channels; c++) + { + output_buffer[out_texel_index + c] += input_buffer[input_texel_index + c] * coefficient; + + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + c] <= 1.0f); + } +} + +static stbr__output_decode_coefficients stbr__get_output_decode_coefficients_function(int channels) +{ + if (channels == 1) + return &stbr__output_decode_coefficients_1; + else if (channels == 2) + return &stbr__output_decode_coefficients_2; + else if (channels == 3) + return &stbr__output_decode_coefficients_3; + else if (channels == 4) + return &stbr__output_decode_coefficients_4; + + return &stbr__output_decode_coefficients_n; +} + static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, float* output_buffer) { - int x, k, c; + int x, k; int output_w = stbr_info->output_w; int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter); int channels = stbr_info->channels; @@ -500,6 +575,8 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; float* horizontal_coefficients = stbr_info->horizontal_coefficients; + stbr__output_decode_coefficients output_decode_coefficients_fn = stbr__get_output_decode_coefficients_function(channels); + for (x = 0; x < output_w; x++) { int n0 = horizontal_contributors[x].n0; @@ -521,19 +598,14 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo int in_texel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_index]; - for (c = 0; c < channels; c++) - { - output_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; - - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + c] <= 1.0f); - } + output_decode_coefficients_fn(output_buffer, out_texel_index, decode_buffer, in_texel_index, channels, coefficient); } } } static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, float* output_buffer) { - int x, k, c; + int x, k; int input_w = stbr_info->input_w; int output_w = stbr_info->output_w; int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter); @@ -544,6 +616,8 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, f int filter_texel_margin = stbr__get_filter_texel_margin(stbr_info->filter); int max_x = input_w + filter_texel_margin * 2; + stbr__output_decode_coefficients output_decode_coefficients_fn = stbr__get_output_decode_coefficients_function(channels); + STBR_DEBUG_ASSERT(!stbr__use_width_upsampling(stbr_info)); for (x = 0; x < max_x; x++) @@ -565,12 +639,7 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, f int out_texel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_index]; - for (c = 0; c < channels; c++) - { - output_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; - - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + c] <= 1.0); // This would indicate that the sum of kernels for this texel doesn't add to 1. - } + output_decode_coefficients_fn(output_buffer, out_texel_index, decode_buffer, in_texel_index, channels, coefficient); } } } @@ -638,6 +707,8 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int output_row_index = n * stbr_info->output_stride_bytes; + stbr__output_decode_coefficients output_decode_coefficients_fn = stbr__get_output_decode_coefficients_function(channels); + STBR_DEBUG_ASSERT(stbr__use_height_upsampling(stbr_info)); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); STBR_DEBUG_ASSERT(n1 <= in_last_scanline); @@ -658,8 +729,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_texel_width, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_index]; - for (c = 0; c < channels; c++) - encode_buffer[c] += ring_buffer_entry[in_texel_index + c] * coefficient; + output_decode_coefficients_fn(encode_buffer, 0, ring_buffer_entry, in_texel_index, channels, coefficient); } for (c = 0; c < channels; c++) @@ -669,7 +739,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) { - int x, k, c; + int x, k; int output_w = stbr_info->output_w; int output_h = stbr_info->output_h; stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; @@ -690,6 +760,8 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int int n0 = vertical_contributors->n0; int n1 = vertical_contributors->n1; + stbr__output_decode_coefficients output_decode_coefficients_fn = stbr__get_output_decode_coefficients_function(channels); + STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); STBR_DEBUG_ASSERT(n1 <= in_last_scanline); @@ -708,13 +780,7 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_texel_width, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_index]; - for (c = 0; c < channels; c++) - { - int index = in_texel_index + c; - ring_buffer_entry[index] += horizontal_buffer[index] * coefficient; - - STBR_DEBUG_ASSERT(ring_buffer_entry[index] <= 1.0); // This would indicate that the sum of kernels for this texel doesn't add to 1. - } + output_decode_coefficients_fn(ring_buffer_entry, in_texel_index, horizontal_buffer, in_texel_index, channels, coefficient); } } } From 01fb58d6b447d2714f015e9adc09658152a2c2a7 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 26 Jul 2014 13:07:04 -0700 Subject: [PATCH 023/168] Add a bicubic filter. --- stb_resample.h | 32 +++++++++++++++++++++++++------- tests/resample_test.cpp | 4 ++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index f706b52..a1bbe97 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -27,8 +27,9 @@ Initial implementation by Jorge L Rodriguez typedef enum { - STBR_FILTER_NEAREST = 1, - STBR_FILTER_LINEAR = 2, + STBR_FILTER_NEAREST = 1, + STBR_FILTER_BILINEAR = 2, + STBR_FILTER_BICUBIC = 3, // A cubic b spline } stbr_filter; typedef enum @@ -212,13 +213,15 @@ static stbr_inline int stbr__max(int a, int b) static float stbr__filter_nearest(float x) { - if (fabs(x) <= 0.5) + x = (float)fabs(x); + + if (x <= 0.5) return 1; else return 0; } -static float stbr__filter_linear(float x) +static float stbr__filter_bilinear(float x) { x = (float)fabs(x); @@ -228,10 +231,25 @@ static float stbr__filter_linear(float x) return 0; } +static float stbr__filter_bicubic(float x) +{ + x = (float)fabs(x); + + float xx = x*x; + + if (x < 1.0f) + return 0.5f * (x * xx) - xx + 0.66666666666f; + else if (x < 2.0f) + return -0.16666666f * (x * xx) + xx - 2 * x + 1.3333333333f; + + return (0.0f); +} + static stbr__filter_info stbr__filter_info_table[] = { - { NULL, 0.0f }, - { stbr__filter_nearest, 0.5f }, - { stbr__filter_linear, 1.0f }, + { NULL, 0.0f }, + { stbr__filter_nearest, 0.5f }, + { stbr__filter_bilinear, 1.0f }, + { stbr__filter_bicubic, 2.0f }, }; stbr_inline static int stbr__use_width_upsampling_noinfo(int output_w, int input_w) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index f2568db..0077131 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -48,7 +48,7 @@ int main(int argc, char** argv) int in_w = 512; int in_h = 512; - size_t memory_required = stbr_calculate_memory(in_w, in_h, w*n, out_w, out_h, out_stride, n, STBR_FILTER_LINEAR); + size_t memory_required = stbr_calculate_memory(in_w, in_h, w*n, out_w, out_h, out_stride, n, STBR_FILTER_BICUBIC); void* extra_memory = malloc(memory_required); // Cut out the outside 64 pixels all around to test the stride. @@ -56,7 +56,7 @@ int main(int argc, char** argv) STBR_ASSERT(in_w + border <= w); STBR_ASSERT(in_h + border <= h); - stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_LINEAR, STBR_EDGE_CLAMP, extra_memory, memory_required); + stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_BICUBIC, STBR_EDGE_CLAMP, extra_memory, memory_required); free(extra_memory); From 155c71fb90a0868fed1f6a49863a8d5f5e1d0d88 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 26 Jul 2014 13:12:48 -0700 Subject: [PATCH 024/168] Reorder these loops because I think we get a cache win if we write the entire ring buffer entry at once. --- stb_resample.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index a1bbe97..6179b9c 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -777,26 +777,26 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int int n0 = vertical_contributors->n0; int n1 = vertical_contributors->n1; + int max_n = stbr__min(n1, output_h - 1); stbr__output_decode_coefficients output_decode_coefficients_fn = stbr__get_output_decode_coefficients_function(channels); STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); STBR_DEBUG_ASSERT(n1 <= in_last_scanline); + STBR_DEBUG_ASSERT(n1 >= n0); - for (x = 0; x < output_w; x++) + // Using min and max to avoid writing into ring buffers that will be thrown out. + for (k = stbr__max(n0, 0); k <= max_n; k++) { - int in_texel_index = x * channels; - int max_n = stbr__min(n1, output_h-1); + int coefficient_index = k - n0; - STBR_DEBUG_ASSERT(n1 >= n0); + float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_texel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_index]; - // Using min and max to avoid writing into ring buffers that will be thrown out. - for (k = stbr__max(n0, 0); k <= max_n; k++) + for (x = 0; x < output_w; x++) { - int coefficient_index = k - n0; - float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_texel_width, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_index]; + int in_texel_index = x * channels; output_decode_coefficients_fn(ring_buffer_entry, in_texel_index, horizontal_buffer, in_texel_index, channels, coefficient); } From 12acf87eec18380dfa4d5cc37bf1e56a621746f7 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 26 Jul 2014 13:56:23 -0700 Subject: [PATCH 025/168] When downsampling start the buffer loop at -filter_texel_margin to make sure that all contributors get their taps in at the ring buffer. --- stb_resample.h | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 6179b9c..b56412e 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -516,7 +516,7 @@ static void stbr__output_decode_coefficients_1(float* output_buffer, int out_tex output_buffer[out_texel_index] += input_buffer[input_texel_index] * coefficient; - STBR_DEBUG_ASSERT(output_buffer[out_texel_index] <= 1.0f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index] <= 1.001f); } static void stbr__output_decode_coefficients_2(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) @@ -526,8 +526,8 @@ static void stbr__output_decode_coefficients_2(float* output_buffer, int out_tex output_buffer[out_texel_index ] += input_buffer[input_texel_index ] * coefficient; output_buffer[out_texel_index + 1] += input_buffer[input_texel_index + 1] * coefficient; - STBR_DEBUG_ASSERT(output_buffer[out_texel_index ] <= 1.0f); - STBR_DEBUG_ASSERT(output_buffer[out_texel_index+1] <= 1.0f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index ] <= 1.001f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 1] <= 1.001f); } static void stbr__output_decode_coefficients_3(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) @@ -538,9 +538,9 @@ static void stbr__output_decode_coefficients_3(float* output_buffer, int out_tex output_buffer[out_texel_index + 1] += input_buffer[input_texel_index + 1] * coefficient; output_buffer[out_texel_index + 2] += input_buffer[input_texel_index + 2] * coefficient; - STBR_DEBUG_ASSERT(output_buffer[out_texel_index ] <= 1.0f); - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 1] <= 1.0f); - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 2] <= 1.0f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index ] <= 1.001f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 1] <= 1.001f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 2] <= 1.001f); } static void stbr__output_decode_coefficients_4(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) @@ -552,10 +552,10 @@ static void stbr__output_decode_coefficients_4(float* output_buffer, int out_tex output_buffer[out_texel_index + 2] += input_buffer[input_texel_index + 2] * coefficient; output_buffer[out_texel_index + 3] += input_buffer[input_texel_index + 3] * coefficient; - STBR_DEBUG_ASSERT(output_buffer[out_texel_index ] <= 1.0f); - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 1] <= 1.0f); - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 2] <= 1.0f); - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 3] <= 1.0f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index ] <= 1.001f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 1] <= 1.001f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 2] <= 1.001f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 3] <= 1.001f); } static void stbr__output_decode_coefficients_n(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) @@ -565,7 +565,7 @@ static void stbr__output_decode_coefficients_n(float* output_buffer, int out_tex { output_buffer[out_texel_index + c] += input_buffer[input_texel_index + c] * coefficient; - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + c] <= 1.0f); + STBR_DEBUG_ASSERT(output_buffer[out_texel_index + c] <= 1.001f); } } @@ -869,16 +869,16 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s if (stbr_info->ring_buffer_begin_index >= 0) { // Get rid of whatever we don't need anymore. - while (first_necessary_scanline > stbr_info->ring_buffer_first_scanline || first_necessary_scanline < 0) + while (first_necessary_scanline > stbr_info->ring_buffer_first_scanline) { - int x, c; - int output_row = stbr_info->ring_buffer_first_scanline * output_stride_bytes; - float* ring_buffer_entry = stbr__get_ring_buffer_entry(ring_buffer, stbr_info->ring_buffer_begin_index, ring_buffer_length); - STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); if (stbr_info->ring_buffer_first_scanline >= 0 && stbr_info->ring_buffer_first_scanline < stbr_info->output_h) { + int x, c; + int output_row = stbr_info->ring_buffer_first_scanline * output_stride_bytes; + float* ring_buffer_entry = stbr__get_ring_buffer_entry(ring_buffer, stbr_info->ring_buffer_begin_index, ring_buffer_length); + for (x = 0; x < output_w; x++) { int texel_index = x * channels; @@ -912,10 +912,11 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) int y; float scale_ratio = (float)stbr_info->output_h / stbr_info->input_h; float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; + int max_y = stbr_info->input_h + stbr__get_filter_texel_margin(stbr_info->filter); STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); - for (y = 0; y < stbr_info->input_h; y++) + for (y = -stbr__get_filter_texel_margin(stbr_info->filter); y < max_y; y++) { float out_center_of_in; // Center of the current out scanline in the in scanline space int out_first_scanline, out_last_scanline; @@ -923,8 +924,8 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) stbr__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, &out_first_scanline, &out_last_scanline, &out_center_of_in); STBR_DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbr__get_filter_texel_width(stbr_info->filter)); - STBR_DEBUG_ASSERT(out_first_scanline >= -stbr__get_filter_texel_margin(stbr_info->filter)); - STBR_DEBUG_ASSERT(out_last_scanline < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter)); + STBR_DEBUG_ASSERT(out_first_scanline >= -2*stbr__get_filter_texel_margin(stbr_info->filter)); + STBR_DEBUG_ASSERT(out_last_scanline < stbr_info->input_w + 2*stbr__get_filter_texel_margin(stbr_info->filter)); stbr__empty_ring_buffer(stbr_info, out_first_scanline); @@ -941,7 +942,7 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) stbr__resample_vertical_downsample(stbr_info, y, out_first_scanline, out_last_scanline, out_center_of_in); } - stbr__empty_ring_buffer(stbr_info, -1); + stbr__empty_ring_buffer(stbr_info, stbr_info->output_h); } STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, From 69af963c4201ca477c0d48fca13806ccb06f34de Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 26 Jul 2014 14:52:28 -0700 Subject: [PATCH 026/168] Add catmull rom filter. Also, move the debug asserts for values <= 1 to the very end of the process. This will make these bugs a lot harder to find, but because some filter kernels have negative values it's possible for the buffers to have values > 1 up until the point where it's converted back to an int. --- stb_resample.h | 50 ++++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index b56412e..1a695b2 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -30,6 +30,7 @@ typedef enum STBR_FILTER_NEAREST = 1, STBR_FILTER_BILINEAR = 2, STBR_FILTER_BICUBIC = 3, // A cubic b spline + STBR_FILTER_CATMULLROM = 4, } stbr_filter; typedef enum @@ -245,11 +246,26 @@ static float stbr__filter_bicubic(float x) return (0.0f); } +static float stbr__filter_catmullrom(float x) +{ + x = (float)fabs(x); + + float xx = x*x; + + if (x < 1.0f) + return 1.5f * (x * xx) - 2.5f * xx + 1; + else if (x < 2.0f) + return -0.5f * (x * xx) + 2.5f * xx - 4 * x + 2; + + return (0.0f); +} + static stbr__filter_info stbr__filter_info_table[] = { - { NULL, 0.0f }, - { stbr__filter_nearest, 0.5f }, - { stbr__filter_bilinear, 1.0f }, - { stbr__filter_bicubic, 2.0f }, + { NULL, 0.0f }, + { stbr__filter_nearest, 0.5f }, + { stbr__filter_bilinear, 1.0f }, + { stbr__filter_bicubic, 2.0f }, + { stbr__filter_catmullrom, 2.0f }, }; stbr_inline static int stbr__use_width_upsampling_noinfo(int output_w, int input_w) @@ -515,8 +531,6 @@ static void stbr__output_decode_coefficients_1(float* output_buffer, int out_tex STBR_DEBUG_ASSERT(channels == 1); output_buffer[out_texel_index] += input_buffer[input_texel_index] * coefficient; - - STBR_DEBUG_ASSERT(output_buffer[out_texel_index] <= 1.001f); } static void stbr__output_decode_coefficients_2(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) @@ -525,9 +539,6 @@ static void stbr__output_decode_coefficients_2(float* output_buffer, int out_tex output_buffer[out_texel_index ] += input_buffer[input_texel_index ] * coefficient; output_buffer[out_texel_index + 1] += input_buffer[input_texel_index + 1] * coefficient; - - STBR_DEBUG_ASSERT(output_buffer[out_texel_index ] <= 1.001f); - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 1] <= 1.001f); } static void stbr__output_decode_coefficients_3(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) @@ -537,10 +548,6 @@ static void stbr__output_decode_coefficients_3(float* output_buffer, int out_tex output_buffer[out_texel_index ] += input_buffer[input_texel_index ] * coefficient; output_buffer[out_texel_index + 1] += input_buffer[input_texel_index + 1] * coefficient; output_buffer[out_texel_index + 2] += input_buffer[input_texel_index + 2] * coefficient; - - STBR_DEBUG_ASSERT(output_buffer[out_texel_index ] <= 1.001f); - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 1] <= 1.001f); - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 2] <= 1.001f); } static void stbr__output_decode_coefficients_4(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) @@ -551,22 +558,13 @@ static void stbr__output_decode_coefficients_4(float* output_buffer, int out_tex output_buffer[out_texel_index + 1] += input_buffer[input_texel_index + 1] * coefficient; output_buffer[out_texel_index + 2] += input_buffer[input_texel_index + 2] * coefficient; output_buffer[out_texel_index + 3] += input_buffer[input_texel_index + 3] * coefficient; - - STBR_DEBUG_ASSERT(output_buffer[out_texel_index ] <= 1.001f); - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 1] <= 1.001f); - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 2] <= 1.001f); - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + 3] <= 1.001f); } static void stbr__output_decode_coefficients_n(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) { int c; for (c = 0; c < channels; c++) - { output_buffer[out_texel_index + c] += input_buffer[input_texel_index + c] * coefficient; - - STBR_DEBUG_ASSERT(output_buffer[out_texel_index + c] <= 1.001f); - } } static stbr__output_decode_coefficients stbr__get_output_decode_coefficients_function(int channels) @@ -751,7 +749,11 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i } for (c = 0; c < channels; c++) + { + STBR_DEBUG_ASSERT(encode_buffer[c] < 1.0f + 1.0f/255); + ((unsigned char*)output_data)[out_texel_index + c] = (unsigned char)(encode_buffer[c] * 255); + } } } @@ -885,7 +887,11 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s int ring_texel_index = texel_index; int output_texel_index = output_row + texel_index; for (c = 0; c < channels; c++) + { + STBR_DEBUG_ASSERT(ring_buffer_entry[ring_texel_index + c] < 1.0f + 1.0f / 255); + ((unsigned char*)output_data)[output_texel_index + c] = (unsigned char)(ring_buffer_entry[ring_texel_index + c] * 255); + } } } From fb2f8df5cce0580b08da784c94420f55a33b0836 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 26 Jul 2014 15:06:31 -0700 Subject: [PATCH 027/168] Add mitchell filter. --- stb_resample.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/stb_resample.h b/stb_resample.h index 1a695b2..b06c4a8 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -31,6 +31,7 @@ typedef enum STBR_FILTER_BILINEAR = 2, STBR_FILTER_BICUBIC = 3, // A cubic b spline STBR_FILTER_CATMULLROM = 4, + STBR_FILTER_MITCHELL = 5, } stbr_filter; typedef enum @@ -260,12 +261,27 @@ static float stbr__filter_catmullrom(float x) return (0.0f); } +static float stbr__filter_mitchell(float x) +{ + x = (float)fabs(x); + + float xx = x*x; + + if (x < 1.0f) + return 1.1666666666666f * (x * xx) - 2 * xx + 0.8888888888f; + else if (x < 2.0f) + return -0.3888888888f * (x * xx) + 2 * xx - 3.333333333f * x + 1.777777777777f; + + return (0.0f); +} + static stbr__filter_info stbr__filter_info_table[] = { { NULL, 0.0f }, { stbr__filter_nearest, 0.5f }, { stbr__filter_bilinear, 1.0f }, { stbr__filter_bicubic, 2.0f }, { stbr__filter_catmullrom, 2.0f }, + { stbr__filter_mitchell, 2.0f }, }; stbr_inline static int stbr__use_width_upsampling_noinfo(int output_w, int input_w) From 6cd81d4dd5032d046b007a9d7124cbb9c98549ff Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 26 Jul 2014 15:36:15 -0700 Subject: [PATCH 028/168] Put the polynomials in horner form to save a multiplication. --- stb_resample.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index b06c4a8..24e723a 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -254,9 +254,9 @@ static float stbr__filter_catmullrom(float x) float xx = x*x; if (x < 1.0f) - return 1.5f * (x * xx) - 2.5f * xx + 1; + return 1.5f * xx * (x - 1.66666666f) + 1; else if (x < 2.0f) - return -0.5f * (x * xx) + 2.5f * xx - 4 * x + 2; + return -0.5f * x * ((xx - 5 * x) + 8) + 2; return (0.0f); } @@ -268,9 +268,9 @@ static float stbr__filter_mitchell(float x) float xx = x*x; if (x < 1.0f) - return 1.1666666666666f * (x * xx) - 2 * xx + 0.8888888888f; + return 1.1666666666666f * xx * (x - 1.714285715f) + 0.8888888888f; else if (x < 2.0f) - return -0.3888888888f * (x * xx) + 2 * xx - 3.333333333f * x + 1.777777777777f; + return -0.3888888888f * x * ((xx - 5.14285714f * x) + 8.571428571f) + 1.777777777777f; return (0.0f); } From 87235674394d5ba57e36dddf977434a57a531f71 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 26 Jul 2014 19:11:02 -0700 Subject: [PATCH 029/168] Add edge reflect mode. --- stb_resample.h | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 24e723a..9f5708e 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -37,6 +37,7 @@ typedef enum typedef enum { STBR_EDGE_CLAMP = 1, + STBR_EDGE_REFLECT = 2, } stbr_edge; typedef enum @@ -347,17 +348,38 @@ stbr_inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, in stbr_inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) { - STBR_UNIMPLEMENTED(edge != STBR_EDGE_CLAMP); - switch (edge) { - default: case STBR_EDGE_CLAMP: if (n < 0) return 0; if (n >= max) return max - 1; return n; + case STBR_EDGE_REFLECT: + { + if (n < 0) + { + if (n < max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n < max2) + return 0; + else + return max2 - n - 1; + } + + return n; + } + default: + STBR_UNIMPLEMENTED("Unimplemented edge type"); + return 0; } } From af1ed58f513a4d5bfbad9cf1e8807ad8999264a0 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 26 Jul 2014 19:30:13 -0700 Subject: [PATCH 030/168] Add wrap, fix reflect so it doesn't wrap. --- stb_resample.h | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 9f5708e..45618a0 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -36,8 +36,9 @@ typedef enum typedef enum { - STBR_EDGE_CLAMP = 1, + STBR_EDGE_CLAMP = 1, STBR_EDGE_REFLECT = 2, + STBR_EDGE_WRAP = 3, } stbr_edge; typedef enum @@ -353,9 +354,12 @@ stbr_inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) case STBR_EDGE_CLAMP: if (n < 0) return 0; + if (n >= max) return max - 1; + return n; + case STBR_EDGE_REFLECT: { if (n < 0) @@ -369,7 +373,7 @@ stbr_inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) if (n >= max) { int max2 = max * 2; - if (n < max2) + if (n >= max2) return 0; else return max2 - n - 1; @@ -377,6 +381,20 @@ stbr_inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) return n; } + + case STBR_EDGE_WRAP: + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; + + if (m != 0) + m = max - m; + + return (m); + } + default: STBR_UNIMPLEMENTED("Unimplemented edge type"); return 0; @@ -926,7 +944,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s int output_texel_index = output_row + texel_index; for (c = 0; c < channels; c++) { - STBR_DEBUG_ASSERT(ring_buffer_entry[ring_texel_index + c] < 1.0f + 1.0f / 255); + //STBR_DEBUG_ASSERT(ring_buffer_entry[ring_texel_index + c] < 1.0f + 1.0f / 255); ((unsigned char*)output_data)[output_texel_index + c] = (unsigned char)(ring_buffer_entry[ring_texel_index + c] * 255); } From 41dc4c476ccbb3b896ffa59d128b8f518e3a550d Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 26 Jul 2014 22:49:56 -0700 Subject: [PATCH 031/168] In some situations with certain filter kernels with negative values it's possible to generate valid results > 1.0, so saturate it before we write it to make sure it doesn't overflow. Also fix incorrect filter radius while downsampling. --- stb_resample.h | 61 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 45618a0..215ffae 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -214,6 +214,17 @@ static stbr_inline int stbr__max(int a, int b) return a > b ? a : b; } +static stbr_inline float stbr__saturate(float x) +{ + if (x < 0) + return 0; + + if (x > 1) + return 1; + + return x; +} + static float stbr__filter_nearest(float x) { @@ -450,8 +461,8 @@ static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_ total_filter += coefficient_group[i] = stbr__filter_info_table[filter].kernel(in_center_of_out - in_texel_center); } - STBR_DEBUG_ASSERT(total_filter > 0); - STBR_DEBUG_ASSERT(fabs(1 - total_filter) < 0.1f); // Make sure it's not way off. + STBR_DEBUG_ASSERT(total_filter > 0.9); + STBR_DEBUG_ASSERT(total_filter < 1.1f); // Make sure it's not way off. // Make sure the sum of all coefficients is 1. filter_scale = 1 / total_filter; @@ -474,10 +485,34 @@ static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float for (i = 0; i <= out_last_texel - out_first_texel; i++) { float in_texel_center = (float)(i + out_first_texel) + 0.5f; - coefficient_group[i] = stbr__filter_info_table[filter].kernel(out_center_of_in - in_texel_center) * scale_ratio; + coefficient_group[i] = stbr__filter_info_table[filter].kernel((out_center_of_in - in_texel_center)/scale_ratio); } } +#ifdef STBR_DEBUG +static void stbr__check_downsample_coefficients(stbr__info* stbr_info) +{ + for (int i = 0; i < stbr_info->output_w; i++) + { + float total = 0; + for (int j = 0; j < stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); j++) + { + if (i >= stbr_info->horizontal_contributors[j].n0 && i <= stbr_info->horizontal_contributors[j].n1) + { + float coefficient = *stbr__get_coefficient(stbr_info, j, i - stbr_info->horizontal_contributors[j].n0); + total += coefficient; + } + else if (i < stbr_info->horizontal_contributors[j].n0) + break; + } + + STBR_DEBUG_ASSERT(stbr_info->type == STBR_TYPE_UINT8); // Assert below should be 1 + 1/(2^n-1) where n is bits per int. + STBR_DEBUG_ASSERT(total > 0.9f); + STBR_DEBUG_ASSERT(total <= 1.0f + 1.0f / 255); + } +} +#endif + // Each scan line uses the same kernel values so we should calculate the kernel // values once and then we can use them for every scan line. static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) @@ -504,7 +539,7 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) } else { - float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; + float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support; // Looping through in texels for (n = 0; n < total_contributors; n++) @@ -517,6 +552,10 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) stbr__calculate_coefficients_downsample(stbr_info, scale_ratio, out_first_texel, out_last_texel, out_center_of_in, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); } + +#ifdef STBR_DEBUG + stbr__check_downsample_coefficients(stbr_info); +#endif } } @@ -805,11 +844,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i } for (c = 0; c < channels; c++) - { - STBR_DEBUG_ASSERT(encode_buffer[c] < 1.0f + 1.0f/255); - - ((unsigned char*)output_data)[out_texel_index + c] = (unsigned char)(encode_buffer[c] * 255); - } + ((unsigned char*)output_data)[out_texel_index + c] = (unsigned char)(stbr__saturate(encode_buffer[c]) * 255); } } @@ -943,11 +978,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s int ring_texel_index = texel_index; int output_texel_index = output_row + texel_index; for (c = 0; c < channels; c++) - { - //STBR_DEBUG_ASSERT(ring_buffer_entry[ring_texel_index + c] < 1.0f + 1.0f / 255); - - ((unsigned char*)output_data)[output_texel_index + c] = (unsigned char)(ring_buffer_entry[ring_texel_index + c] * 255); - } + ((unsigned char*)output_data)[output_texel_index + c] = (unsigned char)(stbr__saturate(ring_buffer_entry[ring_texel_index + c]) * 255); } } @@ -973,7 +1004,7 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) { int y; float scale_ratio = (float)stbr_info->output_h / stbr_info->input_h; - float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; + float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support; int max_y = stbr_info->input_h + stbr__get_filter_texel_margin(stbr_info->filter); STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); From 6c8cac0a66f2fa9d489c325ec4f26736ede54747 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 26 Jul 2014 23:44:45 -0700 Subject: [PATCH 032/168] Support for sRGB color space. --- stb_resample.h | 205 +++++++++++++++++++++++++++++++++++++++++++++--- tests/resample_test.cpp | 4 +- 2 files changed, 195 insertions(+), 14 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 215ffae..9ae8cda 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -77,7 +77,7 @@ extern "C" { STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, //int channels, int alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, - int channels, stbr_type type, stbr_filter filter, stbr_edge edge, + int channels, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes); @@ -182,6 +182,7 @@ typedef struct stbr_type type; stbr_filter filter; stbr_edge edge; + stbr_colorspace colorspace; stbr__contributors* horizontal_contributors; float* horizontal_coefficients; @@ -225,6 +226,14 @@ static stbr_inline float stbr__saturate(float x) return x; } +static float stbr__srgb_uchar_to_linear_float[256] = { + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, 0.982251f, 0.991102f, 1.0f +}; + +static unsigned char stbr__linear_uchar_to_srgb_uchar[256] = { + 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255 +}; + static float stbr__filter_nearest(float x) { @@ -566,9 +575,92 @@ static float* stbr__get_decode_buffer(stbr__info* stbr_info) return &stbr_info->decode_buffer[stbr__get_filter_texel_margin(stbr_info->filter) * stbr_info->channels]; } +typedef float(*stbr__decode_scanline_type_colorspace)(const void* buffer, int offset, int channel); + +static float stbr__decode_scanline_uchar_sRGB(const void* buffer, int offset, int channel) +{ + return stbr__srgb_uchar_to_linear_float[((const unsigned char*)buffer)[offset+channel]]; +} + +static float stbr__decode_scanline_uchar_linear(const void* buffer, int offset, int channel) +{ + return ((float)((const unsigned char*)buffer)[offset + channel]) / 255; +} + +typedef void(*stbr__decode_scanline_channels)(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace); + +static void stbr__decode_scanline_1(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace) +{ + STBR_DEBUG_ASSERT(channels == 1); + + decode_buffer[out_texel_index] = decode_type_colorspace(input_buffer, input_texel_index, 0); +} + +static void stbr__decode_scanline_2(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace) +{ + STBR_DEBUG_ASSERT(channels == 2); + + decode_buffer[out_texel_index] = decode_type_colorspace(input_buffer, input_texel_index, 0); + decode_buffer[out_texel_index + 1] = decode_type_colorspace(input_buffer, input_texel_index, 1); +} + +static void stbr__decode_scanline_3(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace) +{ + STBR_DEBUG_ASSERT(channels == 3); + + decode_buffer[out_texel_index] = decode_type_colorspace(input_buffer, input_texel_index, 0); + decode_buffer[out_texel_index + 1] = decode_type_colorspace(input_buffer, input_texel_index, 1); + decode_buffer[out_texel_index + 2] = decode_type_colorspace(input_buffer, input_texel_index, 2); +} + +static void stbr__decode_scanline_4(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace) +{ + STBR_DEBUG_ASSERT(channels == 4); + + decode_buffer[out_texel_index] = decode_type_colorspace(input_buffer, input_texel_index, 0); + decode_buffer[out_texel_index + 1] = decode_type_colorspace(input_buffer, input_texel_index, 1); + decode_buffer[out_texel_index + 2] = decode_type_colorspace(input_buffer, input_texel_index, 2); + decode_buffer[out_texel_index + 3] = decode_type_colorspace(input_buffer, input_texel_index, 3); +} + +static void stbr__decode_scanline_n(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace) +{ + int c; + for (c = 0; c < channels; c++) + decode_buffer[out_texel_index + c] = decode_type_colorspace(input_buffer, input_texel_index, c); +} + +static stbr__decode_scanline_type_colorspace stbr__get_decode_type_colorspace_function(stbr_type type, stbr_colorspace colorspace) +{ + STBR_UNIMPLEMENTED(type != STBR_TYPE_UINT8); + + if (colorspace == STBR_COLORSPACE_LINEAR) + return stbr__decode_scanline_uchar_linear; + else if (colorspace == STBR_COLORSPACE_SRGB) + return stbr__decode_scanline_uchar_sRGB; + + STBR_UNIMPLEMENTED("Unknown color space."); + + return NULL; +} + +static stbr__decode_scanline_channels stbr__get_decode_channels_function(int channels) +{ + if (channels == 1) + return &stbr__decode_scanline_1; + else if (channels == 2) + return &stbr__decode_scanline_2; + else if (channels == 3) + return &stbr__decode_scanline_3; + else if (channels == 4) + return &stbr__decode_scanline_4; + + return &stbr__decode_scanline_n; +} + static void stbr__decode_scanline(stbr__info* stbr_info, int n) { - int x, c; + int x; int channels = stbr_info->channels; int input_w = stbr_info->input_w; int input_stride_bytes = stbr_info->input_stride_bytes; @@ -578,15 +670,15 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) int in_buffer_row_index = stbr__edge_wrap(edge, n, stbr_info->input_h) * input_stride_bytes; int max_x = input_w + stbr__get_filter_texel_margin(stbr_info->filter); - STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); + stbr__decode_scanline_channels decode_channels_fn = stbr__get_decode_channels_function(channels); + stbr__decode_scanline_type_colorspace decode_type_colorspace_fn = stbr__get_decode_type_colorspace_function(stbr_info->type, stbr_info->colorspace); for (x = -stbr__get_filter_texel_margin(stbr_info->filter); x < max_x; x++) { int decode_texel_index = x * channels; int input_texel_index = in_buffer_row_index + stbr__edge_wrap(edge, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_texel_index + c] = ((float)((const unsigned char*)input_data)[input_texel_index + c]) / 255; + decode_channels_fn(decode_buffer, decode_texel_index, input_data, input_texel_index, channels, decode_type_colorspace_fn); } } @@ -792,9 +884,93 @@ static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffe return stbr__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); } + +typedef void(*stbr__encode_scanline_type_colorspace)(const void* buffer, int offset, int channel, float value); + +static void stbr__encode_scanline_uchar_sRGB(const void* buffer, int offset, int channel, float value) +{ + ((unsigned char*)buffer)[offset + channel] = stbr__linear_uchar_to_srgb_uchar[(unsigned char)(stbr__saturate(value)*255)]; +} + +static void stbr__encode_scanline_uchar_linear(const void* buffer, int offset, int channel, float value) +{ + ((unsigned char*)buffer)[offset + channel] = (unsigned char)(stbr__saturate(value) * 255); +} + +typedef void(*stbr__encode_scanline_channels)(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace); + +static void stbr__encode_scanline_1(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace) +{ + STBR_DEBUG_ASSERT(channels == 1); + + encode_type_colorspace(output_buffer, output_texel_index, 0, encode_buffer[encode_texel_index]); +} + +static void stbr__encode_scanline_2(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace) +{ + STBR_DEBUG_ASSERT(channels == 2); + + encode_type_colorspace(output_buffer, output_texel_index, 0, encode_buffer[encode_texel_index]); + encode_type_colorspace(output_buffer, output_texel_index, 1, encode_buffer[encode_texel_index + 1]); +} + +static void stbr__encode_scanline_3(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace) +{ + STBR_DEBUG_ASSERT(channels == 3); + + encode_type_colorspace(output_buffer, output_texel_index, 0, encode_buffer[encode_texel_index]); + encode_type_colorspace(output_buffer, output_texel_index, 1, encode_buffer[encode_texel_index + 1]); + encode_type_colorspace(output_buffer, output_texel_index, 2, encode_buffer[encode_texel_index + 2]); +} + +static void stbr__encode_scanline_4(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace) +{ + STBR_DEBUG_ASSERT(channels == 4); + + encode_type_colorspace(output_buffer, output_texel_index, 0, encode_buffer[encode_texel_index]); + encode_type_colorspace(output_buffer, output_texel_index, 1, encode_buffer[encode_texel_index + 1]); + encode_type_colorspace(output_buffer, output_texel_index, 2, encode_buffer[encode_texel_index + 2]); + encode_type_colorspace(output_buffer, output_texel_index, 3, encode_buffer[encode_texel_index + 3]); +} + +static void stbr__encode_scanline_n(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace) +{ + int c; + for (c = 0; c < channels; c++) + encode_type_colorspace(output_buffer, output_texel_index, c, encode_buffer[encode_texel_index + c]); +} + +static stbr__encode_scanline_type_colorspace stbr__get_encode_type_colorspace_function(stbr_type type, stbr_colorspace colorspace) +{ + STBR_UNIMPLEMENTED(type != STBR_TYPE_UINT8); + + if (colorspace == STBR_COLORSPACE_LINEAR) + return stbr__encode_scanline_uchar_linear; + else if (colorspace == STBR_COLORSPACE_SRGB) + return stbr__encode_scanline_uchar_sRGB; + + STBR_UNIMPLEMENTED("Unknown color space."); + + return NULL; +} + +static stbr__encode_scanline_channels stbr__get_encode_channels_function(int channels) +{ + if (channels == 1) + return &stbr__encode_scanline_1; + else if (channels == 2) + return &stbr__encode_scanline_2; + else if (channels == 3) + return &stbr__encode_scanline_3; + else if (channels == 4) + return &stbr__encode_scanline_4; + + return &stbr__encode_scanline_n; +} + static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) { - int x, k, c; + int x, k; int output_w = stbr_info->output_w; stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; float* vertical_coefficients = stbr_info->vertical_coefficients; @@ -819,6 +995,8 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int output_row_index = n * stbr_info->output_stride_bytes; stbr__output_decode_coefficients output_decode_coefficients_fn = stbr__get_output_decode_coefficients_function(channels); + stbr__encode_scanline_channels encode_channels_fn = stbr__get_encode_channels_function(channels); + stbr__encode_scanline_type_colorspace encode_type_colorspace_fn = stbr__get_encode_type_colorspace_function(stbr_info->type, stbr_info->colorspace); STBR_DEBUG_ASSERT(stbr__use_height_upsampling(stbr_info)); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); @@ -843,8 +1021,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i output_decode_coefficients_fn(encode_buffer, 0, ring_buffer_entry, in_texel_index, channels, coefficient); } - for (c = 0; c < channels; c++) - ((unsigned char*)output_data)[out_texel_index + c] = (unsigned char)(stbr__saturate(encode_buffer[c]) * 255); + encode_channels_fn(output_data, out_texel_index, encode_buffer, 0, channels, encode_type_colorspace_fn); } } @@ -959,6 +1136,9 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s float* ring_buffer = stbr_info->ring_buffer; int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); + stbr__encode_scanline_channels encode_channels_fn = stbr__get_encode_channels_function(channels); + stbr__encode_scanline_type_colorspace encode_type_colorspace_fn = stbr__get_encode_type_colorspace_function(stbr_info->type, stbr_info->colorspace); + if (stbr_info->ring_buffer_begin_index >= 0) { // Get rid of whatever we don't need anymore. @@ -968,7 +1148,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s if (stbr_info->ring_buffer_first_scanline >= 0 && stbr_info->ring_buffer_first_scanline < stbr_info->output_h) { - int x, c; + int x; int output_row = stbr_info->ring_buffer_first_scanline * output_stride_bytes; float* ring_buffer_entry = stbr__get_ring_buffer_entry(ring_buffer, stbr_info->ring_buffer_begin_index, ring_buffer_length); @@ -977,8 +1157,8 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s int texel_index = x * channels; int ring_texel_index = texel_index; int output_texel_index = output_row + texel_index; - for (c = 0; c < channels; c++) - ((unsigned char*)output_data)[output_texel_index + c] = (unsigned char)(stbr__saturate(ring_buffer_entry[ring_texel_index + c]) * 255); + + encode_channels_fn(output_data, output_texel_index, ring_buffer_entry, ring_texel_index, channels, encode_type_colorspace_fn); } } @@ -1040,7 +1220,7 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, - int channels, stbr_type type, stbr_filter filter, stbr_edge edge, + int channels, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes) { int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w; @@ -1085,6 +1265,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->type = type; stbr_info->filter = filter; stbr_info->edge = edge; + stbr_info->colorspace = colorspace; stbr_info->ring_buffer_length_bytes = output_w * channels * sizeof(float); stbr_info->decode_buffer_texels = input_w + stbr__get_filter_texel_margin(filter) * 2; diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 0077131..5ec296a 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -48,7 +48,7 @@ int main(int argc, char** argv) int in_w = 512; int in_h = 512; - size_t memory_required = stbr_calculate_memory(in_w, in_h, w*n, out_w, out_h, out_stride, n, STBR_FILTER_BICUBIC); + size_t memory_required = stbr_calculate_memory(in_w, in_h, w*n, out_w, out_h, out_stride, n, STBR_FILTER_CATMULLROM); void* extra_memory = malloc(memory_required); // Cut out the outside 64 pixels all around to test the stride. @@ -56,7 +56,7 @@ int main(int argc, char** argv) STBR_ASSERT(in_w + border <= w); STBR_ASSERT(in_h + border <= h); - stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_BICUBIC, STBR_EDGE_CLAMP, extra_memory, memory_required); + stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); free(extra_memory); From 7d475825785703ca81264d0ac5662a46940589fd Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sun, 27 Jul 2014 00:09:22 -0700 Subject: [PATCH 033/168] Support for 16 and 32 bit integer images, and float images. --- stb_resample.h | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 148 insertions(+), 14 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 9ae8cda..113c6cf 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -49,7 +49,10 @@ typedef enum typedef enum { - STBR_TYPE_UINT8 = 1, + STBR_TYPE_UINT8 = 1, + STBR_TYPE_UINT16 = 2, + STBR_TYPE_UINT32 = 3, + STBR_TYPE_FLOAT = 4, } stbr_type; typedef unsigned char stbr_uc; @@ -234,6 +237,23 @@ static unsigned char stbr__linear_uchar_to_srgb_uchar[256] = { 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255 }; +float stbr__srgb_to_linear(float f) +{ + if (f <= 0.04045f) + return f / 12.92f; + else + return (float)pow((f + 0.055f) / 1.055f, 2.4f); +} + +float stbr__linear_to_srgb(float f) +{ + if (f <= 0.0031308f) + return f * 12.92f; + else + return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; +} + + static float stbr__filter_nearest(float x) { @@ -579,7 +599,7 @@ typedef float(*stbr__decode_scanline_type_colorspace)(const void* buffer, int of static float stbr__decode_scanline_uchar_sRGB(const void* buffer, int offset, int channel) { - return stbr__srgb_uchar_to_linear_float[((const unsigned char*)buffer)[offset+channel]]; + return stbr__srgb_uchar_to_linear_float[((const unsigned char*)buffer)[offset + channel]]; } static float stbr__decode_scanline_uchar_linear(const void* buffer, int offset, int channel) @@ -587,6 +607,36 @@ static float stbr__decode_scanline_uchar_linear(const void* buffer, int offset, return ((float)((const unsigned char*)buffer)[offset + channel]) / 255; } +static float stbr__decode_scanline_ushort_sRGB(const void* buffer, int offset, int channel) +{ + return stbr__srgb_to_linear((float)(((const unsigned short*)buffer)[offset + channel])/65535); +} + +static float stbr__decode_scanline_ushort_linear(const void* buffer, int offset, int channel) +{ + return ((float)((const unsigned short*)buffer)[offset + channel]) / 65535; +} + +static float stbr__decode_scanline_uint_sRGB(const void* buffer, int offset, int channel) +{ + return stbr__srgb_to_linear((float)(((const unsigned int*)buffer)[offset + channel]) / 4294967295); +} + +static float stbr__decode_scanline_uint_linear(const void* buffer, int offset, int channel) +{ + return ((float)((const unsigned int*)buffer)[offset + channel]) / 4294967295; +} + +static float stbr__decode_scanline_float_sRGB(const void* buffer, int offset, int channel) +{ + return stbr__srgb_to_linear(((const float*)buffer)[offset + channel]); +} + +static float stbr__decode_scanline_float_linear(const void* buffer, int offset, int channel) +{ + return ((const float*)buffer)[offset + channel]; +} + typedef void(*stbr__decode_scanline_channels)(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace); static void stbr__decode_scanline_1(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace) @@ -632,15 +682,42 @@ static void stbr__decode_scanline_n(float* decode_buffer, int out_texel_index, c static stbr__decode_scanline_type_colorspace stbr__get_decode_type_colorspace_function(stbr_type type, stbr_colorspace colorspace) { - STBR_UNIMPLEMENTED(type != STBR_TYPE_UINT8); + switch (type) + { + case STBR_TYPE_UINT8: + if (colorspace == STBR_COLORSPACE_LINEAR) + return stbr__decode_scanline_uchar_linear; + else if (colorspace == STBR_COLORSPACE_SRGB) + return stbr__decode_scanline_uchar_sRGB; + break; - if (colorspace == STBR_COLORSPACE_LINEAR) - return stbr__decode_scanline_uchar_linear; - else if (colorspace == STBR_COLORSPACE_SRGB) - return stbr__decode_scanline_uchar_sRGB; + case STBR_TYPE_UINT16: + if (colorspace == STBR_COLORSPACE_LINEAR) + return stbr__decode_scanline_ushort_linear; + else if (colorspace == STBR_COLORSPACE_SRGB) + return stbr__decode_scanline_ushort_sRGB; + break; + + case STBR_TYPE_UINT32: + if (colorspace == STBR_COLORSPACE_LINEAR) + return stbr__decode_scanline_uint_linear; + else if (colorspace == STBR_COLORSPACE_SRGB) + return stbr__decode_scanline_uint_sRGB; + break; + + case STBR_TYPE_FLOAT: + if (colorspace == STBR_COLORSPACE_LINEAR) + return stbr__decode_scanline_float_linear; + else if (colorspace == STBR_COLORSPACE_SRGB) + return stbr__decode_scanline_float_sRGB; + break; + + default: + STBR_UNIMPLEMENTED("Unknown type."); + return NULL; + } STBR_UNIMPLEMENTED("Unknown color space."); - return NULL; } @@ -897,6 +974,36 @@ static void stbr__encode_scanline_uchar_linear(const void* buffer, int offset, i ((unsigned char*)buffer)[offset + channel] = (unsigned char)(stbr__saturate(value) * 255); } +static void stbr__encode_scanline_ushort_sRGB(const void* buffer, int offset, int channel, float value) +{ + ((unsigned short*)buffer)[offset + channel] = (unsigned short)stbr__linear_to_srgb((stbr__saturate(value) * 65535)); +} + +static void stbr__encode_scanline_ushort_linear(const void* buffer, int offset, int channel, float value) +{ + ((unsigned short*)buffer)[offset + channel] = (unsigned short)(stbr__saturate(value) * 65535); +} + +static void stbr__encode_scanline_uint_sRGB(const void* buffer, int offset, int channel, float value) +{ + ((unsigned int*)buffer)[offset + channel] = (unsigned int)stbr__linear_to_srgb((stbr__saturate(value) * 4294967295)); +} + +static void stbr__encode_scanline_uint_linear(const void* buffer, int offset, int channel, float value) +{ + ((unsigned int*)buffer)[offset + channel] = (unsigned int)(stbr__saturate(value) * 4294967295); +} + +static void stbr__encode_scanline_float_sRGB(const void* buffer, int offset, int channel, float value) +{ + ((float*)buffer)[offset + channel] = stbr__linear_to_srgb(stbr__saturate(value)); +} + +static void stbr__encode_scanline_float_linear(const void* buffer, int offset, int channel, float value) +{ + ((float*)buffer)[offset + channel] = stbr__saturate(value); +} + typedef void(*stbr__encode_scanline_channels)(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace); static void stbr__encode_scanline_1(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace) @@ -942,15 +1049,42 @@ static void stbr__encode_scanline_n(void* output_buffer, int output_texel_index, static stbr__encode_scanline_type_colorspace stbr__get_encode_type_colorspace_function(stbr_type type, stbr_colorspace colorspace) { - STBR_UNIMPLEMENTED(type != STBR_TYPE_UINT8); + switch (type) + { + case STBR_TYPE_UINT8: + if (colorspace == STBR_COLORSPACE_LINEAR) + return stbr__encode_scanline_uchar_linear; + else if (colorspace == STBR_COLORSPACE_SRGB) + return stbr__encode_scanline_uchar_sRGB; + break; - if (colorspace == STBR_COLORSPACE_LINEAR) - return stbr__encode_scanline_uchar_linear; - else if (colorspace == STBR_COLORSPACE_SRGB) - return stbr__encode_scanline_uchar_sRGB; + case STBR_TYPE_UINT16: + if (colorspace == STBR_COLORSPACE_LINEAR) + return stbr__encode_scanline_ushort_linear; + else if (colorspace == STBR_COLORSPACE_SRGB) + return stbr__encode_scanline_ushort_sRGB; + break; + + case STBR_TYPE_UINT32: + if (colorspace == STBR_COLORSPACE_LINEAR) + return stbr__encode_scanline_uint_linear; + else if (colorspace == STBR_COLORSPACE_SRGB) + return stbr__encode_scanline_uint_sRGB; + break; + + case STBR_TYPE_FLOAT: + if (colorspace == STBR_COLORSPACE_LINEAR) + return stbr__encode_scanline_float_linear; + else if (colorspace == STBR_COLORSPACE_SRGB) + return stbr__encode_scanline_float_sRGB; + break; + + default: + STBR_UNIMPLEMENTED("Unknown type."); + return NULL; + } STBR_UNIMPLEMENTED("Unknown color space."); - return NULL; } From ef3a460ec41af11278c1a4bd6021f610e8423e59 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sun, 27 Jul 2014 00:55:47 -0700 Subject: [PATCH 034/168] Some better looking horners save another multiplication. --- stb_resample.h | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 113c6cf..e577f0f 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -279,12 +279,10 @@ static float stbr__filter_bicubic(float x) { x = (float)fabs(x); - float xx = x*x; - if (x < 1.0f) - return 0.5f * (x * xx) - xx + 0.66666666666f; + return 0.66666666666f + x*x*(0.5f*x - 1); else if (x < 2.0f) - return -0.16666666f * (x * xx) + xx - 2 * x + 1.3333333333f; + return 1.3333333333f + x*(-2 + x*(1 - 0.16666666f * x)); return (0.0f); } @@ -293,12 +291,10 @@ static float stbr__filter_catmullrom(float x) { x = (float)fabs(x); - float xx = x*x; - if (x < 1.0f) - return 1.5f * xx * (x - 1.66666666f) + 1; + return 1 - x*x*(1.5f - 2.5f*x); else if (x < 2.0f) - return -0.5f * x * ((xx - 5 * x) + 8) + 2; + return 2 - x*(4 + x*(0.5f*x - 2.5f)); return (0.0f); } @@ -307,12 +303,10 @@ static float stbr__filter_mitchell(float x) { x = (float)fabs(x); - float xx = x*x; - if (x < 1.0f) - return 1.1666666666666f * xx * (x - 1.714285715f) + 0.8888888888f; + return 0.8888888888f + x*x*(1.1666666666666f * x - 2.0f); else if (x < 2.0f) - return -0.3888888888f * x * ((xx - 5.14285714f * x) + 8.571428571f) + 1.777777777777f; + return 1.777777777777f + x*(-3.3333333333f + x*(2 - 0.3888888888888f*x)); return (0.0f); } From e2ac4f65050143777476893a0e21bb4aad4b9bdb Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sun, 27 Jul 2014 12:05:17 -0700 Subject: [PATCH 035/168] More resample ideas --- docs/stb_resample_ideas.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/stb_resample_ideas.txt b/docs/stb_resample_ideas.txt index 96b3fac..0903ead 100644 --- a/docs/stb_resample_ideas.txt +++ b/docs/stb_resample_ideas.txt @@ -196,6 +196,6 @@ Wish list: s0, t0, s1, t1 vs scale_x, scale_y, offset_x, offset_y - What's the best interface? Separate wrap modes and filter modes per axis Alpha test coverage respecting resize (FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp) - Edge: Clamp, ignore, wrap, reflect + Installable filter kernels From 1fcf30ada021e5bbd7f1ed7deff39dbfb91895f5 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 29 Jul 2014 00:33:29 -0700 Subject: [PATCH 036/168] Fix a math error. --- stb_resample.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stb_resample.h b/stb_resample.h index e577f0f..963f7ff 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -292,7 +292,7 @@ static float stbr__filter_catmullrom(float x) x = (float)fabs(x); if (x < 1.0f) - return 1 - x*x*(1.5f - 2.5f*x); + return 1 - x*x*(2.5f - 1.5f*x); else if (x < 2.0f) return 2 - x*(4 + x*(0.5f*x - 2.5f)); From 5dff80ed313028d61d84393dfb3c87e11b0fc61c Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 29 Jul 2014 11:39:42 -0700 Subject: [PATCH 037/168] Trying some different strategies for optimizing the decoder. The code in #ifdef 1 is slightly faster by my measurements, but a whole lot uglier. --- stb_resample.h | 330 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 193 insertions(+), 137 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 963f7ff..2217424 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -45,16 +45,22 @@ typedef enum { STBR_COLORSPACE_LINEAR = 1, STBR_COLORSPACE_SRGB = 2, + // If you add here, update STBR_MAX_COLORSPACES } stbr_colorspace; +#define STBR_MAX_COLORSPACES 2 + typedef enum { STBR_TYPE_UINT8 = 1, STBR_TYPE_UINT16 = 2, STBR_TYPE_UINT32 = 3, STBR_TYPE_FLOAT = 4, + // If you add here, update STBR_MAX_TYPES } stbr_type; +#define STBR_MAX_TYPES 4 + typedef unsigned char stbr_uc; typedef unsigned int stbr_size_t; // to avoid including a header for size_t @@ -109,9 +115,9 @@ extern "C" { // If you hit this it means I haven't done it yet. #define STBR_UNIMPLEMENTED(x) STBR_ASSERT(!(x)) -#ifdef STBR_DEBUG_OVERWRITE_TEST + +// For memset #include -#endif #include @@ -152,6 +158,8 @@ typedef unsigned char stbr__validate_uint32[sizeof(stbr__uint32) == 4 ? 1 : -1]; #define STBR_ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) +#define STBR__MAX_UNROLLED_CHANNELS 4 + // Kernel function centered at 0 typedef float (stbr__kernel_fn)(float x); @@ -589,150 +597,104 @@ static float* stbr__get_decode_buffer(stbr__info* stbr_info) return &stbr_info->decode_buffer[stbr__get_filter_texel_margin(stbr_info->filter) * stbr_info->channels]; } -typedef float(*stbr__decode_scanline_type_colorspace)(const void* buffer, int offset, int channel); +#define DECODE(type, colorspace) ((type) * (STBR_MAX_COLORSPACES) + (colorspace)) -static float stbr__decode_scanline_uchar_sRGB(const void* buffer, int offset, int channel) +#define DECODE_UINT8_SRGB(n) \ + decode_buffer[decode_texel_index + n] = stbr__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_texel_index + n]]; + +#define DECODE_UINT8_LINEAR(n) \ + decode_buffer[decode_texel_index + n] = ((float)((const unsigned char*)input_data)[input_texel_index + n]) / 255; + +#define DECODE_UINT16_SRGB(n) \ + decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((float)((const unsigned short*)input_data)[input_texel_index + n])/65535); + +#define DECODE_UINT16_LINEAR(n) \ + decode_buffer[decode_texel_index + n] = ((float)((const unsigned short*)input_data)[input_texel_index + n]) / 65535; + +#define DECODE_UINT32_SRGB(n) \ + decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((float)((const unsigned int*)input_data)[input_texel_index + n])/4294967295); + +#define DECODE_UINT32_LINEAR(n) \ + decode_buffer[decode_texel_index + n] = ((float)((const unsigned int*)input_data)[input_texel_index + n]) / 4294967295; + +#define DECODE_FLOAT_SRGB(n) \ + decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((const float*)input_data)[input_texel_index + n]); + +#define DECODE_FLOAT_LINEAR(n) \ + decode_buffer[decode_texel_index + n] = ((const float*)input_data)[input_texel_index + n]; + +static void stbr__decode_scanline_range(stbr__info* stbr_info, int start, int max, int in_buffer_row_index) { - return stbr__srgb_uchar_to_linear_float[((const unsigned char*)buffer)[offset + channel]]; -} + int x; + int channels = stbr_info->channels; + stbr_edge edge = stbr_info->edge; + int colorspace = stbr_info->colorspace; + int type = stbr_info->type; + int decode = DECODE(type, colorspace); + int input_w = stbr_info->input_w; + float* decode_buffer = stbr__get_decode_buffer(stbr_info); + const void* input_data = stbr_info->input_data; -static float stbr__decode_scanline_uchar_linear(const void* buffer, int offset, int channel) -{ - return ((float)((const unsigned char*)buffer)[offset + channel]) / 255; -} - -static float stbr__decode_scanline_ushort_sRGB(const void* buffer, int offset, int channel) -{ - return stbr__srgb_to_linear((float)(((const unsigned short*)buffer)[offset + channel])/65535); -} - -static float stbr__decode_scanline_ushort_linear(const void* buffer, int offset, int channel) -{ - return ((float)((const unsigned short*)buffer)[offset + channel]) / 65535; -} - -static float stbr__decode_scanline_uint_sRGB(const void* buffer, int offset, int channel) -{ - return stbr__srgb_to_linear((float)(((const unsigned int*)buffer)[offset + channel]) / 4294967295); -} - -static float stbr__decode_scanline_uint_linear(const void* buffer, int offset, int channel) -{ - return ((float)((const unsigned int*)buffer)[offset + channel]) / 4294967295; -} - -static float stbr__decode_scanline_float_sRGB(const void* buffer, int offset, int channel) -{ - return stbr__srgb_to_linear(((const float*)buffer)[offset + channel]); -} - -static float stbr__decode_scanline_float_linear(const void* buffer, int offset, int channel) -{ - return ((const float*)buffer)[offset + channel]; -} - -typedef void(*stbr__decode_scanline_channels)(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace); - -static void stbr__decode_scanline_1(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace) -{ - STBR_DEBUG_ASSERT(channels == 1); - - decode_buffer[out_texel_index] = decode_type_colorspace(input_buffer, input_texel_index, 0); -} - -static void stbr__decode_scanline_2(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace) -{ - STBR_DEBUG_ASSERT(channels == 2); - - decode_buffer[out_texel_index] = decode_type_colorspace(input_buffer, input_texel_index, 0); - decode_buffer[out_texel_index + 1] = decode_type_colorspace(input_buffer, input_texel_index, 1); -} - -static void stbr__decode_scanline_3(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace) -{ - STBR_DEBUG_ASSERT(channels == 3); - - decode_buffer[out_texel_index] = decode_type_colorspace(input_buffer, input_texel_index, 0); - decode_buffer[out_texel_index + 1] = decode_type_colorspace(input_buffer, input_texel_index, 1); - decode_buffer[out_texel_index + 2] = decode_type_colorspace(input_buffer, input_texel_index, 2); -} - -static void stbr__decode_scanline_4(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace) -{ - STBR_DEBUG_ASSERT(channels == 4); - - decode_buffer[out_texel_index] = decode_type_colorspace(input_buffer, input_texel_index, 0); - decode_buffer[out_texel_index + 1] = decode_type_colorspace(input_buffer, input_texel_index, 1); - decode_buffer[out_texel_index + 2] = decode_type_colorspace(input_buffer, input_texel_index, 2); - decode_buffer[out_texel_index + 3] = decode_type_colorspace(input_buffer, input_texel_index, 3); -} - -static void stbr__decode_scanline_n(float* decode_buffer, int out_texel_index, const void* input_buffer, int input_texel_index, int channels, stbr__decode_scanline_type_colorspace decode_type_colorspace) -{ - int c; - for (c = 0; c < channels; c++) - decode_buffer[out_texel_index + c] = decode_type_colorspace(input_buffer, input_texel_index, c); -} - -static stbr__decode_scanline_type_colorspace stbr__get_decode_type_colorspace_function(stbr_type type, stbr_colorspace colorspace) -{ - switch (type) + for (x = start; x < max; x++) { - case STBR_TYPE_UINT8: - if (colorspace == STBR_COLORSPACE_LINEAR) - return stbr__decode_scanline_uchar_linear; - else if (colorspace == STBR_COLORSPACE_SRGB) - return stbr__decode_scanline_uchar_sRGB; - break; + int decode_texel_index = x * channels; + int input_texel_index = in_buffer_row_index + stbr__edge_wrap(edge, x, input_w) * channels; - case STBR_TYPE_UINT16: - if (colorspace == STBR_COLORSPACE_LINEAR) - return stbr__decode_scanline_ushort_linear; - else if (colorspace == STBR_COLORSPACE_SRGB) - return stbr__decode_scanline_ushort_sRGB; - break; + switch (decode) + { + case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): + for (int n = 0; n < channels; n++) + DECODE_UINT8_LINEAR(n); + break; - case STBR_TYPE_UINT32: - if (colorspace == STBR_COLORSPACE_LINEAR) - return stbr__decode_scanline_uint_linear; - else if (colorspace == STBR_COLORSPACE_SRGB) - return stbr__decode_scanline_uint_sRGB; - break; + case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): + for (int n = 0; n < channels; n++) + DECODE_UINT8_SRGB(n); + break; - case STBR_TYPE_FLOAT: - if (colorspace == STBR_COLORSPACE_LINEAR) - return stbr__decode_scanline_float_linear; - else if (colorspace == STBR_COLORSPACE_SRGB) - return stbr__decode_scanline_float_sRGB; - break; + case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): + for (int n = 0; n < channels; n++) + DECODE_UINT16_LINEAR(n); + break; - default: - STBR_UNIMPLEMENTED("Unknown type."); - return NULL; + case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): + for (int n = 0; n < channels; n++) + DECODE_UINT16_SRGB(n); + break; + + case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): + for (int n = 0; n < channels; n++) + DECODE_UINT32_LINEAR(n); + break; + + case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): + for (int n = 0; n < channels; n++) + DECODE_UINT32_SRGB(n); + break; + + case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): + for (int n = 0; n < channels; n++) + DECODE_FLOAT_LINEAR(n); + break; + + case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): + for (int n = 0; n < channels; n++) + DECODE_FLOAT_SRGB(n); + break; + + default: + STBR_UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; + } } - - STBR_UNIMPLEMENTED("Unknown color space."); - return NULL; -} - -static stbr__decode_scanline_channels stbr__get_decode_channels_function(int channels) -{ - if (channels == 1) - return &stbr__decode_scanline_1; - else if (channels == 2) - return &stbr__decode_scanline_2; - else if (channels == 3) - return &stbr__decode_scanline_3; - else if (channels == 4) - return &stbr__decode_scanline_4; - - return &stbr__decode_scanline_n; } static void stbr__decode_scanline(stbr__info* stbr_info, int n) { int x; int channels = stbr_info->channels; + int type = stbr_info->type; + int colorspace = stbr_info->colorspace; int input_w = stbr_info->input_w; int input_stride_bytes = stbr_info->input_stride_bytes; const void* input_data = stbr_info->input_data; @@ -740,19 +702,113 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) stbr_edge edge = stbr_info->edge; int in_buffer_row_index = stbr__edge_wrap(edge, n, stbr_info->input_h) * input_stride_bytes; int max_x = input_w + stbr__get_filter_texel_margin(stbr_info->filter); + int decode = DECODE(type, colorspace); - stbr__decode_scanline_channels decode_channels_fn = stbr__get_decode_channels_function(channels); - stbr__decode_scanline_type_colorspace decode_type_colorspace_fn = stbr__get_decode_type_colorspace_function(stbr_info->type, stbr_info->colorspace); +#if 1 + // Do the first and last first because they're more complicated due to edge behavior + stbr__decode_scanline_range(stbr_info, -stbr__get_filter_texel_margin(stbr_info->filter), 0, in_buffer_row_index); + stbr__decode_scanline_range(stbr_info, input_w, max_x, in_buffer_row_index); - for (x = -stbr__get_filter_texel_margin(stbr_info->filter); x < max_x; x++) + const int group_size = 4; + // Now do the center ones since we can do them in big batches. 4 seems to be the magic number here, after some testing. + for (x = 0; x < input_w * channels - group_size; x += group_size) { - int decode_texel_index = x * channels; - int input_texel_index = in_buffer_row_index + stbr__edge_wrap(edge, x, input_w) * channels; + int decode_texel_index = x; + int input_texel_index = in_buffer_row_index + x; - decode_channels_fn(decode_buffer, decode_texel_index, input_data, input_texel_index, channels, decode_type_colorspace_fn); + switch (decode) + { + case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): + DECODE_UINT8_LINEAR(0); + DECODE_UINT8_LINEAR(1); + DECODE_UINT8_LINEAR(2); + DECODE_UINT8_LINEAR(3); + break; + + case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): + DECODE_UINT8_SRGB(0); + DECODE_UINT8_SRGB(1); + DECODE_UINT8_SRGB(2); + DECODE_UINT8_SRGB(3); + break; + + case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): + DECODE_UINT16_LINEAR(0); + DECODE_UINT16_LINEAR(1); + DECODE_UINT16_LINEAR(2); + DECODE_UINT16_LINEAR(3); + break; + + case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): + DECODE_UINT16_SRGB(0); + DECODE_UINT16_SRGB(1); + DECODE_UINT16_SRGB(2); + DECODE_UINT16_SRGB(3); + break; + + case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): + DECODE_UINT32_LINEAR(0); + DECODE_UINT32_LINEAR(1); + DECODE_UINT32_LINEAR(2); + DECODE_UINT32_LINEAR(3); + break; + + case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): + DECODE_UINT32_SRGB(0); + DECODE_UINT32_SRGB(1); + DECODE_UINT32_SRGB(2); + DECODE_UINT32_SRGB(3); + break; + + case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): + DECODE_FLOAT_LINEAR(0); + DECODE_FLOAT_LINEAR(1); + DECODE_FLOAT_LINEAR(2); + DECODE_FLOAT_LINEAR(3); + break; + + case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): + DECODE_FLOAT_SRGB(0); + DECODE_FLOAT_SRGB(1); + DECODE_FLOAT_SRGB(2); + DECODE_FLOAT_SRGB(3); + break; + + default: + STBR_UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; + } } + + // Do the remainder one at a time. + for (; x < input_w * channels; x++) + { + int decode_texel_index = x; + int input_texel_index = in_buffer_row_index + x; + + switch (decode) + { + case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): DECODE_UINT8_LINEAR(0); break; + case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): DECODE_UINT8_SRGB(0); break; + case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): DECODE_UINT16_LINEAR(0); break; + case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): DECODE_UINT16_SRGB(0); break; + case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): DECODE_UINT32_LINEAR(0); break; + case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): DECODE_UINT32_SRGB(0); break; + case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): DECODE_FLOAT_LINEAR(0); break; + case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): DECODE_FLOAT_SRGB(0); break; + + default: + STBR_UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; + } + } +#else + stbr__decode_scanline_range(stbr_info, -stbr__get_filter_texel_margin(stbr_info->filter), max_x, in_buffer_row_index); +#endif } +#undef DECODE + static float* stbr__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) { return &ring_buffer[index * ring_buffer_length]; From 9bd5abb52dc2e8ae79d4d6debaf4676d8605d2d2 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 29 Jul 2014 11:44:32 -0700 Subject: [PATCH 038/168] Both versions run within the margin of error on my machine so we'll go with the simpler one. --- stb_resample.h | 177 +++++++-------------------------------------------------- 1 file changed, 19 insertions(+), 158 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 2217424..792092f 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -599,96 +599,6 @@ static float* stbr__get_decode_buffer(stbr__info* stbr_info) #define DECODE(type, colorspace) ((type) * (STBR_MAX_COLORSPACES) + (colorspace)) -#define DECODE_UINT8_SRGB(n) \ - decode_buffer[decode_texel_index + n] = stbr__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_texel_index + n]]; - -#define DECODE_UINT8_LINEAR(n) \ - decode_buffer[decode_texel_index + n] = ((float)((const unsigned char*)input_data)[input_texel_index + n]) / 255; - -#define DECODE_UINT16_SRGB(n) \ - decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((float)((const unsigned short*)input_data)[input_texel_index + n])/65535); - -#define DECODE_UINT16_LINEAR(n) \ - decode_buffer[decode_texel_index + n] = ((float)((const unsigned short*)input_data)[input_texel_index + n]) / 65535; - -#define DECODE_UINT32_SRGB(n) \ - decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((float)((const unsigned int*)input_data)[input_texel_index + n])/4294967295); - -#define DECODE_UINT32_LINEAR(n) \ - decode_buffer[decode_texel_index + n] = ((float)((const unsigned int*)input_data)[input_texel_index + n]) / 4294967295; - -#define DECODE_FLOAT_SRGB(n) \ - decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((const float*)input_data)[input_texel_index + n]); - -#define DECODE_FLOAT_LINEAR(n) \ - decode_buffer[decode_texel_index + n] = ((const float*)input_data)[input_texel_index + n]; - -static void stbr__decode_scanline_range(stbr__info* stbr_info, int start, int max, int in_buffer_row_index) -{ - int x; - int channels = stbr_info->channels; - stbr_edge edge = stbr_info->edge; - int colorspace = stbr_info->colorspace; - int type = stbr_info->type; - int decode = DECODE(type, colorspace); - int input_w = stbr_info->input_w; - float* decode_buffer = stbr__get_decode_buffer(stbr_info); - const void* input_data = stbr_info->input_data; - - for (x = start; x < max; x++) - { - int decode_texel_index = x * channels; - int input_texel_index = in_buffer_row_index + stbr__edge_wrap(edge, x, input_w) * channels; - - switch (decode) - { - case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): - for (int n = 0; n < channels; n++) - DECODE_UINT8_LINEAR(n); - break; - - case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): - for (int n = 0; n < channels; n++) - DECODE_UINT8_SRGB(n); - break; - - case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): - for (int n = 0; n < channels; n++) - DECODE_UINT16_LINEAR(n); - break; - - case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): - for (int n = 0; n < channels; n++) - DECODE_UINT16_SRGB(n); - break; - - case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): - for (int n = 0; n < channels; n++) - DECODE_UINT32_LINEAR(n); - break; - - case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): - for (int n = 0; n < channels; n++) - DECODE_UINT32_SRGB(n); - break; - - case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): - for (int n = 0; n < channels; n++) - DECODE_FLOAT_LINEAR(n); - break; - - case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): - for (int n = 0; n < channels; n++) - DECODE_FLOAT_SRGB(n); - break; - - default: - STBR_UNIMPLEMENTED("Unknown type/colorspace/channels combination."); - break; - } - } -} - static void stbr__decode_scanline(stbr__info* stbr_info, int n) { int x; @@ -704,74 +614,51 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) int max_x = input_w + stbr__get_filter_texel_margin(stbr_info->filter); int decode = DECODE(type, colorspace); -#if 1 - // Do the first and last first because they're more complicated due to edge behavior - stbr__decode_scanline_range(stbr_info, -stbr__get_filter_texel_margin(stbr_info->filter), 0, in_buffer_row_index); - stbr__decode_scanline_range(stbr_info, input_w, max_x, in_buffer_row_index); - - const int group_size = 4; - // Now do the center ones since we can do them in big batches. 4 seems to be the magic number here, after some testing. - for (x = 0; x < input_w * channels - group_size; x += group_size) + for (x = -stbr__get_filter_texel_margin(stbr_info->filter); x < max_x; x++) { - int decode_texel_index = x; - int input_texel_index = in_buffer_row_index + x; + int decode_texel_index = x * channels; + int input_texel_index = in_buffer_row_index + stbr__edge_wrap(edge, x, input_w) * channels; switch (decode) { case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): - DECODE_UINT8_LINEAR(0); - DECODE_UINT8_LINEAR(1); - DECODE_UINT8_LINEAR(2); - DECODE_UINT8_LINEAR(3); + for (int n = 0; n < channels; n++) + decode_buffer[decode_texel_index + n] = ((float)((const unsigned char*)input_data)[input_texel_index + n]) / 255; break; case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): - DECODE_UINT8_SRGB(0); - DECODE_UINT8_SRGB(1); - DECODE_UINT8_SRGB(2); - DECODE_UINT8_SRGB(3); + for (int n = 0; n < channels; n++) + decode_buffer[decode_texel_index + n] = stbr__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_texel_index + n]]; break; case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): - DECODE_UINT16_LINEAR(0); - DECODE_UINT16_LINEAR(1); - DECODE_UINT16_LINEAR(2); - DECODE_UINT16_LINEAR(3); + for (int n = 0; n < channels; n++) + decode_buffer[decode_texel_index + n] = ((float)((const unsigned short*)input_data)[input_texel_index + n]) / 65535; break; case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): - DECODE_UINT16_SRGB(0); - DECODE_UINT16_SRGB(1); - DECODE_UINT16_SRGB(2); - DECODE_UINT16_SRGB(3); + for (int n = 0; n < channels; n++) + decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((float)((const unsigned short*)input_data)[input_texel_index + n]) / 65535); break; case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): - DECODE_UINT32_LINEAR(0); - DECODE_UINT32_LINEAR(1); - DECODE_UINT32_LINEAR(2); - DECODE_UINT32_LINEAR(3); + for (int n = 0; n < channels; n++) + decode_buffer[decode_texel_index + n] = ((float)((const unsigned int*)input_data)[input_texel_index + n]) / 4294967295; break; case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): - DECODE_UINT32_SRGB(0); - DECODE_UINT32_SRGB(1); - DECODE_UINT32_SRGB(2); - DECODE_UINT32_SRGB(3); + for (int n = 0; n < channels; n++) + decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((float)((const unsigned int*)input_data)[input_texel_index + n]) / 4294967295); break; case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): - DECODE_FLOAT_LINEAR(0); - DECODE_FLOAT_LINEAR(1); - DECODE_FLOAT_LINEAR(2); - DECODE_FLOAT_LINEAR(3); + for (int n = 0; n < channels; n++) + decode_buffer[decode_texel_index + n] = ((const float*)input_data)[input_texel_index + n]; break; case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): - DECODE_FLOAT_SRGB(0); - DECODE_FLOAT_SRGB(1); - DECODE_FLOAT_SRGB(2); - DECODE_FLOAT_SRGB(3); + for (int n = 0; n < channels; n++) + decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((const float*)input_data)[input_texel_index + n]); break; default: @@ -779,32 +666,6 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) break; } } - - // Do the remainder one at a time. - for (; x < input_w * channels; x++) - { - int decode_texel_index = x; - int input_texel_index = in_buffer_row_index + x; - - switch (decode) - { - case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): DECODE_UINT8_LINEAR(0); break; - case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): DECODE_UINT8_SRGB(0); break; - case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): DECODE_UINT16_LINEAR(0); break; - case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): DECODE_UINT16_SRGB(0); break; - case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): DECODE_UINT32_LINEAR(0); break; - case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): DECODE_UINT32_SRGB(0); break; - case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): DECODE_FLOAT_LINEAR(0); break; - case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): DECODE_FLOAT_SRGB(0); break; - - default: - STBR_UNIMPLEMENTED("Unknown type/colorspace/channels combination."); - break; - } - } -#else - stbr__decode_scanline_range(stbr_info, -stbr__get_filter_texel_margin(stbr_info->filter), max_x, in_buffer_row_index); -#endif } #undef DECODE From 3a3e06029e4836d86ea7e5e7b7e5da413a78a843 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 29 Jul 2014 12:11:03 -0700 Subject: [PATCH 039/168] This is definitely faster than the function pointer solution. --- stb_resample.h | 79 +++++++++------------------------------------------------- 1 file changed, 12 insertions(+), 67 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 792092f..3e15400 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -699,62 +699,6 @@ static float* stbr__add_empty_ring_buffer_entry(stbr__info* stbr_info, int n) return ring_buffer; } -typedef void(*stbr__output_decode_coefficients)(float* output_buffer, int out_texel_index, float* decode_buffer, int decode_texel_index, int channels, float coefficient); - -static void stbr__output_decode_coefficients_1(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) -{ - STBR_DEBUG_ASSERT(channels == 1); - - output_buffer[out_texel_index] += input_buffer[input_texel_index] * coefficient; -} - -static void stbr__output_decode_coefficients_2(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) -{ - STBR_DEBUG_ASSERT(channels == 2); - - output_buffer[out_texel_index ] += input_buffer[input_texel_index ] * coefficient; - output_buffer[out_texel_index + 1] += input_buffer[input_texel_index + 1] * coefficient; -} - -static void stbr__output_decode_coefficients_3(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) -{ - STBR_DEBUG_ASSERT(channels == 3); - - output_buffer[out_texel_index ] += input_buffer[input_texel_index ] * coefficient; - output_buffer[out_texel_index + 1] += input_buffer[input_texel_index + 1] * coefficient; - output_buffer[out_texel_index + 2] += input_buffer[input_texel_index + 2] * coefficient; -} - -static void stbr__output_decode_coefficients_4(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) -{ - STBR_DEBUG_ASSERT(channels == 4); - - output_buffer[out_texel_index ] += input_buffer[input_texel_index ] * coefficient; - output_buffer[out_texel_index + 1] += input_buffer[input_texel_index + 1] * coefficient; - output_buffer[out_texel_index + 2] += input_buffer[input_texel_index + 2] * coefficient; - output_buffer[out_texel_index + 3] += input_buffer[input_texel_index + 3] * coefficient; -} - -static void stbr__output_decode_coefficients_n(float* output_buffer, int out_texel_index, float* input_buffer, int input_texel_index, int channels, float coefficient) -{ - int c; - for (c = 0; c < channels; c++) - output_buffer[out_texel_index + c] += input_buffer[input_texel_index + c] * coefficient; -} - -static stbr__output_decode_coefficients stbr__get_output_decode_coefficients_function(int channels) -{ - if (channels == 1) - return &stbr__output_decode_coefficients_1; - else if (channels == 2) - return &stbr__output_decode_coefficients_2; - else if (channels == 3) - return &stbr__output_decode_coefficients_3; - else if (channels == 4) - return &stbr__output_decode_coefficients_4; - - return &stbr__output_decode_coefficients_n; -} static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, float* output_buffer) { @@ -766,8 +710,6 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; float* horizontal_coefficients = stbr_info->horizontal_coefficients; - stbr__output_decode_coefficients output_decode_coefficients_fn = stbr__get_output_decode_coefficients_function(channels); - for (x = 0; x < output_w; x++) { int n0 = horizontal_contributors[x].n0; @@ -789,7 +731,9 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo int in_texel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_index]; - output_decode_coefficients_fn(output_buffer, out_texel_index, decode_buffer, in_texel_index, channels, coefficient); + int c; + for (c = 0; c < channels; c++) + output_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; } } } @@ -807,8 +751,6 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, f int filter_texel_margin = stbr__get_filter_texel_margin(stbr_info->filter); int max_x = input_w + filter_texel_margin * 2; - stbr__output_decode_coefficients output_decode_coefficients_fn = stbr__get_output_decode_coefficients_function(channels); - STBR_DEBUG_ASSERT(!stbr__use_width_upsampling(stbr_info)); for (x = 0; x < max_x; x++) @@ -830,7 +772,9 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, f int out_texel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_index]; - output_decode_coefficients_fn(output_buffer, out_texel_index, decode_buffer, in_texel_index, channels, coefficient); + int c; + for (c = 0; c < channels; c++) + output_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; } } } @@ -1039,7 +983,6 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int output_row_index = n * stbr_info->output_stride_bytes; - stbr__output_decode_coefficients output_decode_coefficients_fn = stbr__get_output_decode_coefficients_function(channels); stbr__encode_scanline_channels encode_channels_fn = stbr__get_encode_channels_function(channels); stbr__encode_scanline_type_colorspace encode_type_colorspace_fn = stbr__get_encode_type_colorspace_function(stbr_info->type, stbr_info->colorspace); @@ -1063,7 +1006,9 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_texel_width, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_index]; - output_decode_coefficients_fn(encode_buffer, 0, ring_buffer_entry, in_texel_index, channels, coefficient); + int c; + for (c = 0; c < channels; c++) + encode_buffer[c] += ring_buffer_entry[in_texel_index + c] * coefficient; } encode_channels_fn(output_data, out_texel_index, encode_buffer, 0, channels, encode_type_colorspace_fn); @@ -1094,8 +1039,6 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int int n1 = vertical_contributors->n1; int max_n = stbr__min(n1, output_h - 1); - stbr__output_decode_coefficients output_decode_coefficients_fn = stbr__get_output_decode_coefficients_function(channels); - STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); STBR_DEBUG_ASSERT(n1 <= in_last_scanline); @@ -1113,7 +1056,9 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int { int in_texel_index = x * channels; - output_decode_coefficients_fn(ring_buffer_entry, in_texel_index, horizontal_buffer, in_texel_index, channels, coefficient); + int c; + for (c = 0; c < channels; c++) + ring_buffer_entry[in_texel_index + c] += horizontal_buffer[in_texel_index + c] * coefficient; } } } From d96c97298cbc25a4e113c718eb0eb69706178f5d Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 29 Jul 2014 16:22:22 -0700 Subject: [PATCH 040/168] This is still faster than the function pointer solution, and neater. --- stb_resample.h | 200 ++++++++++++++++----------------------------------------- 1 file changed, 54 insertions(+), 146 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 3e15400..6efcb13 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -597,7 +597,7 @@ static float* stbr__get_decode_buffer(stbr__info* stbr_info) return &stbr_info->decode_buffer[stbr__get_filter_texel_margin(stbr_info->filter) * stbr_info->channels]; } -#define DECODE(type, colorspace) ((type) * (STBR_MAX_COLORSPACES) + (colorspace)) +#define STBR__DECODE(type, colorspace) ((type) * (STBR_MAX_COLORSPACES) + (colorspace)) static void stbr__decode_scanline(stbr__info* stbr_info, int n) { @@ -612,7 +612,7 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) stbr_edge edge = stbr_info->edge; int in_buffer_row_index = stbr__edge_wrap(edge, n, stbr_info->input_h) * input_stride_bytes; int max_x = input_w + stbr__get_filter_texel_margin(stbr_info->filter); - int decode = DECODE(type, colorspace); + int decode = STBR__DECODE(type, colorspace); for (x = -stbr__get_filter_texel_margin(stbr_info->filter); x < max_x; x++) { @@ -621,42 +621,42 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) switch (decode) { - case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): + case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): for (int n = 0; n < channels; n++) decode_buffer[decode_texel_index + n] = ((float)((const unsigned char*)input_data)[input_texel_index + n]) / 255; break; - case DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): + case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): for (int n = 0; n < channels; n++) decode_buffer[decode_texel_index + n] = stbr__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_texel_index + n]]; break; - case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): + case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): for (int n = 0; n < channels; n++) decode_buffer[decode_texel_index + n] = ((float)((const unsigned short*)input_data)[input_texel_index + n]) / 65535; break; - case DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): + case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): for (int n = 0; n < channels; n++) decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((float)((const unsigned short*)input_data)[input_texel_index + n]) / 65535); break; - case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): + case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): for (int n = 0; n < channels; n++) decode_buffer[decode_texel_index + n] = ((float)((const unsigned int*)input_data)[input_texel_index + n]) / 4294967295; break; - case DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): + case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): for (int n = 0; n < channels; n++) decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((float)((const unsigned int*)input_data)[input_texel_index + n]) / 4294967295); break; - case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): + case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): for (int n = 0; n < channels; n++) decode_buffer[decode_texel_index + n] = ((const float*)input_data)[input_texel_index + n]; break; - case DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): + case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): for (int n = 0; n < channels; n++) decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((const float*)input_data)[input_texel_index + n]); break; @@ -668,8 +668,6 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) } } -#undef DECODE - static float* stbr__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) { return &ring_buffer[index * ring_buffer_length]; @@ -817,144 +815,54 @@ static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffe } -typedef void(*stbr__encode_scanline_type_colorspace)(const void* buffer, int offset, int channel, float value); - -static void stbr__encode_scanline_uchar_sRGB(const void* buffer, int offset, int channel, float value) +static stbr_inline void stbr__encode_scanline(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, int decode) { - ((unsigned char*)buffer)[offset + channel] = stbr__linear_uchar_to_srgb_uchar[(unsigned char)(stbr__saturate(value)*255)]; -} - -static void stbr__encode_scanline_uchar_linear(const void* buffer, int offset, int channel, float value) -{ - ((unsigned char*)buffer)[offset + channel] = (unsigned char)(stbr__saturate(value) * 255); -} - -static void stbr__encode_scanline_ushort_sRGB(const void* buffer, int offset, int channel, float value) -{ - ((unsigned short*)buffer)[offset + channel] = (unsigned short)stbr__linear_to_srgb((stbr__saturate(value) * 65535)); -} - -static void stbr__encode_scanline_ushort_linear(const void* buffer, int offset, int channel, float value) -{ - ((unsigned short*)buffer)[offset + channel] = (unsigned short)(stbr__saturate(value) * 65535); -} - -static void stbr__encode_scanline_uint_sRGB(const void* buffer, int offset, int channel, float value) -{ - ((unsigned int*)buffer)[offset + channel] = (unsigned int)stbr__linear_to_srgb((stbr__saturate(value) * 4294967295)); -} - -static void stbr__encode_scanline_uint_linear(const void* buffer, int offset, int channel, float value) -{ - ((unsigned int*)buffer)[offset + channel] = (unsigned int)(stbr__saturate(value) * 4294967295); -} - -static void stbr__encode_scanline_float_sRGB(const void* buffer, int offset, int channel, float value) -{ - ((float*)buffer)[offset + channel] = stbr__linear_to_srgb(stbr__saturate(value)); -} - -static void stbr__encode_scanline_float_linear(const void* buffer, int offset, int channel, float value) -{ - ((float*)buffer)[offset + channel] = stbr__saturate(value); -} - -typedef void(*stbr__encode_scanline_channels)(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace); - -static void stbr__encode_scanline_1(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace) -{ - STBR_DEBUG_ASSERT(channels == 1); - - encode_type_colorspace(output_buffer, output_texel_index, 0, encode_buffer[encode_texel_index]); -} - -static void stbr__encode_scanline_2(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace) -{ - STBR_DEBUG_ASSERT(channels == 2); - - encode_type_colorspace(output_buffer, output_texel_index, 0, encode_buffer[encode_texel_index]); - encode_type_colorspace(output_buffer, output_texel_index, 1, encode_buffer[encode_texel_index + 1]); -} - -static void stbr__encode_scanline_3(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace) -{ - STBR_DEBUG_ASSERT(channels == 3); - - encode_type_colorspace(output_buffer, output_texel_index, 0, encode_buffer[encode_texel_index]); - encode_type_colorspace(output_buffer, output_texel_index, 1, encode_buffer[encode_texel_index + 1]); - encode_type_colorspace(output_buffer, output_texel_index, 2, encode_buffer[encode_texel_index + 2]); -} - -static void stbr__encode_scanline_4(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace) -{ - STBR_DEBUG_ASSERT(channels == 4); - - encode_type_colorspace(output_buffer, output_texel_index, 0, encode_buffer[encode_texel_index]); - encode_type_colorspace(output_buffer, output_texel_index, 1, encode_buffer[encode_texel_index + 1]); - encode_type_colorspace(output_buffer, output_texel_index, 2, encode_buffer[encode_texel_index + 2]); - encode_type_colorspace(output_buffer, output_texel_index, 3, encode_buffer[encode_texel_index + 3]); -} - -static void stbr__encode_scanline_n(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, stbr__encode_scanline_type_colorspace encode_type_colorspace) -{ - int c; - for (c = 0; c < channels; c++) - encode_type_colorspace(output_buffer, output_texel_index, c, encode_buffer[encode_texel_index + c]); -} - -static stbr__encode_scanline_type_colorspace stbr__get_encode_type_colorspace_function(stbr_type type, stbr_colorspace colorspace) -{ - switch (type) + switch (decode) { - case STBR_TYPE_UINT8: - if (colorspace == STBR_COLORSPACE_LINEAR) - return stbr__encode_scanline_uchar_linear; - else if (colorspace == STBR_COLORSPACE_SRGB) - return stbr__encode_scanline_uchar_sRGB; + case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): + for (int n = 0; n < channels; n++) + ((unsigned char*)output_buffer)[output_texel_index + n] = (unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 255); break; - case STBR_TYPE_UINT16: - if (colorspace == STBR_COLORSPACE_LINEAR) - return stbr__encode_scanline_ushort_linear; - else if (colorspace == STBR_COLORSPACE_SRGB) - return stbr__encode_scanline_ushort_sRGB; + case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): + for (int n = 0; n < channels; n++) + ((unsigned char*)output_buffer)[output_texel_index + n] = stbr__linear_uchar_to_srgb_uchar[(unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 255)]; break; - case STBR_TYPE_UINT32: - if (colorspace == STBR_COLORSPACE_LINEAR) - return stbr__encode_scanline_uint_linear; - else if (colorspace == STBR_COLORSPACE_SRGB) - return stbr__encode_scanline_uint_sRGB; + case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): + for (int n = 0; n < channels; n++) + ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 65535); break; - case STBR_TYPE_FLOAT: - if (colorspace == STBR_COLORSPACE_LINEAR) - return stbr__encode_scanline_float_linear; - else if (colorspace == STBR_COLORSPACE_SRGB) - return stbr__encode_scanline_float_sRGB; + case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): + for (int n = 0; n < channels; n++) + ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)stbr__linear_to_srgb((stbr__saturate(encode_buffer[encode_texel_index + n]) * 65535)); + break; + + case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): + for (int n = 0; n < channels; n++) + ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 4294967295); + break; + + case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): + for (int n = 0; n < channels; n++) + ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)stbr__linear_to_srgb((stbr__saturate(encode_buffer[encode_texel_index + n]) * 4294967295)); + break; + + case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): + for (int n = 0; n < channels; n++) + ((float*)output_buffer)[output_texel_index + n] = stbr__saturate(encode_buffer[encode_texel_index + n]); + break; + + case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): + for (int n = 0; n < channels; n++) + ((float*)output_buffer)[output_texel_index + n] = stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n])); break; default: - STBR_UNIMPLEMENTED("Unknown type."); - return NULL; + STBR_UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; } - - STBR_UNIMPLEMENTED("Unknown color space."); - return NULL; -} - -static stbr__encode_scanline_channels stbr__get_encode_channels_function(int channels) -{ - if (channels == 1) - return &stbr__encode_scanline_1; - else if (channels == 2) - return &stbr__encode_scanline_2; - else if (channels == 3) - return &stbr__encode_scanline_3; - else if (channels == 4) - return &stbr__encode_scanline_4; - - return &stbr__encode_scanline_n; } static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) @@ -964,9 +872,12 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; float* vertical_coefficients = stbr_info->vertical_coefficients; int channels = stbr_info->channels; + int type = stbr_info->type; + int colorspace = stbr_info->colorspace; int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter); void* output_data = stbr_info->output_data; float* encode_buffer = stbr_info->encode_buffer; + int decode = STBR__DECODE(type, colorspace); float* ring_buffer = stbr_info->ring_buffer; int ring_buffer_begin_index = stbr_info->ring_buffer_begin_index; @@ -983,9 +894,6 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int output_row_index = n * stbr_info->output_stride_bytes; - stbr__encode_scanline_channels encode_channels_fn = stbr__get_encode_channels_function(channels); - stbr__encode_scanline_type_colorspace encode_type_colorspace_fn = stbr__get_encode_type_colorspace_function(stbr_info->type, stbr_info->colorspace); - STBR_DEBUG_ASSERT(stbr__use_height_upsampling(stbr_info)); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); STBR_DEBUG_ASSERT(n1 <= in_last_scanline); @@ -1011,7 +919,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i encode_buffer[c] += ring_buffer_entry[in_texel_index + c] * coefficient; } - encode_channels_fn(output_data, out_texel_index, encode_buffer, 0, channels, encode_type_colorspace_fn); + stbr__encode_scanline(output_data, out_texel_index, encode_buffer, 0, channels, decode); } } @@ -1120,15 +1028,15 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s { int output_stride_bytes = stbr_info->output_stride_bytes; int channels = stbr_info->channels; + int type = stbr_info->type; + int colorspace = stbr_info->colorspace; int output_w = stbr_info->output_w; void* output_data = stbr_info->output_data; + int decode = STBR__DECODE(type, colorspace); float* ring_buffer = stbr_info->ring_buffer; int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); - stbr__encode_scanline_channels encode_channels_fn = stbr__get_encode_channels_function(channels); - stbr__encode_scanline_type_colorspace encode_type_colorspace_fn = stbr__get_encode_type_colorspace_function(stbr_info->type, stbr_info->colorspace); - if (stbr_info->ring_buffer_begin_index >= 0) { // Get rid of whatever we don't need anymore. @@ -1148,7 +1056,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s int ring_texel_index = texel_index; int output_texel_index = output_row + texel_index; - encode_channels_fn(output_data, output_texel_index, ring_buffer_entry, ring_texel_index, channels, encode_type_colorspace_fn); + stbr__encode_scanline(output_data, output_texel_index, ring_buffer_entry, ring_texel_index, channels, decode); } } From 5b4090627162d4a57778fa09840b8de782369eed Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 29 Jul 2014 17:44:45 -0700 Subject: [PATCH 041/168] My perf testing code. --- tests/resample_test.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 5ec296a..a0fe3a6 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -56,7 +56,28 @@ int main(int argc, char** argv) STBR_ASSERT(in_w + border <= w); STBR_ASSERT(in_h + border <= h); +#ifdef PERF_TEST + struct timeb initial_time_millis, final_time_millis; + + long average = 0; + for (int j = 0; j < 10; j++) + { + ftime(&initial_time_millis); + for (int i = 0; i < 100; i++) + stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + ftime(&final_time_millis); + long lapsed_ms = (long)(final_time_millis.time - initial_time_millis.time) * 1000 + (final_time_millis.millitm - initial_time_millis.millitm); + printf("Resample: %dms\n", lapsed_ms); + + average += lapsed_ms; + } + + average /= 10; + + printf("Average: %dms\n", average); +#else stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); +#endif free(extra_memory); From 68f93b72d5de533b2a873f3b586521a02d0fdc2f Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 29 Jul 2014 20:18:28 -0700 Subject: [PATCH 042/168] Update documentation and add helper functions. --- stb_resample.h | 207 +++++++++++++++++++++++++++++++++++++++--------- tests/resample_test.cpp | 2 +- 2 files changed, 170 insertions(+), 39 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 6efcb13..c89c254 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -1,29 +1,65 @@ -/* stb_resample - v0.1 - public domain image resampling -no warranty implied; use at your own risk +/* stb_resample - v0.50 - public domain image resampling + no warranty implied; use at your own risk -Do this: -#define STB_RESAMPLE_IMPLEMENTATION -before you include this file in *one* C or C++ file to create the implementation. + Do this: + #define STB_RESAMPLE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. -#define STBR_ASSERT(x) to avoid using assert.h. + #define STBR_ASSERT(x) to avoid using assert.h. -Latest revisions: + #define STBR_NO_MALLOC to avoid using stdlib.h and malloc. This will remove + all resize functions except stbr_resize_arbitrary() from the API. -See end of file for full revision history. + QUICK NOTES: + Written with emphasis on usage and speed. Only the resize operation is + currently supported, no rotations or translations. -Initial implementation by Jorge L Rodriguez + Supports arbitrary resize for separable filters. For a list of + supported filters see the stbr_filter enum. To add a new filter, + write a filter function and add it to stbr__filter_info_table. + + Latest revisions: + 0.50 (2014-07-29) first released version + + See end of file for full revision history. + + TODO: + Installable filters + Specify with (s0, t0) X (s1, t1) what area of the source image to use, + at sub-pixel level + 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 ) + + Initial implementation by Jorge L Rodriguez, @VinoBS */ #ifndef STBR_INCLUDE_STB_RESAMPLE_H #define STBR_INCLUDE_STB_RESAMPLE_H // Basic usage: -// result = stbr_resize(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, channels, alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_BILINEAR, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); +// result = stbr_resize_srgb_uint8(input_data, input_w, input_h, output_data, output_w, output_h, channels, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); // // input_data is your supplied texels. -// output_data will be the resized texels. It should be of size output_w * output_h * input_components (or output_h * output_stride if you provided a stride.) -// If input_stride or output_stride is 0 (as in this example) the stride will be automatically calculated as width*components. -// Returned result is 1 for success or 0 in case of an error. +// output_data will be the resized texels. It should be of size output_w * output_h * channels +// Returned result is 1 for success or 0 in case of an error. Currently the only error is failure to allocate memory. +// If you're unsure of which filter to use, Catmull-Rom is a good upsampling filter and Mitchell is a good downsampling filter. + + +// Advanced usage: +// size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, channels, STBR_FILTER_CATMULLROM); +// void* extra_memory = malloc(memory_required); // Any memory allocation method of your choosing +// result = stbr_resize_arbitrary(input_data, input_w, input_h, input_stride_in_bytes, +// output_data, output_w, output_h, output_stride_in_bytes, +// channels, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, +// extra_memory, memory_required); +// free(extra_memory); +// +// input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. +// Returned result is 1 for success or 0 in case of an error. Currently the only error is that the memory passed in is insufficient. +// stbr_resize_arbitrary() will not allocate any memory, it will use the memory you pass in to do its work. + typedef enum { @@ -61,7 +97,17 @@ typedef enum #define STBR_MAX_TYPES 4 -typedef unsigned char stbr_uc; +typedef unsigned char stbr_uint8; + +#ifdef _MSC_VER +typedef unsigned short stbr_uint16; +typedef unsigned int stbr_uint32; +#else +#include +typedef uint16_t stbr_uint16; +typedef uint32_t stbr_uint32; +#endif + typedef unsigned int stbr_size_t; // to avoid including a header for size_t #ifdef __cplusplus @@ -74,18 +120,40 @@ extern "C" { #define STBRDEF extern #endif +#ifndef STBR_NO_MALLOC + ////////////////////////////////////////////////////////////////////////////// // - // PRIMARY API - resize an image + // PRIMARY API - sRGB type-safe image resizing. // - STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int input_stride_in_bytes, - int output_w, int output_h, int output_stride_in_bytes, - int channels, stbr_filter filter); + STBRDEF int stbr_resize_srgb_uint8(const stbr_uint8* input_data, int input_w, int input_h, + stbr_uint8* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge); + + STBRDEF int stbr_resize_srgb_uint16(const stbr_uint16* input_data, int input_w, int input_h, + stbr_uint16* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge); + + STBRDEF int stbr_resize_srgb_uint32(const stbr_uint32* input_data, int input_w, int input_h, + stbr_uint32* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge); + + STBRDEF int stbr_resize_srgb_float(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge); + +#endif // STBR_NO_MALLOC + + ////////////////////////////////////////////////////////////////////////////// + // + // ADVANCED API + // + + STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w, int output_h, int channels, stbr_filter filter); STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, - //int channels, int alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, int channels, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes); @@ -119,9 +187,12 @@ extern "C" { // For memset #include - #include +#ifndef STBR_NO_MALLOC +#include +#endif + #ifndef _MSC_VER #ifdef __cplusplus @@ -134,21 +205,8 @@ extern "C" { #endif -#ifdef _MSC_VER -typedef unsigned short stbr__uint16; -typedef signed short stbr__int16; -typedef unsigned int stbr__uint32; -typedef signed int stbr__int32; -#else -#include -typedef uint16_t stbr__uint16; -typedef int16_t stbr__int16; -typedef uint32_t stbr__uint32; -typedef int32_t stbr__int32; -#endif - // should produce compiler error if size is wrong -typedef unsigned char stbr__validate_uint32[sizeof(stbr__uint32) == 4 ? 1 : -1]; +typedef unsigned char stbr__validate_uint32[sizeof(stbr_uint32) == 4 ? 1 : -1]; #ifdef _MSC_VER #define STBR_NOTUSED(v) (void)(v) @@ -1142,7 +1200,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input if (!tempmem) return 0; - if (tempmem_size_in_bytes < stbr_calculate_memory(input_w, input_h, input_stride_in_bytes, output_w, output_h, output_stride_in_bytes, channels, STBR_FILTER_NEAREST)) + if (tempmem_size_in_bytes < stbr_calculate_memory(input_w, input_h, output_w, output_h, channels, filter)) return 0; memset(tempmem, 0, tempmem_size_in_bytes); @@ -1213,9 +1271,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input } -STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int input_stride_in_bytes, - int output_w, int output_h, int output_stride_in_bytes, - int channels, stbr_filter filter) +STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w, int output_h, int channels, stbr_filter filter) { STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); @@ -1244,8 +1300,83 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int input_st return info_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size; } +#ifndef STBR_NO_MALLOC + +STBRDEF int stbr_resize_srgb_uint8(const stbr_uint8* input_data, int input_w, int input_h, + stbr_uint8* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, channels, filter); + void* extra_memory = malloc(memory_required); + + if (!extra_memory) + return 0; + + int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, channels, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + free(extra_memory); + + return result; +} + +STBRDEF int stbr_resize_srgb_uint16(const stbr_uint16* input_data, int input_w, int input_h, + stbr_uint16* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, channels, filter); + void* extra_memory = malloc(memory_required); + + if (!extra_memory) + return 0; + + int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, channels, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + free(extra_memory); + + return result; +} + +STBRDEF int stbr_resize_srgb_uint32(const stbr_uint32* input_data, int input_w, int input_h, + stbr_uint32* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, channels, filter); + void* extra_memory = malloc(memory_required); + + if (!extra_memory) + return 0; + + int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, channels, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + free(extra_memory); + + return result; +} + +STBRDEF int stbr_resize_srgb_float(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, channels, filter); + void* extra_memory = malloc(memory_required); + + if (!extra_memory) + return 0; + + int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, channels, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + free(extra_memory); + + return result; +} + +#endif // STBR_NO_MALLOC + + #endif // STB_RESAMPLE_IMPLEMENTATION /* revision history: + 0.50 (2014-07-29) + first released version */ diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index a0fe3a6..645846c 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -48,7 +48,7 @@ int main(int argc, char** argv) int in_w = 512; int in_h = 512; - size_t memory_required = stbr_calculate_memory(in_w, in_h, w*n, out_w, out_h, out_stride, n, STBR_FILTER_CATMULLROM); + size_t memory_required = stbr_calculate_memory(in_w, in_h, out_w, out_h, n, STBR_FILTER_CATMULLROM); void* extra_memory = malloc(memory_required); // Cut out the outside 64 pixels all around to test the stride. From ebe0473d8be0088faa8d22cc18d9b0f105abc54a Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 29 Jul 2014 22:50:06 -0700 Subject: [PATCH 043/168] Add a test suite to do a bunch of different resizes to find problems. One problem found was an incorrect calculation of texel support how many margin texels needed when downsampling. When downsampling we need to spread out the support of each contributing texel, so to compensate you need more margin texels. --- stb_resample.h | 109 +++++++++++++++++++++++++++--------------------- tests/resample_test.cpp | 59 ++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 48 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index c89c254..a563210 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -408,19 +408,22 @@ stbr_inline static int stbr__use_height_upsampling(stbr__info* stbr_info) // This is the maximum number of input samples that can affect an output sample // with the given filter -stbr_inline static int stbr__get_filter_texel_width(stbr_filter filter) +stbr_inline static int stbr__get_filter_texel_width(stbr_filter filter, int input_w, int output_w) { STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); - return (int)ceil(stbr__filter_info_table[filter].support * 2); + if (stbr__use_height_upsampling_noinfo(output_w, input_w)) + return (int)ceil(stbr__filter_info_table[filter].support * 2); + else + return (int)ceil(stbr__filter_info_table[filter].support * 2 * input_w / output_w); } // This is how much to expand buffers to account for filters seeking outside // the image boundaries. -stbr_inline static int stbr__get_filter_texel_margin(stbr_filter filter) +stbr_inline static int stbr__get_filter_texel_margin(stbr_filter filter, int input_w, int output_w) { - return stbr__get_filter_texel_width(filter) / 2; + return stbr__get_filter_texel_width(filter, input_w, output_w) / 2; } stbr_inline static int stbr__get_horizontal_contributors(stbr_filter filter, int input_w, int output_w) @@ -428,12 +431,12 @@ stbr_inline static int stbr__get_horizontal_contributors(stbr_filter filter, int if (stbr__use_width_upsampling_noinfo(output_w, input_w)) return output_w; else - return (input_w + stbr__get_filter_texel_margin(filter) * 2); + return (input_w + stbr__get_filter_texel_margin(filter, input_w, output_w) * 2); } stbr_inline static int stbr__get_total_coefficients(stbr_filter filter, int input_w, int output_w) { - return stbr__get_horizontal_contributors(filter, input_w, output_w) * stbr__get_filter_texel_width(filter); + return stbr__get_horizontal_contributors(filter, input_w, output_w) * stbr__get_filter_texel_width(filter, input_w, output_w); } stbr_inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_info, int n) @@ -444,7 +447,7 @@ stbr_inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_in stbr_inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, int c) { - return &stbr_info->horizontal_coefficients[stbr__get_filter_texel_width(stbr_info->filter)*n + c]; + return &stbr_info->horizontal_coefficients[stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)*n + c]; } stbr_inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) @@ -538,12 +541,14 @@ static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_ float filter_scale; stbr_filter filter = stbr_info->filter; - STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr__get_filter_texel_width(filter)); + STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr__get_filter_texel_width(filter, stbr_info->input_w, stbr_info->output_w)); STBR_DEBUG_ASSERT(in_last_texel < stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); contributor->n0 = in_first_texel; contributor->n1 = in_last_texel; + STBR_DEBUG_ASSERT(contributor->n1 >= contributor->n0); + for (i = 0; i <= in_last_texel - in_first_texel; i++) { float in_texel_center = (float)(i + in_first_texel) + 0.5f; @@ -565,12 +570,14 @@ static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float int i; stbr_filter filter = stbr_info->filter; - STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr__get_filter_texel_width(filter)); + STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr__get_filter_texel_width(filter, stbr_info->input_w, stbr_info->output_w)); STBR_DEBUG_ASSERT(out_last_texel < stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); contributor->n0 = out_first_texel; contributor->n1 = out_last_texel; + STBR_DEBUG_ASSERT(contributor->n1 >= contributor->n0); + for (i = 0; i <= out_last_texel - out_first_texel; i++) { float in_texel_center = (float)(i + out_first_texel) + 0.5f; @@ -628,14 +635,14 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) } else { - float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support; + float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; // Looping through in texels for (n = 0; n < total_contributors; n++) { float out_center_of_in; // Center of the current out texel in the in texel space int out_first_texel, out_last_texel; - int n_adjusted = n - stbr__get_filter_texel_margin(stbr_info->filter); + int n_adjusted = n - stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); stbr__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, &out_first_texel, &out_last_texel, &out_center_of_in); @@ -652,7 +659,7 @@ static float* stbr__get_decode_buffer(stbr__info* stbr_info) { // The 0 index of the decode buffer starts after the margin. This makes // it okay to use negative indexes on the decode buffer. - return &stbr_info->decode_buffer[stbr__get_filter_texel_margin(stbr_info->filter) * stbr_info->channels]; + return &stbr_info->decode_buffer[stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w) * stbr_info->channels]; } #define STBR__DECODE(type, colorspace) ((type) * (STBR_MAX_COLORSPACES) + (colorspace)) @@ -669,10 +676,10 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) float* decode_buffer = stbr__get_decode_buffer(stbr_info); stbr_edge edge = stbr_info->edge; int in_buffer_row_index = stbr__edge_wrap(edge, n, stbr_info->input_h) * input_stride_bytes; - int max_x = input_w + stbr__get_filter_texel_margin(stbr_info->filter); + int max_x = input_w + stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); int decode = STBR__DECODE(type, colorspace); - for (x = -stbr__get_filter_texel_margin(stbr_info->filter); x < max_x; x++) + for (x = -stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); x < max_x; x++) { int decode_texel_index = x * channels; int input_texel_index = in_buffer_row_index + stbr__edge_wrap(edge, x, input_w) * channels; @@ -743,7 +750,7 @@ static float* stbr__add_empty_ring_buffer_entry(stbr__info* stbr_info, int n) } else { - ring_buffer_index = (stbr_info->ring_buffer_begin_index + (stbr_info->ring_buffer_last_scanline - stbr_info->ring_buffer_first_scanline) + 1) % stbr__get_filter_texel_width(stbr_info->filter); + ring_buffer_index = (stbr_info->ring_buffer_begin_index + (stbr_info->ring_buffer_last_scanline - stbr_info->ring_buffer_first_scanline) + 1) % stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); STBR_DEBUG_ASSERT(ring_buffer_index != stbr_info->ring_buffer_begin_index); } @@ -760,7 +767,7 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo { int x, k; int output_w = stbr_info->output_w; - int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter); + int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); int channels = stbr_info->channels; float* decode_buffer = stbr__get_decode_buffer(stbr_info); stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; @@ -776,10 +783,10 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo int coefficient_counter = 0; STBR_DEBUG_ASSERT(n1 >= n0); - STBR_DEBUG_ASSERT(n0 >= -stbr__get_filter_texel_margin(stbr_info->filter)); - STBR_DEBUG_ASSERT(n1 >= -stbr__get_filter_texel_margin(stbr_info->filter)); - STBR_DEBUG_ASSERT(n0 < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter)); - STBR_DEBUG_ASSERT(n1 < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter)); + STBR_DEBUG_ASSERT(n0 >= -stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); + STBR_DEBUG_ASSERT(n1 >= -stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); + STBR_DEBUG_ASSERT(n0 < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); + STBR_DEBUG_ASSERT(n1 < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); for (k = n0; k <= n1; k++) { @@ -799,12 +806,12 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, f int x, k; int input_w = stbr_info->input_w; int output_w = stbr_info->output_w; - int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter); + int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); int channels = stbr_info->channels; float* decode_buffer = stbr__get_decode_buffer(stbr_info); stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; float* horizontal_coefficients = stbr_info->horizontal_coefficients; - int filter_texel_margin = stbr__get_filter_texel_margin(stbr_info->filter); + int filter_texel_margin = stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); int max_x = input_w + filter_texel_margin * 2; STBR_DEBUG_ASSERT(!stbr__use_width_upsampling(stbr_info)); @@ -932,7 +939,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int channels = stbr_info->channels; int type = stbr_info->type; int colorspace = stbr_info->colorspace; - int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter); + int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); void* output_data = stbr_info->output_data; float* encode_buffer = stbr_info->encode_buffer; int decode = STBR__DECODE(type, colorspace); @@ -989,7 +996,7 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; float* vertical_coefficients = stbr_info->vertical_coefficients; int channels = stbr_info->channels; - int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter); + int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); void* output_data = stbr_info->output_data; float* horizontal_buffer = stbr_info->horizontal_buffer; @@ -1044,9 +1051,9 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) stbr__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, &in_first_scanline, &in_last_scanline, &in_center_of_out); - STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr__get_filter_texel_width(stbr_info->filter)); - STBR_DEBUG_ASSERT(in_first_scanline >= -stbr__get_filter_texel_margin(stbr_info->filter)); - STBR_DEBUG_ASSERT(in_last_scanline < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter)); + STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h)); + STBR_DEBUG_ASSERT(in_first_scanline >= -stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h)); + STBR_DEBUG_ASSERT(in_last_scanline < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h)); if (stbr_info->ring_buffer_begin_index >= 0) { @@ -1065,7 +1072,7 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) else { stbr_info->ring_buffer_first_scanline++; - stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_texel_width(stbr_info->filter); + stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); } } } @@ -1130,7 +1137,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s else { stbr_info->ring_buffer_first_scanline++; - stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_texel_width(stbr_info->filter); + stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); } } } @@ -1141,20 +1148,20 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) int y; float scale_ratio = (float)stbr_info->output_h / stbr_info->input_h; float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support; - int max_y = stbr_info->input_h + stbr__get_filter_texel_margin(stbr_info->filter); + int max_y = stbr_info->input_h + stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); - for (y = -stbr__get_filter_texel_margin(stbr_info->filter); y < max_y; y++) + for (y = -stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); y < max_y; y++) { float out_center_of_in; // Center of the current out scanline in the in scanline space int out_first_scanline, out_last_scanline; stbr__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, &out_first_scanline, &out_last_scanline, &out_center_of_in); - STBR_DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbr__get_filter_texel_width(stbr_info->filter)); - STBR_DEBUG_ASSERT(out_first_scanline >= -2*stbr__get_filter_texel_margin(stbr_info->filter)); - STBR_DEBUG_ASSERT(out_last_scanline < stbr_info->input_w + 2*stbr__get_filter_texel_margin(stbr_info->filter)); + STBR_DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h)); + STBR_DEBUG_ASSERT(out_first_scanline >= -2 * stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h)); + STBR_DEBUG_ASSERT(out_last_scanline < stbr_info->input_w + 2 * stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h)); stbr__empty_ring_buffer(stbr_info, out_first_scanline); @@ -1183,13 +1190,17 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w; #ifdef STBR_DEBUG_OVERWRITE_TEST -#define OVERWRITE_ARRAY_SIZE 64 - unsigned char overwrite_output_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_pre[OVERWRITE_ARRAY_SIZE]; +#define OVERWRITE_ARRAY_SIZE 8 + unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; stbr_size_t begin_forbidden = width_stride_output * (output_h - 1) + output_w * channels; - memcpy(overwrite_output_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); #endif STBR_UNIMPLEMENTED(type != STBR_TYPE_UINT8); @@ -1224,20 +1235,20 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->colorspace = colorspace; stbr_info->ring_buffer_length_bytes = output_w * channels * sizeof(float); - stbr_info->decode_buffer_texels = input_w + stbr__get_filter_texel_margin(filter) * 2; + stbr_info->decode_buffer_texels = input_w + stbr__get_filter_texel_margin(filter, input_w, output_w) * 2; #define STBR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) stbr_info->horizontal_contributors = STBR__NEXT_MEMPTR(stbr_info, sizeof(stbr__info), stbr__contributors); stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, stbr__get_horizontal_contributors(filter, input_w, output_w) * sizeof(stbr__contributors), float); stbr_info->vertical_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr__get_total_coefficients(filter, input_w, output_w) * sizeof(float), float); - stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr__get_filter_texel_width(filter) * sizeof(float), float); + stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr__get_filter_texel_width(filter, input_h, output_h) * sizeof(float), float); if (stbr__use_height_upsampling(stbr_info)) { stbr_info->horizontal_buffer = NULL; stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, stbr_info->decode_buffer_texels * channels * sizeof(float), float); - stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width(filter), float); + stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width(filter, input_w, output_w), float); STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->encode_buffer, stbr_info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } @@ -1247,7 +1258,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->horizontal_buffer, output_w * channels * sizeof(float), float); stbr_info->encode_buffer = NULL; - STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width(filter), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width(filter, input_h, output_h), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } #undef STBR__NEXT_MEMPTR @@ -1263,8 +1274,10 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr__buffer_loop_downsample(stbr_info); #ifdef STBR_DEBUG_OVERWRITE_TEST - STBR_DEBUG_ASSERT(memcmp(overwrite_output_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); - STBR_DEBUG_ASSERT(memcmp(overwrite_tempmem_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); + STBR_DEBUG_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBR_DEBUG_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); + STBR_DEBUG_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBR_DEBUG_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); #endif return 1; @@ -1276,15 +1289,15 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); - int texel_margin = stbr__get_filter_texel_margin(filter); + int texel_margin = stbr__get_filter_texel_margin(filter, input_w, output_w); int info_size = sizeof(stbr__info); int contributors_size = stbr__get_horizontal_contributors(filter, input_w, output_w) * sizeof(stbr__contributors); int horizontal_coefficients_size = stbr__get_total_coefficients(filter, input_w, output_w) * sizeof(float); - int vertical_coefficients_size = stbr__get_filter_texel_width(filter) * sizeof(float); + int vertical_coefficients_size = stbr__get_filter_texel_width(filter, input_h, output_h) * sizeof(float); int decode_buffer_size = (input_w + texel_margin*2) * channels * sizeof(float); int horizontal_buffer_size = output_w * channels * sizeof(float); - int ring_buffer_size = output_w * channels * sizeof(float) * stbr__get_filter_texel_width(filter); + int ring_buffer_size = output_w * channels * sizeof(float) * stbr__get_filter_texel_width(filter, input_h, output_h); int encode_buffer_size = channels * sizeof(float); if (stbr__use_height_upsampling_noinfo(output_h, input_h)) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 645846c..b446442 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -18,6 +18,8 @@ #include #endif +void test_suite(); + int main(int argc, char** argv) { unsigned char* input_data; @@ -26,6 +28,11 @@ int main(int argc, char** argv) int n; int out_w, out_h, out_stride; +#if 1 + test_suite(); + return 0; +#endif + if (argc <= 1) { printf("No input image\n"); @@ -87,3 +94,55 @@ int main(int argc, char** argv) return 0; } + +void resize_image(const char* filename, float width_percent, float height_percent, stbr_filter filter, stbr_edge edge, const char* output_filename) +{ + int w, h, n; + + unsigned char* input_data = stbi_load(filename, &w, &h, &n, 0); + if (!input_data) + { + printf("Input image could not be loaded"); + return; + } + + int out_w = (int)(w * width_percent); + int out_h = (int)(h * height_percent); + + unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); + + size_t memory_required = stbr_calculate_memory(w, h, out_w, out_h, n, filter); + void* extra_memory = malloc(memory_required); + + stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, n, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + free(extra_memory); + + stbi_write_png(output_filename, out_w, out_h, n, output_data, 0); + + free(output_data); +} + +void test_suite() +{ + // sRGB tests + resize_image("gamma_colors.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, "test-output/gamma_colors.jpg"); + resize_image("gamma_2.2.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, "test-output/gamma_2.2.jpg"); + resize_image("gamma_dalai_lama_gray.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, "test-output/gamma_dalai_lama_gray.jpg"); + + for (int i = 10; i < 100; i++) + { + char outname[200]; + sprintf(outname, "test-output/barbara-width-%d.jpg", i); + resize_image("barbara.png", (float)i / 100, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); + } + + for (int i = 110; i < 1000; i += 10) + { + char outname[200]; + sprintf(outname, "test-output/barbara-width-%d.jpg", i); + resize_image("barbara.png", (float)i / 100, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); + } +} + + From 043fa28c111b9bf2bc7799193e5a879c121b5aff Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 29 Jul 2014 23:02:56 -0700 Subject: [PATCH 044/168] Same deal with height. --- stb_resample.h | 2 +- tests/resample_test.cpp | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index a563210..c6eb1f3 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -1147,7 +1147,7 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) { int y; float scale_ratio = (float)stbr_info->output_h / stbr_info->input_h; - float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support; + float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; int max_y = stbr_info->input_h + stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index b446442..bd40333 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -137,12 +137,26 @@ void test_suite() resize_image("barbara.png", (float)i / 100, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); } - for (int i = 110; i < 1000; i += 10) + for (int i = 110; i < 500; i += 10) { char outname[200]; sprintf(outname, "test-output/barbara-width-%d.jpg", i); resize_image("barbara.png", (float)i / 100, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); } + + for (int i = 10; i < 100; i++) + { + char outname[200]; + sprintf(outname, "test-output/barbara-height-%d.jpg", i); + resize_image("barbara.png", 1, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); + } + + for (int i = 110; i < 500; i += 10) + { + char outname[200]; + sprintf(outname, "test-output/barbara-height-%d.jpg", i); + resize_image("barbara.png", 1, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); + } } From 7ead9a748d46057ad6ad8236aeab224f64a57de7 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 29 Jul 2014 23:09:41 -0700 Subject: [PATCH 045/168] Fix. Ring buffers are a height value. --- stb_resample.h | 2 +- tests/resample_test.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/stb_resample.h b/stb_resample.h index c6eb1f3..fda8080 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -1248,7 +1248,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input { stbr_info->horizontal_buffer = NULL; stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, stbr_info->decode_buffer_texels * channels * sizeof(float), float); - stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width(filter, input_w, output_w), float); + stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width(filter, input_h, output_h), float); STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->encode_buffer, stbr_info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index bd40333..30787c8 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -157,6 +157,13 @@ void test_suite() sprintf(outname, "test-output/barbara-height-%d.jpg", i); resize_image("barbara.png", 1, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); } + + for (int i = 50; i < 200; i += 10) + { + char outname[200]; + sprintf(outname, "test-output/barbara-width-height-%d.jpg", i); + resize_image("barbara.png", 100 / (float)i, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); + } } From 1fcbe0daaf0bc54d22910d7efe01320bcc8421fa Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 30 Jul 2014 00:16:13 -0700 Subject: [PATCH 046/168] Fix shorts. Add test cases for shorts. --- stb_resample.h | 32 ++++++++++++++++++-------------- tests/resample_test.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index fda8080..ac2a0c8 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -92,7 +92,7 @@ typedef enum STBR_TYPE_UINT16 = 2, STBR_TYPE_UINT32 = 3, STBR_TYPE_FLOAT = 4, - // If you add here, update STBR_MAX_TYPES + // If you add here, update STBR_MAX_TYPES and stbr__type_size } stbr_type; #define STBR_MAX_TYPES 4 @@ -303,6 +303,14 @@ static unsigned char stbr__linear_uchar_to_srgb_uchar[256] = { 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255 }; +static unsigned char stbr__type_size[] = { + 0, + 1, // STBR_TYPE_UINT8 + 2, // STBR_TYPE_UINT16 + 4, // STBR_TYPE_UINT32 + 4, // STBR_TYPE_FLOAT +}; + float stbr__srgb_to_linear(float f) { if (f <= 0.04045f) @@ -671,11 +679,11 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) int type = stbr_info->type; int colorspace = stbr_info->colorspace; int input_w = stbr_info->input_w; - int input_stride_bytes = stbr_info->input_stride_bytes; + int input_stride = stbr_info->input_stride_bytes / stbr__type_size[stbr_info->type]; const void* input_data = stbr_info->input_data; float* decode_buffer = stbr__get_decode_buffer(stbr_info); stbr_edge edge = stbr_info->edge; - int in_buffer_row_index = stbr__edge_wrap(edge, n, stbr_info->input_h) * input_stride_bytes; + int in_buffer_row_index = stbr__edge_wrap(edge, n, stbr_info->input_h) * input_stride; int max_x = input_w + stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); int decode = STBR__DECODE(type, colorspace); @@ -901,7 +909,7 @@ static stbr_inline void stbr__encode_scanline(void* output_buffer, int output_te case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): for (int n = 0; n < channels; n++) - ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)stbr__linear_to_srgb((stbr__saturate(encode_buffer[encode_texel_index + n]) * 65535)); + ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n])) * 65535); break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): @@ -911,7 +919,7 @@ static stbr_inline void stbr__encode_scanline(void* output_buffer, int output_te case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): for (int n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)stbr__linear_to_srgb((stbr__saturate(encode_buffer[encode_texel_index + n]) * 4294967295)); + ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n])) * 4294967295); break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): @@ -1091,7 +1099,7 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_scanline) { - int output_stride_bytes = stbr_info->output_stride_bytes; + int output_stride = stbr_info->output_stride_bytes / stbr__type_size[stbr_info->type]; int channels = stbr_info->channels; int type = stbr_info->type; int colorspace = stbr_info->colorspace; @@ -1107,12 +1115,10 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s // Get rid of whatever we don't need anymore. while (first_necessary_scanline > stbr_info->ring_buffer_first_scanline) { - STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); - if (stbr_info->ring_buffer_first_scanline >= 0 && stbr_info->ring_buffer_first_scanline < stbr_info->output_h) { int x; - int output_row = stbr_info->ring_buffer_first_scanline * output_stride_bytes; + int output_row = stbr_info->ring_buffer_first_scanline * output_stride; float* ring_buffer_entry = stbr__get_ring_buffer_entry(ring_buffer, stbr_info->ring_buffer_begin_index, ring_buffer_length); for (x = 0; x < output_w; x++) @@ -1186,8 +1192,8 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input int channels, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes) { - int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w; - int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w; + int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w * stbr__type_size[type]; + int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w * stbr__type_size[type]; #ifdef STBR_DEBUG_OVERWRITE_TEST #define OVERWRITE_ARRAY_SIZE 8 @@ -1196,15 +1202,13 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; - stbr_size_t begin_forbidden = width_stride_output * (output_h - 1) + output_w * channels; + stbr_size_t begin_forbidden = width_stride_output * (output_h - 1) + output_w * channels * stbr__type_size[type]; memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); #endif - STBR_UNIMPLEMENTED(type != STBR_TYPE_UINT8); - STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 30787c8..c4c457d 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -87,6 +87,7 @@ int main(int argc, char** argv) #endif free(extra_memory); + stbi_image_free(input_data); stbi_write_png("output.png", out_w, out_h, n, output_data, out_stride); @@ -117,12 +118,21 @@ void resize_image(const char* filename, float width_percent, float height_percen stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, n, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); free(extra_memory); + stbi_image_free(input_data); stbi_write_png(output_filename, out_w, out_h, n, output_data, 0); free(output_data); } +template +void convert_image(const F* input, T* output, int length) +{ + float f = (pow(2.0f, 8.0f * sizeof(T)) - 1) / (pow(2.0f, 8.0f * sizeof(F)) - 1); + for (int i = 0; i < length; i++) + output[i] = (T)(((float)input[i]) * f); +} + void test_suite() { // sRGB tests @@ -164,6 +174,29 @@ void test_suite() sprintf(outname, "test-output/barbara-width-height-%d.jpg", i); resize_image("barbara.png", 100 / (float)i, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); } + + { + int w, h, n; + unsigned char* input_data = stbi_load("barbara.png", &w, &h, &n, 0); + + unsigned short* short_data = (unsigned short*)malloc(w * h * n * sizeof(unsigned short)); + convert_image(input_data, short_data, w * h * n); + + unsigned short* output_data = (unsigned short*)malloc(w * h * n * sizeof(unsigned short)); + + stbr_resize_srgb_uint16(short_data, w, h, output_data, w * 2, h / 2, n, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); + + free(short_data); + stbi_image_free(input_data); + + char* char_data = (char*)malloc(w * h * n * sizeof(char)); + convert_image(output_data, char_data, w * h * n); + + stbi_write_png("test-output/barbara-short.png", w * 2, h / 2, n, char_data, 0); + + free(char_data); + free(output_data); + } } From 11897fbf9698ad0c992a401f161b0f08bd967790 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 30 Jul 2014 00:34:25 -0700 Subject: [PATCH 047/168] More fixing shorts. --- stb_resample.h | 7 ++---- tests/resample_test.cpp | 65 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index ac2a0c8..be08db1 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -610,9 +610,8 @@ static void stbr__check_downsample_coefficients(stbr__info* stbr_info) break; } - STBR_DEBUG_ASSERT(stbr_info->type == STBR_TYPE_UINT8); // Assert below should be 1 + 1/(2^n-1) where n is bits per int. STBR_DEBUG_ASSERT(total > 0.9f); - STBR_DEBUG_ASSERT(total <= 1.0f + 1.0f / 255); + STBR_DEBUG_ASSERT(total <= 1.0f + 1.0f / (pow(2.0f, 8.0f * stbr__type_size[stbr_info->type]) - 1)); } } #endif @@ -958,14 +957,12 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); - STBR_UNIMPLEMENTED(stbr_info->type != STBR_TYPE_UINT8); - stbr__calculate_coefficients_upsample(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); int n0 = vertical_contributors->n0; int n1 = vertical_contributors->n1; - int output_row_index = n * stbr_info->output_stride_bytes; + int output_row_index = n * stbr_info->output_stride_bytes / stbr__type_size[type]; STBR_DEBUG_ASSERT(stbr__use_height_upsampling(stbr_info)); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index c4c457d..8a19de8 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -133,6 +133,39 @@ void convert_image(const F* input, T* output, int length) output[i] = (T)(((float)input[i]) * f); } +template +void test_format(const char* file, float width_percent, float height_percent, stbr_type type, stbr_colorspace colorspace) +{ + int w, h, n; + unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + + int new_w = (int)(w * width_percent); + int new_h = (int)(h * height_percent); + + T* T_data = (T*)malloc(w * h * n * sizeof(T)); + convert_image(input_data, T_data, w * h * n); + + T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); + + size_t required = stbr_calculate_memory(w, h, new_w, new_h, n, STBR_FILTER_CATMULLROM); + void* extra_memory = malloc(required); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, n, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); + free(extra_memory); + + free(T_data); + stbi_image_free(input_data); + + char* char_data = (char*)malloc(new_w * new_h * n * sizeof(char)); + convert_image(output_data, char_data, new_w * new_h * n); + + char output[200]; + sprintf(output, "test-output/type-%d-%d-%d-%d-%s", type, colorspace, new_w, new_h, file); + stbi_write_png(output, new_w, new_h, n, char_data, 0); + + free(char_data); + free(output_data); +} + void test_suite() { // sRGB tests @@ -175,28 +208,20 @@ void test_suite() resize_image("barbara.png", 100 / (float)i, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); } - { - int w, h, n; - unsigned char* input_data = stbi_load("barbara.png", &w, &h, &n, 0); + test_format("barbara.png", 0.5, 2.0, STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB); + test_format("barbara.png", 0.5, 2.0, STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR); + test_format("barbara.png", 2.0, 0.5, STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB); + test_format("barbara.png", 2.0, 0.5, STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR); - unsigned short* short_data = (unsigned short*)malloc(w * h * n * sizeof(unsigned short)); - convert_image(input_data, short_data, w * h * n); + test_format("barbara.png", 0.5, 2.0, STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB); + test_format("barbara.png", 0.5, 2.0, STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR); + test_format("barbara.png", 2.0, 0.5, STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB); + test_format("barbara.png", 2.0, 0.5, STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR); - unsigned short* output_data = (unsigned short*)malloc(w * h * n * sizeof(unsigned short)); - - stbr_resize_srgb_uint16(short_data, w, h, output_data, w * 2, h / 2, n, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); - - free(short_data); - stbi_image_free(input_data); - - char* char_data = (char*)malloc(w * h * n * sizeof(char)); - convert_image(output_data, char_data, w * h * n); - - stbi_write_png("test-output/barbara-short.png", w * 2, h / 2, n, char_data, 0); - - free(char_data); - free(output_data); - } + test_format("barbara.png", 0.5, 2.0, STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB); + test_format("barbara.png", 0.5, 2.0, STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR); + test_format("barbara.png", 2.0, 0.5, STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB); + test_format("barbara.png", 2.0, 0.5, STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR); } From 6625259959e99299562b826a4276d7a2ce3fb2c8 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 30 Jul 2014 01:18:23 -0700 Subject: [PATCH 048/168] Try to keep integer precision by briefly casting to double while decoding and encoding. --- stb_resample.h | 8 ++++---- tests/resample_test.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index be08db1..36a0fa2 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -715,12 +715,12 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): for (int n = 0; n < channels; n++) - decode_buffer[decode_texel_index + n] = ((float)((const unsigned int*)input_data)[input_texel_index + n]) / 4294967295; + decode_buffer[decode_texel_index + n] = (float)(((double)((const unsigned int*)input_data)[input_texel_index + n]) / 4294967295); break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): for (int n = 0; n < channels; n++) - decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((float)((const unsigned int*)input_data)[input_texel_index + n]) / 4294967295); + decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_texel_index + n]) / 4294967295)); break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): @@ -913,12 +913,12 @@ static stbr_inline void stbr__encode_scanline(void* output_buffer, int output_te case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): for (int n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 4294967295); + ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__saturate(encode_buffer[encode_texel_index + n])) * 4294967295); break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): for (int n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n])) * 4294967295); + ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n]))) * 4294967295); break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 8a19de8..ff4cf49 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -128,9 +128,9 @@ void resize_image(const char* filename, float width_percent, float height_percen template void convert_image(const F* input, T* output, int length) { - float f = (pow(2.0f, 8.0f * sizeof(T)) - 1) / (pow(2.0f, 8.0f * sizeof(F)) - 1); + double f = (pow(2.0, 8.0 * sizeof(T)) - 1) / (pow(2.0, 8.0 * sizeof(F)) - 1); for (int i = 0; i < length; i++) - output[i] = (T)(((float)input[i]) * f); + output[i] = (T)(((double)input[i]) * f); } template @@ -155,8 +155,8 @@ void test_format(const char* file, float width_percent, float height_percent, st free(T_data); stbi_image_free(input_data); - char* char_data = (char*)malloc(new_w * new_h * n * sizeof(char)); - convert_image(output_data, char_data, new_w * new_h * n); + unsigned char* char_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(char)); + convert_image(output_data, char_data, new_w * new_h * n); char output[200]; sprintf(output, "test-output/type-%d-%d-%d-%d-%s", type, colorspace, new_w, new_h, file); From 59cb71ea182a37453f781eb2aff99c6faf63e4cd Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 30 Jul 2014 08:47:55 -0700 Subject: [PATCH 049/168] Fix float conversion. --- tests/resample_test.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index ff4cf49..f1cab9c 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -166,6 +166,50 @@ void test_format(const char* file, float width_percent, float height_percent, st free(output_data); } +void convert_image_float(const unsigned char* input, float* output, int length) +{ + for (int i = 0; i < length; i++) + output[i] = ((float)input[i])/255; +} + +void convert_image_float(const float* input, unsigned char* output, int length) +{ + for (int i = 0; i < length; i++) + output[i] = (unsigned char)(input[i] * 255); +} + +void test_float(const char* file, float width_percent, float height_percent, stbr_type type, stbr_colorspace colorspace) +{ + int w, h, n; + unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + + int new_w = (int)(w * width_percent); + int new_h = (int)(h * height_percent); + + float* T_data = (float*)malloc(w * h * n * sizeof(float)); + convert_image_float(input_data, T_data, w * h * n); + + float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); + + size_t required = stbr_calculate_memory(w, h, new_w, new_h, n, STBR_FILTER_CATMULLROM); + void* extra_memory = malloc(required); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, n, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); + free(extra_memory); + + free(T_data); + stbi_image_free(input_data); + + unsigned char* char_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(char)); + convert_image_float(output_data, char_data, new_w * new_h * n); + + char output[200]; + sprintf(output, "test-output/type-%d-%d-%d-%d-%s", type, colorspace, new_w, new_h, file); + stbi_write_png(output, new_w, new_h, n, char_data, 0); + + free(char_data); + free(output_data); +} + void test_suite() { // sRGB tests @@ -218,10 +262,10 @@ void test_suite() test_format("barbara.png", 2.0, 0.5, STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB); test_format("barbara.png", 2.0, 0.5, STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR); - test_format("barbara.png", 0.5, 2.0, STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB); - test_format("barbara.png", 0.5, 2.0, STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR); - test_format("barbara.png", 2.0, 0.5, STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB); - test_format("barbara.png", 2.0, 0.5, STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR); + test_float("barbara.png", 0.5, 2.0, STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB); + test_float("barbara.png", 0.5, 2.0, STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR); + test_float("barbara.png", 2.0, 0.5, STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB); + test_float("barbara.png", 2.0, 0.5, STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR); } From c2449acc3eb1e1ada60933c4b8f9fba77ecc27d6 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 30 Jul 2014 09:14:38 -0700 Subject: [PATCH 050/168] Tests for edge behavior --- tests/resample_test.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index f1cab9c..6ae6682 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -96,7 +96,7 @@ int main(int argc, char** argv) return 0; } -void resize_image(const char* filename, float width_percent, float height_percent, stbr_filter filter, stbr_edge edge, const char* output_filename) +void resize_image(const char* filename, float width_percent, float height_percent, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, const char* output_filename) { int w, h, n; @@ -115,7 +115,7 @@ void resize_image(const char* filename, float width_percent, float height_percen size_t memory_required = stbr_calculate_memory(w, h, out_w, out_h, n, filter); void* extra_memory = malloc(memory_required); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, n, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, n, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); free(extra_memory); stbi_image_free(input_data); @@ -212,44 +212,54 @@ void test_float(const char* file, float width_percent, float height_percent, stb void test_suite() { + // Edge behavior tests + resize_image("hgradient.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR, "test-output/hgradient-clamp.png"); + resize_image("hgradient.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_WRAP, STBR_COLORSPACE_LINEAR, "test-output/hgradient-wrap.png"); + + resize_image("vgradient.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR, "test-output/vgradient-clamp.png"); + resize_image("vgradient.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_WRAP, STBR_COLORSPACE_LINEAR, "test-output/vgradient-wrap.png"); + + resize_image("1px-border.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, STBR_COLORSPACE_LINEAR, "test-output/1px-border-reflect.png"); + resize_image("1px-border.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR, "test-output/1px-border-clamp.png"); + // sRGB tests - resize_image("gamma_colors.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, "test-output/gamma_colors.jpg"); - resize_image("gamma_2.2.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, "test-output/gamma_2.2.jpg"); - resize_image("gamma_dalai_lama_gray.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, "test-output/gamma_dalai_lama_gray.jpg"); + resize_image("gamma_colors.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, STBR_COLORSPACE_SRGB, "test-output/gamma_colors.jpg"); + resize_image("gamma_2.2.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, STBR_COLORSPACE_SRGB, "test-output/gamma_2.2.jpg"); + resize_image("gamma_dalai_lama_gray.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, STBR_COLORSPACE_SRGB, "test-output/gamma_dalai_lama_gray.jpg"); for (int i = 10; i < 100; i++) { char outname[200]; sprintf(outname, "test-output/barbara-width-%d.jpg", i); - resize_image("barbara.png", (float)i / 100, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); + resize_image("barbara.png", (float)i / 100, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); } for (int i = 110; i < 500; i += 10) { char outname[200]; sprintf(outname, "test-output/barbara-width-%d.jpg", i); - resize_image("barbara.png", (float)i / 100, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); + resize_image("barbara.png", (float)i / 100, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); } for (int i = 10; i < 100; i++) { char outname[200]; sprintf(outname, "test-output/barbara-height-%d.jpg", i); - resize_image("barbara.png", 1, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); + resize_image("barbara.png", 1, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); } for (int i = 110; i < 500; i += 10) { char outname[200]; sprintf(outname, "test-output/barbara-height-%d.jpg", i); - resize_image("barbara.png", 1, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); + resize_image("barbara.png", 1, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); } for (int i = 50; i < 200; i += 10) { char outname[200]; sprintf(outname, "test-output/barbara-width-height-%d.jpg", i); - resize_image("barbara.png", 100 / (float)i, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, outname); + resize_image("barbara.png", 100 / (float)i, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); } test_format("barbara.png", 0.5, 2.0, STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB); From 985ac752510db2871edd5ab81afac42a02c13e68 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 30 Jul 2014 09:27:42 -0700 Subject: [PATCH 051/168] When doing a perfect po2 nearest neighbor downsample don't allow -0.5 and 0.5 to both contribute to a texel or you'll get a double tap. --- stb_resample.h | 10 +++++----- tests/resample_test.cpp | 13 +++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 36a0fa2..10bdbbb 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -331,12 +331,12 @@ float stbr__linear_to_srgb(float f) static float stbr__filter_nearest(float x) { - x = (float)fabs(x); - - if (x <= 0.5) - return 1; - else + if (x <= -0.5f) return 0; + else if (x > 0.5f) + return 0; + else + return 1; } static float stbr__filter_bilinear(float x) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 6ae6682..b9d1f08 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -227,6 +227,19 @@ void test_suite() resize_image("gamma_2.2.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, STBR_COLORSPACE_SRGB, "test-output/gamma_2.2.jpg"); resize_image("gamma_dalai_lama_gray.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, STBR_COLORSPACE_SRGB, "test-output/gamma_dalai_lama_gray.jpg"); + // filter tests + resize_image("barbara.png", 2, 2, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-upsample-nearest.png"); + resize_image("barbara.png", 2, 2, STBR_FILTER_BILINEAR, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); + resize_image("barbara.png", 2, 2, STBR_FILTER_BICUBIC, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); + resize_image("barbara.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-upsample-catmullrom.png"); + resize_image("barbara.png", 2, 2, STBR_FILTER_MITCHELL, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-upsample-mitchell.png"); + + resize_image("barbara.png", 0.5f, 0.5f, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-downsample-nearest.png"); + resize_image("barbara.png", 0.5f, 0.5f, STBR_FILTER_BILINEAR, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); + resize_image("barbara.png", 0.5f, 0.5f, STBR_FILTER_BICUBIC, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); + resize_image("barbara.png", 0.5f, 0.5f, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png"); + resize_image("barbara.png", 0.5f, 0.5f, STBR_FILTER_MITCHELL, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); + for (int i = 10; i < 100; i++) { char outname[200]; From c5de2f32981ca05876f2d2d09aa82a2ee881ac01 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 30 Jul 2014 09:41:41 -0700 Subject: [PATCH 052/168] Test channels. --- tests/resample_test.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index b9d1f08..55a3b2d 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -210,8 +210,52 @@ void test_float(const char* file, float width_percent, float height_percent, stb free(output_data); } +void test_channels(char* file, float width_percent, float height_percent, int channels) +{ + int w, h, n; + unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + + int new_w = (int)(w * width_percent); + int new_h = (int)(h * height_percent); + + unsigned char* channels_data = (unsigned char*)malloc(w * h * channels * sizeof(unsigned char)); + + for (int i = 0; i < w * h; i++) + { + int input_position = i * n; + int output_position = i * channels; + + for (int c = 0; c < channels; c++) + channels_data[output_position + c] = input_data[input_position + stbr__min(c, n)]; + } + + unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * channels * sizeof(unsigned char)); + + stbr_resize_srgb_uint8(channels_data, w, h, output_data, new_w, new_h, channels, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); + + free(channels_data); + stbi_image_free(input_data); + + char output[200]; + sprintf(output, "test-output/channels-%d-%d-%d-%s", channels, new_w, new_h, file); + stbi_write_png(output, new_w, new_h, channels, output_data, 0); + + free(output_data); +} + void test_suite() { + // Channels test + test_channels("barbara.png", 0.5f, 0.5f, 1); + test_channels("barbara.png", 0.5f, 0.5f, 2); + test_channels("barbara.png", 0.5f, 0.5f, 3); + test_channels("barbara.png", 0.5f, 0.5f, 4); + + test_channels("barbara.png", 2, 2, 1); + test_channels("barbara.png", 2, 2, 2); + test_channels("barbara.png", 2, 2, 3); + test_channels("barbara.png", 2, 2, 4); + // Edge behavior tests resize_image("hgradient.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR, "test-output/hgradient-clamp.png"); resize_image("hgradient.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_WRAP, STBR_COLORSPACE_LINEAR, "test-output/hgradient-wrap.png"); From fdc979e48b67369204a23230f4b0881c7e3a460c Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 30 Jul 2014 17:30:25 -0700 Subject: [PATCH 053/168] Some minor ports for Linux. No idea how it worked at all in Windows with STB_RESIZE_IMPLEMENTATION instead of STB_RESAMPLE_IMPLEMENTATION. --- tests/resample_test.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 55a3b2d..71332fb 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -2,6 +2,9 @@ #define STBR_ASSERT(x) \ if (!(x)) \ __debugbreak(); +#else +#include +#define STBR_ASSERT(x) assert(x) #endif #define STB_RESAMPLE_IMPLEMENTATION @@ -28,7 +31,7 @@ int main(int argc, char** argv) int n; int out_w, out_h, out_stride; -#if 1 +#if 0 test_suite(); return 0; #endif @@ -210,7 +213,7 @@ void test_float(const char* file, float width_percent, float height_percent, stb free(output_data); } -void test_channels(char* file, float width_percent, float height_percent, int channels) +void test_channels(const char* file, float width_percent, float height_percent, int channels) { int w, h, n; unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); From 52ac93225abe1855dbe05f5fb228a9ffc5dd5b8f Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 30 Jul 2014 17:33:47 -0700 Subject: [PATCH 054/168] C<99 ports --- stb_resample.h | 59 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 10bdbbb..5a46460 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -167,7 +167,7 @@ extern "C" { //// end header file ///////////////////////////////////////////////////// #endif // STBR_INCLUDE_STB_RESAMPLE_H -#ifdef STB_RESIZE_IMPLEMENTATION +#ifdef STB_RESAMPLE_IMPLEMENTATION #ifndef STBR_ASSERT #include @@ -596,10 +596,12 @@ static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float #ifdef STBR_DEBUG static void stbr__check_downsample_coefficients(stbr__info* stbr_info) { - for (int i = 0; i < stbr_info->output_w; i++) + int i; + for (i = 0; i < stbr_info->output_w; i++) { float total = 0; - for (int j = 0; j < stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); j++) + int j; + for (j = 0; j < stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); j++) { if (i >= stbr_info->horizontal_contributors[j].n0 && i <= stbr_info->horizontal_contributors[j].n1) { @@ -673,7 +675,7 @@ static float* stbr__get_decode_buffer(stbr__info* stbr_info) static void stbr__decode_scanline(stbr__info* stbr_info, int n) { - int x; + int x, c; int channels = stbr_info->channels; int type = stbr_info->type; int colorspace = stbr_info->colorspace; @@ -694,43 +696,43 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) switch (decode) { case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): - for (int n = 0; n < channels; n++) - decode_buffer[decode_texel_index + n] = ((float)((const unsigned char*)input_data)[input_texel_index + n]) / 255; + for (c = 0; c < channels; c++) + decode_buffer[decode_texel_index + c] = ((float)((const unsigned char*)input_data)[input_texel_index + c]) / 255; break; case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): - for (int n = 0; n < channels; n++) - decode_buffer[decode_texel_index + n] = stbr__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_texel_index + n]]; + for (c = 0; c < channels; c++) + decode_buffer[decode_texel_index + c] = stbr__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_texel_index + c]]; break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): - for (int n = 0; n < channels; n++) - decode_buffer[decode_texel_index + n] = ((float)((const unsigned short*)input_data)[input_texel_index + n]) / 65535; + for (c = 0; c < channels; c++) + decode_buffer[decode_texel_index + c] = ((float)((const unsigned short*)input_data)[input_texel_index + c]) / 65535; break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): - for (int n = 0; n < channels; n++) - decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((float)((const unsigned short*)input_data)[input_texel_index + n]) / 65535); + for (c = 0; c < channels; c++) + decode_buffer[decode_texel_index + c] = stbr__srgb_to_linear(((float)((const unsigned short*)input_data)[input_texel_index + c]) / 65535); break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): - for (int n = 0; n < channels; n++) - decode_buffer[decode_texel_index + n] = (float)(((double)((const unsigned int*)input_data)[input_texel_index + n]) / 4294967295); + for (c = 0; c < channels; c++) + decode_buffer[decode_texel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_texel_index + c]) / 4294967295); break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): - for (int n = 0; n < channels; n++) - decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_texel_index + n]) / 4294967295)); + for (c = 0; c < channels; c++) + decode_buffer[decode_texel_index + c] = stbr__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_texel_index + c]) / 4294967295)); break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): - for (int n = 0; n < channels; n++) - decode_buffer[decode_texel_index + n] = ((const float*)input_data)[input_texel_index + n]; + for (c = 0; c < channels; c++) + decode_buffer[decode_texel_index + c] = ((const float*)input_data)[input_texel_index + c]; break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): - for (int n = 0; n < channels; n++) - decode_buffer[decode_texel_index + n] = stbr__srgb_to_linear(((const float*)input_data)[input_texel_index + n]); + for (c = 0; c < channels; c++) + decode_buffer[decode_texel_index + c] = stbr__srgb_to_linear(((const float*)input_data)[input_texel_index + c]); break; default: @@ -889,45 +891,46 @@ static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffe static stbr_inline void stbr__encode_scanline(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, int decode) { + int n; switch (decode) { case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): - for (int n = 0; n < channels; n++) + for (n = 0; n < channels; n++) ((unsigned char*)output_buffer)[output_texel_index + n] = (unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 255); break; case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): - for (int n = 0; n < channels; n++) + for (n = 0; n < channels; n++) ((unsigned char*)output_buffer)[output_texel_index + n] = stbr__linear_uchar_to_srgb_uchar[(unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 255)]; break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): - for (int n = 0; n < channels; n++) + for (n = 0; n < channels; n++) ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 65535); break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): - for (int n = 0; n < channels; n++) + for (n = 0; n < channels; n++) ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n])) * 65535); break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): - for (int n = 0; n < channels; n++) + for (n = 0; n < channels; n++) ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__saturate(encode_buffer[encode_texel_index + n])) * 4294967295); break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): - for (int n = 0; n < channels; n++) + for (n = 0; n < channels; n++) ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n]))) * 4294967295); break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): - for (int n = 0; n < channels; n++) + for (n = 0; n < channels; n++) ((float*)output_buffer)[output_texel_index + n] = stbr__saturate(encode_buffer[encode_texel_index + n]); break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): - for (int n = 0; n < channels; n++) + for (n = 0; n < channels; n++) ((float*)output_buffer)[output_texel_index + n] = stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n])); break; From 35cb95b8031442979a34203ba371a2328f6367f6 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 31 Jul 2014 00:39:33 -0700 Subject: [PATCH 055/168] Allow for specifying a sub-region of the source image to use. Downsampling only, currently. --- stb_resample.h | 220 ++++++++++++++++++++++++++++++------------------ tests/resample_test.cpp | 25 +++--- 2 files changed, 153 insertions(+), 92 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 5a46460..843b6b4 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -52,11 +52,13 @@ // void* extra_memory = malloc(memory_required); // Any memory allocation method of your choosing // result = stbr_resize_arbitrary(input_data, input_w, input_h, input_stride_in_bytes, // output_data, output_w, output_h, output_stride_in_bytes, +// s0, t0, s1, t1, // channels, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, // extra_memory, memory_required); // free(extra_memory); // // input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. +// 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. // Returned result is 1 for success or 0 in case of an error. Currently the only error is that the memory passed in is insufficient. // stbr_resize_arbitrary() will not allocate any memory, it will use the memory you pass in to do its work. @@ -150,10 +152,11 @@ extern "C" { // ADVANCED API // - STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w, int output_h, int channels, stbr_filter filter); + STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter); STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, int channels, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes); @@ -247,6 +250,13 @@ typedef struct int output_h; int output_stride_bytes; + float s0, t0, s1, t1; + + float horizontal_shift; // Units: output texels + float vertical_shift; // Units: output texels + float horizontal_scale; + float vertical_scale; + int channels; stbr_type type; stbr_filter filter; @@ -394,68 +404,93 @@ static stbr__filter_info stbr__filter_info_table[] = { { stbr__filter_mitchell, 2.0f }, }; -stbr_inline static int stbr__use_width_upsampling_noinfo(int output_w, int input_w) +stbr_inline static int stbr__use_upsampling(int output_w, int input_w) { return output_w > input_w; } -stbr_inline static int stbr__use_height_upsampling_noinfo(int output_h, int input_h) -{ - return output_h > input_h; -} - stbr_inline static int stbr__use_width_upsampling(stbr__info* stbr_info) { - return stbr__use_width_upsampling_noinfo(stbr_info->output_w, stbr_info->input_w); + return stbr__use_upsampling(stbr_info->output_w, stbr_info->input_w); } stbr_inline static int stbr__use_height_upsampling(stbr__info* stbr_info) { - return stbr__use_height_upsampling_noinfo(stbr_info->output_h, stbr_info->input_h); + return stbr__use_upsampling(stbr_info->output_h, stbr_info->input_h); } // This is the maximum number of input samples that can affect an output sample // with the given filter -stbr_inline static int stbr__get_filter_texel_width(stbr_filter filter, int input_w, int output_w) +stbr_inline static int stbr__get_filter_texel_width(stbr_filter filter, int input_w, int output_w, float scale) { STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); - if (stbr__use_height_upsampling_noinfo(output_w, input_w)) + if (stbr__use_upsampling(output_w, input_w)) return (int)ceil(stbr__filter_info_table[filter].support * 2); else - return (int)ceil(stbr__filter_info_table[filter].support * 2 * input_w / output_w); + return (int)ceil(stbr__filter_info_table[filter].support * 2 / scale); +} + +stbr_inline static int stbr__get_filter_texel_width_horizontal(stbr__info* stbr_info) +{ + return stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale); +} + +stbr_inline static int stbr__get_filter_texel_width_vertical(stbr__info* stbr_info) +{ + return stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h, stbr_info->vertical_scale); } // This is how much to expand buffers to account for filters seeking outside // the image boundaries. -stbr_inline static int stbr__get_filter_texel_margin(stbr_filter filter, int input_w, int output_w) +stbr_inline static int stbr__get_filter_texel_margin(stbr_filter filter, int input_w, int output_w, float scale) { - return stbr__get_filter_texel_width(filter, input_w, output_w) / 2; + return stbr__get_filter_texel_width(filter, input_w, output_w, scale) / 2; } -stbr_inline static int stbr__get_horizontal_contributors(stbr_filter filter, int input_w, int output_w) +stbr_inline static int stbr__get_filter_texel_margin_horizontal(stbr__info* stbr_info) { - if (stbr__use_width_upsampling_noinfo(output_w, input_w)) + return stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale) / 2; +} + +stbr_inline static int stbr__get_filter_texel_margin_vertical(stbr__info* stbr_info) +{ + return stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h, stbr_info->vertical_scale) / 2; +} + +stbr_inline static int stbr__get_horizontal_contributors_noinfo(stbr_filter filter, int input_w, int output_w, float horizontal_scale) +{ + if (stbr__use_upsampling(output_w, input_w)) return output_w; else - return (input_w + stbr__get_filter_texel_margin(filter, input_w, output_w) * 2); + return (input_w + stbr__get_filter_texel_margin(filter, input_w, output_w, horizontal_scale) * 2); } -stbr_inline static int stbr__get_total_coefficients(stbr_filter filter, int input_w, int output_w) +stbr_inline static int stbr__get_horizontal_contributors(stbr__info* stbr_info) { - return stbr__get_horizontal_contributors(filter, input_w, output_w) * stbr__get_filter_texel_width(filter, input_w, output_w); + return stbr__get_horizontal_contributors_noinfo(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale); +} + +stbr_inline static int stbr__get_total_coefficients_noinfo(stbr_filter filter, int input_w, int output_w, float horizontal_scale) +{ + return stbr__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * stbr__get_filter_texel_width(filter, input_w, output_w, horizontal_scale); +} + +stbr_inline static int stbr__get_total_coefficients(stbr__info* stbr_info) +{ + return stbr__get_total_coefficients_noinfo(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale); } stbr_inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_info, int n) { - STBR_DEBUG_ASSERT(n >= 0 && n < stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); + STBR_DEBUG_ASSERT(n >= 0 && n < stbr__get_horizontal_contributors(stbr_info)); return &stbr_info->horizontal_contributors[n]; } stbr_inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, int c) { - return &stbr_info->horizontal_coefficients[stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)*n + c]; + return &stbr_info->horizontal_coefficients[stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale)*n + c]; } stbr_inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) @@ -528,16 +563,16 @@ static void stbr__calculate_sample_range_upsample(int n, float out_filter_radius } // What output texels does this input texel contribute to? -static void stbr__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, int* out_first_texel, int* out_last_texel, float* out_center_of_in) +static void stbr__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_texel, int* out_last_texel, float* out_center_of_in) { float in_texel_center = (float)n + 0.5f; float in_texel_influence_lowerbound = in_texel_center - in_pixels_radius; float in_texel_influence_upperbound = in_texel_center + in_pixels_radius; - float out_texel_influence_lowerbound = in_texel_influence_lowerbound * scale_ratio; - float out_texel_influence_upperbound = in_texel_influence_upperbound * scale_ratio; + float out_texel_influence_lowerbound = in_texel_influence_lowerbound * scale_ratio - out_shift; + float out_texel_influence_upperbound = in_texel_influence_upperbound * scale_ratio - out_shift; - *out_center_of_in = in_texel_center * scale_ratio; + *out_center_of_in = in_texel_center * scale_ratio - out_shift; *out_first_texel = (int)(floor(out_texel_influence_lowerbound + 0.5)); *out_last_texel = (int)(floor(out_texel_influence_upperbound - 0.5)); } @@ -549,8 +584,8 @@ static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_ float filter_scale; stbr_filter filter = stbr_info->filter; - STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr__get_filter_texel_width(filter, stbr_info->input_w, stbr_info->output_w)); - STBR_DEBUG_ASSERT(in_last_texel < stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); + STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr__get_filter_texel_width_horizontal(stbr_info)); + STBR_DEBUG_ASSERT(in_last_texel < stbr__get_horizontal_contributors(stbr_info)); contributor->n0 = in_first_texel; contributor->n1 = in_last_texel; @@ -578,8 +613,8 @@ static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float int i; stbr_filter filter = stbr_info->filter; - STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr__get_filter_texel_width(filter, stbr_info->input_w, stbr_info->output_w)); - STBR_DEBUG_ASSERT(out_last_texel < stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); + STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr__get_filter_texel_width_horizontal(stbr_info)); + STBR_DEBUG_ASSERT(out_last_texel < stbr__get_horizontal_contributors(stbr_info)); contributor->n0 = out_first_texel; contributor->n1 = out_last_texel; @@ -601,7 +636,7 @@ static void stbr__check_downsample_coefficients(stbr__info* stbr_info) { float total = 0; int j; - for (j = 0; j < stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); j++) + for (j = 0; j < stbr__get_horizontal_contributors(stbr_info); j++) { if (i >= stbr_info->horizontal_contributors[j].n0 && i <= stbr_info->horizontal_contributors[j].n1) { @@ -623,14 +658,16 @@ static void stbr__check_downsample_coefficients(stbr__info* stbr_info) static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) { int n; - float scale_ratio = (float)stbr_info->output_w / stbr_info->input_w; + float scale_ratio = stbr_info->horizontal_scale; - int total_contributors = stbr__get_horizontal_contributors(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); + int total_contributors = stbr__get_horizontal_contributors(stbr_info); if (stbr__use_width_upsampling(stbr_info)) { float out_pixels_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; + STBR_UNIMPLEMENTED(stbr_info->horizontal_shift); + // Looping through out texels for (n = 0; n < total_contributors; n++) { @@ -651,9 +688,9 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) { float out_center_of_in; // Center of the current out texel in the in texel space int out_first_texel, out_last_texel; - int n_adjusted = n - stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); + int n_adjusted = n - stbr__get_filter_texel_margin_horizontal(stbr_info); - stbr__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, &out_first_texel, &out_last_texel, &out_center_of_in); + stbr__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, stbr_info->horizontal_shift, &out_first_texel, &out_last_texel, &out_center_of_in); stbr__calculate_coefficients_downsample(stbr_info, scale_ratio, out_first_texel, out_last_texel, out_center_of_in, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); } @@ -668,7 +705,7 @@ static float* stbr__get_decode_buffer(stbr__info* stbr_info) { // The 0 index of the decode buffer starts after the margin. This makes // it okay to use negative indexes on the decode buffer. - return &stbr_info->decode_buffer[stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w) * stbr_info->channels]; + return &stbr_info->decode_buffer[stbr__get_filter_texel_margin_horizontal(stbr_info) * stbr_info->channels]; } #define STBR__DECODE(type, colorspace) ((type) * (STBR_MAX_COLORSPACES) + (colorspace)) @@ -685,10 +722,10 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) float* decode_buffer = stbr__get_decode_buffer(stbr_info); stbr_edge edge = stbr_info->edge; int in_buffer_row_index = stbr__edge_wrap(edge, n, stbr_info->input_h) * input_stride; - int max_x = input_w + stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); + int max_x = input_w + stbr__get_filter_texel_margin_horizontal(stbr_info); int decode = STBR__DECODE(type, colorspace); - for (x = -stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); x < max_x; x++) + for (x = -stbr__get_filter_texel_margin_horizontal(stbr_info); x < max_x; x++) { int decode_texel_index = x * channels; int input_texel_index = in_buffer_row_index + stbr__edge_wrap(edge, x, input_w) * channels; @@ -759,7 +796,7 @@ static float* stbr__add_empty_ring_buffer_entry(stbr__info* stbr_info, int n) } else { - ring_buffer_index = (stbr_info->ring_buffer_begin_index + (stbr_info->ring_buffer_last_scanline - stbr_info->ring_buffer_first_scanline) + 1) % stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); + ring_buffer_index = (stbr_info->ring_buffer_begin_index + (stbr_info->ring_buffer_last_scanline - stbr_info->ring_buffer_first_scanline) + 1) % stbr__get_filter_texel_width_vertical(stbr_info); STBR_DEBUG_ASSERT(ring_buffer_index != stbr_info->ring_buffer_begin_index); } @@ -776,7 +813,7 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo { int x, k; int output_w = stbr_info->output_w; - int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); + int kernel_texel_width = stbr__get_filter_texel_width_horizontal(stbr_info); int channels = stbr_info->channels; float* decode_buffer = stbr__get_decode_buffer(stbr_info); stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; @@ -792,10 +829,10 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo int coefficient_counter = 0; STBR_DEBUG_ASSERT(n1 >= n0); - STBR_DEBUG_ASSERT(n0 >= -stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); - STBR_DEBUG_ASSERT(n1 >= -stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); - STBR_DEBUG_ASSERT(n0 < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); - STBR_DEBUG_ASSERT(n1 < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w)); + STBR_DEBUG_ASSERT(n0 >= -stbr__get_filter_texel_margin_horizontal(stbr_info)); + STBR_DEBUG_ASSERT(n1 >= -stbr__get_filter_texel_margin_horizontal(stbr_info)); + STBR_DEBUG_ASSERT(n0 < stbr_info->input_w + stbr__get_filter_texel_margin_horizontal(stbr_info)); + STBR_DEBUG_ASSERT(n1 < stbr_info->input_w + stbr__get_filter_texel_margin_horizontal(stbr_info)); for (k = n0; k <= n1; k++) { @@ -815,12 +852,12 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, f int x, k; int input_w = stbr_info->input_w; int output_w = stbr_info->output_w; - int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); + int kernel_texel_width = stbr__get_filter_texel_width_horizontal(stbr_info); int channels = stbr_info->channels; float* decode_buffer = stbr__get_decode_buffer(stbr_info); stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; float* horizontal_coefficients = stbr_info->horizontal_coefficients; - int filter_texel_margin = stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_w, stbr_info->output_w); + int filter_texel_margin = stbr__get_filter_texel_margin_horizontal(stbr_info); int max_x = input_w + filter_texel_margin * 2; STBR_DEBUG_ASSERT(!stbr__use_width_upsampling(stbr_info)); @@ -949,7 +986,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int channels = stbr_info->channels; int type = stbr_info->type; int colorspace = stbr_info->colorspace; - int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); + int kernel_texel_width = stbr__get_filter_texel_width_vertical(stbr_info); void* output_data = stbr_info->output_data; float* encode_buffer = stbr_info->encode_buffer; int decode = STBR__DECODE(type, colorspace); @@ -1004,7 +1041,7 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; float* vertical_coefficients = stbr_info->vertical_coefficients; int channels = stbr_info->channels; - int kernel_texel_width = stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); + int kernel_texel_width = stbr__get_filter_texel_width_vertical(stbr_info); void* output_data = stbr_info->output_data; float* horizontal_buffer = stbr_info->horizontal_buffer; @@ -1014,7 +1051,7 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); - stbr__calculate_coefficients_downsample(stbr_info, (float)stbr_info->output_h / stbr_info->input_h, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); + stbr__calculate_coefficients_downsample(stbr_info, stbr_info->vertical_scale, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); int n0 = vertical_contributors->n0; int n1 = vertical_contributors->n1; @@ -1059,9 +1096,7 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) stbr__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, &in_first_scanline, &in_last_scanline, &in_center_of_out); - STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h)); - STBR_DEBUG_ASSERT(in_first_scanline >= -stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h)); - STBR_DEBUG_ASSERT(in_last_scanline < stbr_info->input_w + stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h)); + STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr__get_filter_texel_width_vertical(stbr_info)); if (stbr_info->ring_buffer_begin_index >= 0) { @@ -1080,7 +1115,7 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) else { stbr_info->ring_buffer_first_scanline++; - stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); + stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_texel_width_horizontal(stbr_info); } } } @@ -1143,7 +1178,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s else { stbr_info->ring_buffer_first_scanline++; - stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); + stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_texel_width_vertical(stbr_info); } } } @@ -1152,22 +1187,24 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s static void stbr__buffer_loop_downsample(stbr__info* stbr_info) { int y; - float scale_ratio = (float)stbr_info->output_h / stbr_info->input_h; + float scale_ratio = stbr_info->vertical_scale; + int output_h = stbr_info->output_h; float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; - int max_y = stbr_info->input_h + stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); + int max_y = stbr_info->input_h + stbr__get_filter_texel_margin_vertical(stbr_info); STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); - for (y = -stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h); y < max_y; y++) + for (y = -stbr__get_filter_texel_margin_vertical(stbr_info); y < max_y; y++) { float out_center_of_in; // Center of the current out scanline in the in scanline space int out_first_scanline, out_last_scanline; - stbr__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, &out_first_scanline, &out_last_scanline, &out_center_of_in); + stbr__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbr_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); - STBR_DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h)); - STBR_DEBUG_ASSERT(out_first_scanline >= -2 * stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h)); - STBR_DEBUG_ASSERT(out_last_scanline < stbr_info->input_w + 2 * stbr__get_filter_texel_margin(stbr_info->filter, stbr_info->input_h, stbr_info->output_h)); + STBR_DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbr__get_filter_texel_width_vertical(stbr_info)); + + if (out_last_scanline < 0 || out_first_scanline >= output_h) + continue; stbr__empty_ring_buffer(stbr_info, out_first_scanline); @@ -1189,6 +1226,7 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, int channels, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes) { @@ -1212,10 +1250,13 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); + STBR_ASSERT(s1 > s0); + STBR_ASSERT(t1 > t0); + if (!tempmem) return 0; - if (tempmem_size_in_bytes < stbr_calculate_memory(input_w, input_h, output_w, output_h, channels, filter)) + if (tempmem_size_in_bytes < stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter)) return 0; memset(tempmem, 0, tempmem_size_in_bytes); @@ -1232,6 +1273,17 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->output_h = output_h; stbr_info->output_stride_bytes = width_stride_output; + stbr_info->s0 = s0; + stbr_info->t0 = t0; + stbr_info->s1 = s1; + stbr_info->t1 = t1; + + stbr_info->horizontal_scale = ((float)output_w / input_w) / (s1 - s0); + stbr_info->vertical_scale = ((float)output_h / input_h) / (t1 - t0); + + stbr_info->horizontal_shift = s0 * input_w * stbr_info->horizontal_scale; + stbr_info->vertical_shift = t0 * input_h * stbr_info->vertical_scale; + stbr_info->channels = channels; stbr_info->type = type; stbr_info->filter = filter; @@ -1239,20 +1291,20 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->colorspace = colorspace; stbr_info->ring_buffer_length_bytes = output_w * channels * sizeof(float); - stbr_info->decode_buffer_texels = input_w + stbr__get_filter_texel_margin(filter, input_w, output_w) * 2; + stbr_info->decode_buffer_texels = input_w + stbr__get_filter_texel_margin_horizontal(stbr_info) * 2; #define STBR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) stbr_info->horizontal_contributors = STBR__NEXT_MEMPTR(stbr_info, sizeof(stbr__info), stbr__contributors); - stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, stbr__get_horizontal_contributors(filter, input_w, output_w) * sizeof(stbr__contributors), float); - stbr_info->vertical_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr__get_total_coefficients(filter, input_w, output_w) * sizeof(float), float); - stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr__get_filter_texel_width(filter, input_h, output_h) * sizeof(float), float); + stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, stbr__get_horizontal_contributors(stbr_info) * sizeof(stbr__contributors), float); + stbr_info->vertical_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr__get_total_coefficients(stbr_info) * sizeof(float), float); + stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr__get_filter_texel_width_vertical(stbr_info) * sizeof(float), float); if (stbr__use_height_upsampling(stbr_info)) { stbr_info->horizontal_buffer = NULL; stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, stbr_info->decode_buffer_texels * channels * sizeof(float), float); - stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width(filter, input_h, output_h), float); + stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width_horizontal(stbr_info), float); STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->encode_buffer, stbr_info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } @@ -1262,7 +1314,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->horizontal_buffer, output_w * channels * sizeof(float), float); stbr_info->encode_buffer = NULL; - STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width(filter, input_h, output_h), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width_vertical(stbr_info), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } #undef STBR__NEXT_MEMPTR @@ -1288,23 +1340,27 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input } -STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w, int output_h, int channels, stbr_filter filter) +STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter) { STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); - int texel_margin = stbr__get_filter_texel_margin(filter, input_w, output_w); + float horizontal_scale = ((float)output_w / input_w) / (s1 - s0); + float vertical_scale = ((float)output_h / input_h) / (t1 - t0); + + int texel_margin = stbr__get_filter_texel_margin(filter, input_w, output_w, horizontal_scale); + int filter_height = stbr__get_filter_texel_width(filter, input_h, output_h, vertical_scale); int info_size = sizeof(stbr__info); - int contributors_size = stbr__get_horizontal_contributors(filter, input_w, output_w) * sizeof(stbr__contributors); - int horizontal_coefficients_size = stbr__get_total_coefficients(filter, input_w, output_w) * sizeof(float); - int vertical_coefficients_size = stbr__get_filter_texel_width(filter, input_h, output_h) * sizeof(float); + int contributors_size = stbr__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(stbr__contributors); + int horizontal_coefficients_size = stbr__get_total_coefficients_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(float); + int vertical_coefficients_size = filter_height * sizeof(float); int decode_buffer_size = (input_w + texel_margin*2) * channels * sizeof(float); int horizontal_buffer_size = output_w * channels * sizeof(float); - int ring_buffer_size = output_w * channels * sizeof(float) * stbr__get_filter_texel_width(filter, input_h, output_h); + int ring_buffer_size = output_w * channels * filter_height * sizeof(float); int encode_buffer_size = channels * sizeof(float); - if (stbr__use_height_upsampling_noinfo(output_h, input_h)) + if (stbr__use_upsampling(output_h, input_h)) // The horizontal buffer is for when we're downsampling the height and we // can't output the result of sampling the decode buffer directly into the // ring buffers. @@ -1323,13 +1379,13 @@ STBRDEF int stbr_resize_srgb_uint8(const stbr_uint8* input_data, int input_w, in stbr_uint8* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, channels, filter); + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = malloc(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, channels, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); free(extra_memory); @@ -1340,13 +1396,13 @@ STBRDEF int stbr_resize_srgb_uint16(const stbr_uint16* input_data, int input_w, stbr_uint16* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, channels, filter); + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = malloc(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, channels, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); free(extra_memory); @@ -1357,13 +1413,13 @@ STBRDEF int stbr_resize_srgb_uint32(const stbr_uint32* input_data, int input_w, stbr_uint32* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, channels, filter); + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = malloc(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, channels, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); free(extra_memory); @@ -1374,13 +1430,13 @@ STBRDEF int stbr_resize_srgb_float(const float* input_data, int input_w, int inp float* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, channels, filter); + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = malloc(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, channels, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); free(extra_memory); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 71332fb..c678c74 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -49,8 +49,8 @@ int main(int argc, char** argv) return 1; } - out_w = 256; - out_h = 256; + out_w = 128; + out_h = 128; out_stride = (out_w + 10) * n; output_data = (unsigned char*)malloc(out_stride * out_h); @@ -58,7 +58,12 @@ int main(int argc, char** argv) int in_w = 512; int in_h = 512; - size_t memory_required = stbr_calculate_memory(in_w, in_h, out_w, out_h, n, STBR_FILTER_CATMULLROM); + float s0 = 0.25f; + float t0 = 0.25f; + float s1 = 0.75f; + float t1 = 0.75f; + + size_t memory_required = stbr_calculate_memory(in_w, in_h, out_w, out_h, s0, t0, s1, t1, n, STBR_FILTER_CATMULLROM); void* extra_memory = malloc(memory_required); // Cut out the outside 64 pixels all around to test the stride. @@ -86,7 +91,7 @@ int main(int argc, char** argv) printf("Average: %dms\n", average); #else - stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); #endif free(extra_memory); @@ -115,10 +120,10 @@ void resize_image(const char* filename, float width_percent, float height_percen unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); - size_t memory_required = stbr_calculate_memory(w, h, out_w, out_h, n, filter); + size_t memory_required = stbr_calculate_memory(w, h, out_w, out_h, 0, 0, 1, 1, n, filter); void* extra_memory = malloc(memory_required); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, n, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); free(extra_memory); stbi_image_free(input_data); @@ -150,9 +155,9 @@ void test_format(const char* file, float width_percent, float height_percent, st T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); - size_t required = stbr_calculate_memory(w, h, new_w, new_h, n, STBR_FILTER_CATMULLROM); + size_t required = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, 1, 1, n, STBR_FILTER_CATMULLROM); void* extra_memory = malloc(required); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, n, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); free(extra_memory); free(T_data); @@ -194,9 +199,9 @@ void test_float(const char* file, float width_percent, float height_percent, stb float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); - size_t required = stbr_calculate_memory(w, h, new_w, new_h, n, STBR_FILTER_CATMULLROM); + size_t required = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, 1, 1, n, STBR_FILTER_CATMULLROM); void* extra_memory = malloc(required); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, n, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); free(extra_memory); free(T_data); From 1b2d104e0077f58e5d191a9ff98a5aa64a03f4f4 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 31 Jul 2014 00:52:03 -0700 Subject: [PATCH 056/168] Some error conditions. --- stb_resample.h | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 843b6b4..8f51d40 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -43,7 +43,7 @@ // // input_data is your supplied texels. // output_data will be the resized texels. It should be of size output_w * output_h * channels -// Returned result is 1 for success or 0 in case of an error. Currently the only error is failure to allocate memory. +// Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. // If you're unsure of which filter to use, Catmull-Rom is a good upsampling filter and Mitchell is a good downsampling filter. @@ -59,7 +59,7 @@ // // input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. // 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. -// Returned result is 1 for success or 0 in case of an error. Currently the only error is that the memory passed in is insufficient. +// Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. // stbr_resize_arbitrary() will not allocate any memory, it will use the memory you pass in to do its work. @@ -1230,6 +1230,8 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input int channels, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes) { + stbr_size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w * stbr__type_size[type]; int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w * stbr__type_size[type]; @@ -1250,13 +1252,28 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); + if (!filter || filter >= STBR_ARRAY_SIZE(stbr__filter_info_table)) + return 0; + STBR_ASSERT(s1 > s0); STBR_ASSERT(t1 > t0); + if (s1 <= s0 || t1 <= t0) + return 0; + + STBR_ASSERT(s1 <= 1 && s0 >= 0 && t1 <= 1 && t0 >= 0); + + if (s1 > 1 || s0 < 0 || t1 > 1 || t0 < 0) + return 0; + + STBR_ASSERT(tempmem); + if (!tempmem) return 0; - if (tempmem_size_in_bytes < stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter)) + STBR_ASSERT(tempmem_size_in_bytes >= memory_required); + + if (tempmem_size_in_bytes < memory_required) return 0; memset(tempmem, 0, tempmem_size_in_bytes); From aae1c7ca414b17c0bb4b7feff678c462a57606e0 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 31 Jul 2014 15:16:36 -0700 Subject: [PATCH 057/168] ZOOM AND ENHANCE! --- stb_resample.h | 36 ++++++++++++++++-------------------- tests/resample_test.cpp | 4 ++-- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 8f51d40..bd0171a 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -404,19 +404,19 @@ static stbr__filter_info stbr__filter_info_table[] = { { stbr__filter_mitchell, 2.0f }, }; -stbr_inline static int stbr__use_upsampling(int output_w, int input_w) +stbr_inline static int stbr__use_upsampling(float ratio) { - return output_w > input_w; + return ratio > 1; } stbr_inline static int stbr__use_width_upsampling(stbr__info* stbr_info) { - return stbr__use_upsampling(stbr_info->output_w, stbr_info->input_w); + return stbr__use_upsampling(stbr_info->horizontal_scale); } stbr_inline static int stbr__use_height_upsampling(stbr__info* stbr_info) { - return stbr__use_upsampling(stbr_info->output_h, stbr_info->input_h); + return stbr__use_upsampling(stbr_info->vertical_scale); } // This is the maximum number of input samples that can affect an output sample @@ -426,7 +426,7 @@ stbr_inline static int stbr__get_filter_texel_width(stbr_filter filter, int inpu STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); - if (stbr__use_upsampling(output_w, input_w)) + if (stbr__use_upsampling(scale)) return (int)ceil(stbr__filter_info_table[filter].support * 2); else return (int)ceil(stbr__filter_info_table[filter].support * 2 / scale); @@ -461,7 +461,7 @@ stbr_inline static int stbr__get_filter_texel_margin_vertical(stbr__info* stbr_i stbr_inline static int stbr__get_horizontal_contributors_noinfo(stbr_filter filter, int input_w, int output_w, float horizontal_scale) { - if (stbr__use_upsampling(output_w, input_w)) + if (stbr__use_upsampling(horizontal_scale)) return output_w; else return (input_w + stbr__get_filter_texel_margin(filter, input_w, output_w, horizontal_scale) * 2); @@ -548,16 +548,16 @@ stbr_inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) } // What input texels contribute to this output texel? -static void stbr__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, int* in_first_texel, int* in_last_texel, float* in_center_of_out) +static void stbr__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_texel, int* in_last_texel, float* in_center_of_out) { float out_texel_center = (float)n + 0.5f; float out_texel_influence_lowerbound = out_texel_center - out_filter_radius; float out_texel_influence_upperbound = out_texel_center + out_filter_radius; - float in_texel_influence_lowerbound = out_texel_influence_lowerbound / scale_ratio; - float in_texel_influence_upperbound = out_texel_influence_upperbound / scale_ratio; + float in_texel_influence_lowerbound = (out_texel_influence_lowerbound + out_shift) / scale_ratio; + float in_texel_influence_upperbound = (out_texel_influence_upperbound + out_shift) / scale_ratio; - *in_center_of_out = out_texel_center / scale_ratio; + *in_center_of_out = (out_texel_center + out_shift) / scale_ratio; *in_first_texel = (int)(floor(in_texel_influence_lowerbound + 0.5)); *in_last_texel = (int)(floor(in_texel_influence_upperbound - 0.5)); } @@ -585,7 +585,6 @@ static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_ stbr_filter filter = stbr_info->filter; STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr__get_filter_texel_width_horizontal(stbr_info)); - STBR_DEBUG_ASSERT(in_last_texel < stbr__get_horizontal_contributors(stbr_info)); contributor->n0 = in_first_texel; contributor->n1 = in_last_texel; @@ -614,7 +613,6 @@ static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float stbr_filter filter = stbr_info->filter; STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr__get_filter_texel_width_horizontal(stbr_info)); - STBR_DEBUG_ASSERT(out_last_texel < stbr__get_horizontal_contributors(stbr_info)); contributor->n0 = out_first_texel; contributor->n1 = out_last_texel; @@ -666,15 +664,13 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) { float out_pixels_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; - STBR_UNIMPLEMENTED(stbr_info->horizontal_shift); - // Looping through out texels for (n = 0; n < total_contributors; n++) { float in_center_of_out; // Center of the current out texel in the in texel space int in_first_texel, in_last_texel; - stbr__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, &in_first_texel, &in_last_texel, &in_center_of_out); + stbr__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, stbr_info->horizontal_shift, &in_first_texel, &in_last_texel, &in_center_of_out); stbr__calculate_coefficients_upsample(stbr_info, in_first_texel, in_last_texel, in_center_of_out, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); } @@ -1084,7 +1080,7 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int static void stbr__buffer_loop_upsample(stbr__info* stbr_info) { int y; - float scale_ratio = (float)stbr_info->output_h / stbr_info->input_h; + float scale_ratio = stbr_info->vertical_scale; float out_scanlines_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; STBR_DEBUG_ASSERT(stbr__use_height_upsampling(stbr_info)); @@ -1094,7 +1090,7 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) float in_center_of_out = 0; // Center of the current out scanline in the in scanline space int in_first_scanline = 0, in_last_scanline = 0; - stbr__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, &in_first_scanline, &in_last_scanline, &in_center_of_out); + stbr__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbr_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr__get_filter_texel_width_vertical(stbr_info)); @@ -1298,8 +1294,8 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->horizontal_scale = ((float)output_w / input_w) / (s1 - s0); stbr_info->vertical_scale = ((float)output_h / input_h) / (t1 - t0); - stbr_info->horizontal_shift = s0 * input_w * stbr_info->horizontal_scale; - stbr_info->vertical_shift = t0 * input_h * stbr_info->vertical_scale; + stbr_info->horizontal_shift = s0 * input_w / (s1 - s0); + stbr_info->vertical_shift = t0 * input_h / (t1 - t0); stbr_info->channels = channels; stbr_info->type = type; @@ -1377,7 +1373,7 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w int ring_buffer_size = output_w * channels * filter_height * sizeof(float); int encode_buffer_size = channels * sizeof(float); - if (stbr__use_upsampling(output_h, input_h)) + if (stbr__use_upsampling(horizontal_scale)) // The horizontal buffer is for when we're downsampling the height and we // can't output the result of sampling the decode buffer directly into the // ring buffers. diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index c678c74..c163656 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -49,8 +49,8 @@ int main(int argc, char** argv) return 1; } - out_w = 128; - out_h = 128; + out_w = 512; + out_h = 512; out_stride = (out_w + 10) * n; output_data = (unsigned char*)malloc(out_stride * out_h); From daf325dc0303783750b98d7bf68053df9864ffe4 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 31 Jul 2014 16:31:45 -0700 Subject: [PATCH 058/168] Sub pixel source area test cases. No problems. --- tests/resample_test.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index c163656..3857d54 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -31,7 +31,7 @@ int main(int argc, char** argv) int n; int out_w, out_h, out_stride; -#if 0 +#if 1 test_suite(); return 0; #endif @@ -251,8 +251,49 @@ void test_channels(const char* file, float width_percent, float height_percent, free(output_data); } +void test_subpixel(const char* file, float width_percent, float height_percent, float s1, float t1) +{ + int w, h, n; + unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + + s1 = ((float)w - 1 + s1)/w; + t1 = ((float)h - 1 + t1)/h; + + int new_w = (int)(w * width_percent); + int new_h = (int)(h * height_percent); + + unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); + + size_t tempmem_size = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, s1, t1, n, STBR_FILTER_CATMULLROM); + void* tempmem = malloc(tempmem_size); + + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); + + free(tempmem); + + stbi_image_free(input_data); + + char output[200]; + sprintf(output, "test-output/subpixel-%d-%d-%f-%f-%s", new_w, new_h, s1, t1, file); + stbi_write_png(output, new_w, new_h, n, output_data, 0); + + free(output_data); +} + void test_suite() { + for (int i = 0; i < 10; i++) + test_subpixel("barbara.png", 0.5f, 0.5f, (float)i / 10, 1); + + for (int i = 0; i < 10; i++) + test_subpixel("barbara.png", 0.5f, 0.5f, 1, (float)i / 10); + + for (int i = 0; i < 10; i++) + test_subpixel("barbara.png", 2, 2, (float)i / 10, 1); + + for (int i = 0; i < 10; i++) + test_subpixel("barbara.png", 2, 2, 1, (float)i / 10); + // Channels test test_channels("barbara.png", 0.5f, 0.5f, 1); test_channels("barbara.png", 0.5f, 0.5f, 2); From a32fa8b4dfd90b86bcd53d463a6e16757cbfa4a1 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 31 Jul 2014 16:36:09 -0700 Subject: [PATCH 059/168] This to-do item done. --- stb_resample.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index bd0171a..73dd28d 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -25,8 +25,6 @@ TODO: Installable filters - Specify with (s0, t0) X (s1, t1) what area of the source image to use, - at sub-pixel level Specify wrap and filter modes independently for each axis Resize that respects alpha test coverage (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: From 8063ea0952a495cce33bb14a8198cb738a953a01 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 31 Jul 2014 17:20:00 -0700 Subject: [PATCH 060/168] Specify a channel as having premultiplied alpha and use it to un-premultiply all other channels before resampling. --- stb_resample.h | 66 +++++++++++++++++++++++++++++++++++++++++-------- tests/resample_test.cpp | 45 +++++++++++++++++++++++++++++---- 2 files changed, 96 insertions(+), 15 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 73dd28d..b76988c 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -51,12 +51,13 @@ // result = stbr_resize_arbitrary(input_data, input_w, input_h, input_stride_in_bytes, // output_data, output_w, output_h, output_stride_in_bytes, // s0, t0, s1, t1, -// channels, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, +// channels, premultiplied_alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, // extra_memory, memory_required); // free(extra_memory); // // input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. // 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. +// premultiplied_alpha_channel is 0 if there are no premultiplied alpha channels. If nonzero, the specified channel will be divided from all other channels before resampling, then multiplied back in after. // Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. // stbr_resize_arbitrary() will not allocate any memory, it will use the memory you pass in to do its work. @@ -155,7 +156,7 @@ extern "C" { STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, + int channels, int premultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes); @@ -256,6 +257,7 @@ typedef struct float vertical_scale; int channels; + int premul_alpha_channel; stbr_type type; stbr_filter filter; stbr_edge edge; @@ -708,6 +710,7 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) { int x, c; int channels = stbr_info->channels; + int premul_alpha_channel = stbr_info->premul_alpha_channel; int type = stbr_info->type; int colorspace = stbr_info->colorspace; int input_w = stbr_info->input_w; @@ -770,6 +773,17 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) STBR_UNIMPLEMENTED("Unknown type/colorspace/channels combination."); break; } + + if (premul_alpha_channel) + { + for (c = 0; c < channels; c++) + { + if (c == premul_alpha_channel) + continue; + + decode_buffer[decode_texel_index + c] /= decode_buffer[decode_texel_index + premul_alpha_channel]; + } + } } } @@ -920,9 +934,21 @@ static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffe } -static stbr_inline void stbr__encode_scanline(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, int decode) +static stbr_inline void stbr__encode_scanline(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, int premul_alpha_channel, int decode) { int n; + + if (premul_alpha_channel) + { + for (n = 0; n < channels; n++) + { + if (n == premul_alpha_channel) + continue; + + encode_buffer[encode_texel_index + n] *= encode_buffer[encode_texel_index + premul_alpha_channel]; + } + } + switch (decode) { case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): @@ -969,6 +995,18 @@ static stbr_inline void stbr__encode_scanline(void* output_buffer, int output_te STBR_UNIMPLEMENTED("Unknown type/colorspace/channels combination."); break; } + + // Put it back the way it was in case this is a ring buffer. + if (premul_alpha_channel) + { + for (n = 0; n < channels; n++) + { + if (n == premul_alpha_channel) + continue; + + encode_buffer[encode_texel_index + n] /= encode_buffer[encode_texel_index + premul_alpha_channel]; + } + } } static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) @@ -978,6 +1016,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; float* vertical_coefficients = stbr_info->vertical_coefficients; int channels = stbr_info->channels; + int premul_alpha_channel = stbr_info->premul_alpha_channel; int type = stbr_info->type; int colorspace = stbr_info->colorspace; int kernel_texel_width = stbr__get_filter_texel_width_vertical(stbr_info); @@ -1023,7 +1062,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i encode_buffer[c] += ring_buffer_entry[in_texel_index + c] * coefficient; } - stbr__encode_scanline(output_data, out_texel_index, encode_buffer, 0, channels, decode); + stbr__encode_scanline(output_data, out_texel_index, encode_buffer, 0, channels, premul_alpha_channel, decode); } } @@ -1130,6 +1169,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s { int output_stride = stbr_info->output_stride_bytes / stbr__type_size[stbr_info->type]; int channels = stbr_info->channels; + int premul_alpha_channel = stbr_info->premul_alpha_channel; int type = stbr_info->type; int colorspace = stbr_info->colorspace; int output_w = stbr_info->output_w; @@ -1156,7 +1196,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s int ring_texel_index = texel_index; int output_texel_index = output_row + texel_index; - stbr__encode_scanline(output_data, output_texel_index, ring_buffer_entry, ring_texel_index, channels, decode); + stbr__encode_scanline(output_data, output_texel_index, ring_buffer_entry, ring_texel_index, channels, premul_alpha_channel, decode); } } @@ -1221,7 +1261,7 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, + int channels, int premul_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes) { stbr_size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); @@ -1260,6 +1300,11 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input if (s1 > 1 || s0 < 0 || t1 > 1 || t0 < 0) return 0; + STBR_ASSERT(premul_alpha_channel >= 0 && premul_alpha_channel < channels); + + if (premul_alpha_channel < 0 || premul_alpha_channel >= channels) + return 0; + STBR_ASSERT(tempmem); if (!tempmem) @@ -1296,6 +1341,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input stbr_info->vertical_shift = t0 * input_h / (t1 - t0); stbr_info->channels = channels; + stbr_info->premul_alpha_channel = premul_alpha_channel; stbr_info->type = type; stbr_info->filter = filter; stbr_info->edge = edge; @@ -1396,7 +1442,7 @@ STBRDEF int stbr_resize_srgb_uint8(const stbr_uint8* input_data, int input_w, in if (!extra_memory) return 0; - int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); free(extra_memory); @@ -1413,7 +1459,7 @@ STBRDEF int stbr_resize_srgb_uint16(const stbr_uint16* input_data, int input_w, if (!extra_memory) return 0; - int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); free(extra_memory); @@ -1430,7 +1476,7 @@ STBRDEF int stbr_resize_srgb_uint32(const stbr_uint32* input_data, int input_w, if (!extra_memory) return 0; - int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); free(extra_memory); @@ -1447,7 +1493,7 @@ STBRDEF int stbr_resize_srgb_float(const float* input_data, int input_w, int inp if (!extra_memory) return 0; - int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); free(extra_memory); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 3857d54..d4be392 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -91,7 +91,7 @@ int main(int argc, char** argv) printf("Average: %dms\n", average); #else - stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); #endif free(extra_memory); @@ -123,7 +123,7 @@ void resize_image(const char* filename, float width_percent, float height_percen size_t memory_required = stbr_calculate_memory(w, h, out_w, out_h, 0, 0, 1, 1, n, filter); void* extra_memory = malloc(memory_required); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, 0, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); free(extra_memory); stbi_image_free(input_data); @@ -157,7 +157,7 @@ void test_format(const char* file, float width_percent, float height_percent, st size_t required = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, 1, 1, n, STBR_FILTER_CATMULLROM); void* extra_memory = malloc(required); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); free(extra_memory); free(T_data); @@ -201,7 +201,7 @@ void test_float(const char* file, float width_percent, float height_percent, stb size_t required = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, 1, 1, n, STBR_FILTER_CATMULLROM); void* extra_memory = malloc(required); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); free(extra_memory); free(T_data); @@ -267,7 +267,7 @@ void test_subpixel(const char* file, float width_percent, float height_percent, size_t tempmem_size = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, s1, t1, n, STBR_FILTER_CATMULLROM); void* tempmem = malloc(tempmem_size); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); free(tempmem); @@ -280,8 +280,43 @@ void test_subpixel(const char* file, float width_percent, float height_percent, free(output_data); } +void test_premul(const char* file) +{ + int w, h, n; + unsigned char* input_data = stbi_load(file, &w, &h, &n, 4); + n = 4; + + // Premultiply the first texel. + input_data[0] /= 2; + input_data[1] /= 2; + input_data[2] /= 2; + input_data[3] = 255 / 2; + + int new_w = (int)(w * .5); + int new_h = (int)(h * .5); + + unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); + + size_t tempmem_size = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, 1, 1, n, STBR_FILTER_CATMULLROM); + void* tempmem = malloc(tempmem_size); + + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); + + free(tempmem); + + stbi_image_free(input_data); + + char output[200]; + sprintf(output, "test-output/premul-%s", file); + stbi_write_png(output, new_w, new_h, n, output_data, 0); + + free(output_data); +} + void test_suite() { + test_premul("barbara.png"); + for (int i = 0; i < 10; i++) test_subpixel("barbara.png", 0.5f, 0.5f, (float)i / 10, 1); From 21c7c8f5d909ad09e85b1cee545872764fd813d9 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 31 Jul 2014 18:04:57 -0700 Subject: [PATCH 061/168] Another stab at the api, offering classes of functions for different common tasks. --- stb_resample.h | 311 +++++++++++++++++++++++++++++++++++++++++------- tests/resample_test.cpp | 16 +-- 2 files changed, 273 insertions(+), 54 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index b76988c..0bbe6fa 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -7,8 +7,8 @@ #define STBR_ASSERT(x) to avoid using assert.h. - #define STBR_NO_MALLOC to avoid using stdlib.h and malloc. This will remove - all resize functions except stbr_resize_arbitrary() from the API. + #define STBR_MALLOC and STBR_FREE to avoid using stdlib.h malloc. This will apply + to all functions except stbr_resize_arbitrary(), which doesn't allocate memory. QUICK NOTES: Written with emphasis on usage and speed. Only the resize operation is @@ -37,29 +37,51 @@ #define STBR_INCLUDE_STB_RESAMPLE_H // Basic usage: -// result = stbr_resize_srgb_uint8(input_data, input_w, input_h, output_data, output_w, output_h, channels, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); +// result = stbr_resize_uint8_srgb(input_data, input_w, input_h, output_data, output_w, output_h, channels, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); +// * input_data is your supplied texels. +// * output_data will be the resized texels. It should be of size output_w * output_h * channels +// * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. +// * If you're unsure of which filter to use, Catmull-Rom is a good upsampling filter and Mitchell is a good downsampling filter. +// +// +// Data types provided: uint8, uint16, uint32, float. +// +// +// Other function groups are provided, one for each data type, for more advanced functionality: +// +// stbr_resize_type_premultiplied(input_data, input_w, input_h, output_data, output_w, output_h, channels, premultiplied_alpha_channel, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP) +// * premultiplied_alpha_channel is 0 if there are no premultiplied alpha channels. If nonzero, the specified channel will be divided from all other channels before resampling, then multiplied back in after. +// +// stbr_resize_type_subpixel(input_data, input_w, input_h, output_data, output_w, output_h, s0, t0, s1, t1, channels, filter, edge) +// * 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. +// +// +// All functionality is offered in this function: // -// input_data is your supplied texels. -// output_data will be the resized texels. It should be of size output_w * output_h * channels -// Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. -// If you're unsure of which filter to use, Catmull-Rom is a good upsampling filter and Mitchell is a good downsampling filter. - - -// Advanced usage: -// size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, channels, STBR_FILTER_CATMULLROM); -// void* extra_memory = malloc(memory_required); // Any memory allocation method of your choosing // result = stbr_resize_arbitrary(input_data, input_w, input_h, input_stride_in_bytes, // output_data, output_w, output_h, output_stride_in_bytes, // s0, t0, s1, t1, +// channels, premultiplied_alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); +// +// +// Control over memory allocation is offered like so: +// +// size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, STBR_FILTER_CATMULLROM); +// void* extra_memory = malloc(memory_required); // Any memory allocation method of your choosing +// result = stbr_resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, +// output_data, output_w, output_h, output_stride_in_bytes, +// s0, t0, s1, t1, // channels, premultiplied_alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, // extra_memory, memory_required); // free(extra_memory); // -// input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. -// 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. -// premultiplied_alpha_channel is 0 if there are no premultiplied alpha channels. If nonzero, the specified channel will be divided from all other channels before resampling, then multiplied back in after. -// Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. -// stbr_resize_arbitrary() will not allocate any memory, it will use the memory you pass in to do its work. +// * input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. +// * 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. +// * premultiplied_alpha_channel is 0 if there are no premultiplied alpha channels. If nonzero, the specified channel will be divided from all other channels before resampling, then multiplied back in after. +// * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. +// * stbr_resize_advanced() will not allocate any memory, it will use the memory you pass in to do its work. Memory required grows +// approximately linearly with input and output size, but with discontinuities at input_w == output_w and input_h == output_height. +// stbr_calculate_memory() is deterministic on its inputs. typedef enum @@ -121,30 +143,71 @@ extern "C" { #define STBRDEF extern #endif -#ifndef STBR_NO_MALLOC - ////////////////////////////////////////////////////////////////////////////// // // PRIMARY API - sRGB type-safe image resizing. // - STBRDEF int stbr_resize_srgb_uint8(const stbr_uint8* input_data, int input_w, int input_h, + STBRDEF int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge); - STBRDEF int stbr_resize_srgb_uint16(const stbr_uint16* input_data, int input_w, int input_h, + STBRDEF int stbr_resize_uint16_srgb(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge); - STBRDEF int stbr_resize_srgb_uint32(const stbr_uint32* input_data, int input_w, int input_h, + STBRDEF int stbr_resize_uint32_srgb(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge); - STBRDEF int stbr_resize_srgb_float(const float* input_data, int input_w, int input_h, + STBRDEF int stbr_resize_float_srgb(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge); -#endif // STBR_NO_MALLOC + + STBRDEF int stbr_resize_uint8_premultiplied(const stbr_uint8* input_data, int input_w, int input_h, + stbr_uint8* output_data, int output_w, int output_h, + int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge); + + STBRDEF int stbr_resize_uint16_premultiplied(const stbr_uint16* input_data, int input_w, int input_h, + stbr_uint16* output_data, int output_w, int output_h, + int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge); + + STBRDEF int stbr_resize_uint32_premultiplied(const stbr_uint32* input_data, int input_w, int input_h, + stbr_uint32* output_data, int output_w, int output_h, + int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge); + + STBRDEF int stbr_resize_float_premultiplied(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge); + + + STBRDEF int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, + stbr_uint8* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbr_filter filter, stbr_edge edge); + + STBRDEF int stbr_resize_uint16_subpixel(const stbr_uint16* input_data, int input_w, int input_h, + stbr_uint16* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbr_filter filter, stbr_edge edge); + + STBRDEF int stbr_resize_uint32_subpixel(const stbr_uint32* input_data, int input_w, int input_h, + stbr_uint32* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbr_filter filter, stbr_edge edge); + + STBRDEF int stbr_resize_float_subpixel(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbr_filter filter, stbr_edge edge); + + + STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, + int channels, int premultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + ////////////////////////////////////////////////////////////////////////////// // @@ -153,7 +216,7 @@ extern "C" { STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter); - STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + STBRDEF int stbr_resize_advanced(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, int channels, int premultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, @@ -191,8 +254,11 @@ extern "C" { #include -#ifndef STBR_NO_MALLOC +#ifndef STBR_MALLOC #include + +#define STBR_MALLOC malloc +#define STBR_FREE free #endif @@ -1258,7 +1324,7 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) stbr__empty_ring_buffer(stbr_info, stbr_info->output_h); } -STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, +STBRDEF int stbr_resize_advanced(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, int channels, int premul_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, @@ -1430,78 +1496,231 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w return info_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size; } -#ifndef STBR_NO_MALLOC - -STBRDEF int stbr_resize_srgb_uint8(const stbr_uint8* input_data, int input_w, int input_h, +STBRDEF stbr_inline int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); - void* extra_memory = malloc(memory_required); + void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); - free(extra_memory); + STBR_FREE(extra_memory); return result; } -STBRDEF int stbr_resize_srgb_uint16(const stbr_uint16* input_data, int input_w, int input_h, +STBRDEF stbr_inline int stbr_resize_uint16_srgb(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); - void* extra_memory = malloc(memory_required); + void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); - free(extra_memory); + STBR_FREE(extra_memory); return result; } -STBRDEF int stbr_resize_srgb_uint32(const stbr_uint32* input_data, int input_w, int input_h, +STBRDEF stbr_inline int stbr_resize_uint32_srgb(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); - void* extra_memory = malloc(memory_required); + void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); - free(extra_memory); + STBR_FREE(extra_memory); return result; } -STBRDEF int stbr_resize_srgb_float(const float* input_data, int input_w, int input_h, +STBRDEF stbr_inline int stbr_resize_float_srgb(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); - void* extra_memory = malloc(memory_required); + void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); - free(extra_memory); + STBR_FREE(extra_memory); return result; } -#endif // STBR_NO_MALLOC +STBRDEF stbr_inline int stbr_resize_uint8_premultiplied(const stbr_uint8* input_data, int input_w, int input_h, + stbr_uint8* output_data, int output_w, int output_h, + int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + void* extra_memory = STBR_MALLOC(memory_required); + if (!extra_memory) + return 0; + + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiplied_alpha_channel, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + STBR_FREE(extra_memory); + + return result; +} + +STBRDEF stbr_inline int stbr_resize_uint16_premultiplied(const stbr_uint16* input_data, int input_w, int input_h, + stbr_uint16* output_data, int output_w, int output_h, + int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + void* extra_memory = STBR_MALLOC(memory_required); + + if (!extra_memory) + return 0; + + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiplied_alpha_channel, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + STBR_FREE(extra_memory); + + return result; +} + +STBRDEF stbr_inline int stbr_resize_uint32_premultiplied(const stbr_uint32* input_data, int input_w, int input_h, + stbr_uint32* output_data, int output_w, int output_h, + int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + void* extra_memory = STBR_MALLOC(memory_required); + + if (!extra_memory) + return 0; + + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiplied_alpha_channel, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + STBR_FREE(extra_memory); + + return result; +} + +STBRDEF stbr_inline int stbr_resize_float_premultiplied(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + void* extra_memory = STBR_MALLOC(memory_required); + + if (!extra_memory) + return 0; + + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiplied_alpha_channel, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + STBR_FREE(extra_memory); + + return result; +} + +STBRDEF stbr_inline int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, + stbr_uint8* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbr_filter filter, stbr_edge edge) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + void* extra_memory = STBR_MALLOC(memory_required); + + if (!extra_memory) + return 0; + + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + STBR_FREE(extra_memory); + + return result; +} + +STBRDEF stbr_inline int stbr_resize_uint16_subpixel(const stbr_uint16* input_data, int input_w, int input_h, + stbr_uint16* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbr_filter filter, stbr_edge edge) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + void* extra_memory = STBR_MALLOC(memory_required); + + if (!extra_memory) + return 0; + + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + STBR_FREE(extra_memory); + + return result; +} + +STBRDEF stbr_inline int stbr_resize_uint32_subpixel(const stbr_uint32* input_data, int input_w, int input_h, + stbr_uint32* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbr_filter filter, stbr_edge edge) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + void* extra_memory = STBR_MALLOC(memory_required); + + if (!extra_memory) + return 0; + + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + STBR_FREE(extra_memory); + + return result; +} + +STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbr_filter filter, stbr_edge edge) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + void* extra_memory = STBR_MALLOC(memory_required); + + if (!extra_memory) + return 0; + + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + + STBR_FREE(extra_memory); + + return result; +} + +STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, + int channels, int premultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) +{ + size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + void* extra_memory = STBR_MALLOC(memory_required); + + if (!extra_memory) + return 0; + + int result = stbr_resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, premultiplied_alpha_channel, type, filter, edge, colorspace, extra_memory, memory_required); + + STBR_FREE(extra_memory); + + return result; +} #endif // STB_RESAMPLE_IMPLEMENTATION diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index d4be392..d68ece2 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -79,7 +79,7 @@ int main(int argc, char** argv) { ftime(&initial_time_millis); for (int i = 0; i < 100; i++) - stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + stbr_resize_advanced(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); ftime(&final_time_millis); long lapsed_ms = (long)(final_time_millis.time - initial_time_millis.time) * 1000 + (final_time_millis.millitm - initial_time_millis.millitm); printf("Resample: %dms\n", lapsed_ms); @@ -91,7 +91,7 @@ int main(int argc, char** argv) printf("Average: %dms\n", average); #else - stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + stbr_resize_advanced(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); #endif free(extra_memory); @@ -123,7 +123,7 @@ void resize_image(const char* filename, float width_percent, float height_percen size_t memory_required = stbr_calculate_memory(w, h, out_w, out_h, 0, 0, 1, 1, n, filter); void* extra_memory = malloc(memory_required); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, 0, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); + stbr_resize_advanced(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, 0, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); free(extra_memory); stbi_image_free(input_data); @@ -157,7 +157,7 @@ void test_format(const char* file, float width_percent, float height_percent, st size_t required = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, 1, 1, n, STBR_FILTER_CATMULLROM); void* extra_memory = malloc(required); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); + stbr_resize_advanced(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); free(extra_memory); free(T_data); @@ -201,7 +201,7 @@ void test_float(const char* file, float width_percent, float height_percent, stb size_t required = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, 1, 1, n, STBR_FILTER_CATMULLROM); void* extra_memory = malloc(required); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); + stbr_resize_advanced(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); free(extra_memory); free(T_data); @@ -239,7 +239,7 @@ void test_channels(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * channels * sizeof(unsigned char)); - stbr_resize_srgb_uint8(channels_data, w, h, output_data, new_w, new_h, channels, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); + stbr_resize_uint8_srgb(channels_data, w, h, output_data, new_w, new_h, channels, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); free(channels_data); stbi_image_free(input_data); @@ -267,7 +267,7 @@ void test_subpixel(const char* file, float width_percent, float height_percent, size_t tempmem_size = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, s1, t1, n, STBR_FILTER_CATMULLROM); void* tempmem = malloc(tempmem_size); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); + stbr_resize_advanced(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); free(tempmem); @@ -300,7 +300,7 @@ void test_premul(const char* file) size_t tempmem_size = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, 1, 1, n, STBR_FILTER_CATMULLROM); void* tempmem = malloc(tempmem_size); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); + stbr_resize_advanced(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); free(tempmem); From 13acfca8298989ef23b08224fdc11080461bd3f8 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 31 Jul 2014 18:46:00 -0700 Subject: [PATCH 062/168] I had the whole premultiply thing backwards. --- stb_resample.h | 64 ++++++++++++++++++++++++------------------------- tests/resample_test.cpp | 26 +++++++++++++------- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 0bbe6fa..b3cc92c 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -49,8 +49,8 @@ // // Other function groups are provided, one for each data type, for more advanced functionality: // -// stbr_resize_type_premultiplied(input_data, input_w, input_h, output_data, output_w, output_h, channels, premultiplied_alpha_channel, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP) -// * premultiplied_alpha_channel is 0 if there are no premultiplied alpha channels. If nonzero, the specified channel will be divided from all other channels before resampling, then multiplied back in after. +// stbr_resize_type_premultiply(input_data, input_w, input_h, output_data, output_w, output_h, channels, premultiply_alpha_channel, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP) +// * premultiply_alpha_channel - if nonzero, the specified channel will be multiplied into all other channels before resampling, then divided back out after. // // stbr_resize_type_subpixel(input_data, input_w, input_h, output_data, output_w, output_h, s0, t0, s1, t1, channels, filter, edge) // * 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. @@ -61,7 +61,7 @@ // result = stbr_resize_arbitrary(input_data, input_w, input_h, input_stride_in_bytes, // output_data, output_w, output_h, output_stride_in_bytes, // s0, t0, s1, t1, -// channels, premultiplied_alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); +// channels, premultiply_alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); // // // Control over memory allocation is offered like so: @@ -71,13 +71,13 @@ // result = stbr_resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, // output_data, output_w, output_h, output_stride_in_bytes, // s0, t0, s1, t1, -// channels, premultiplied_alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, +// channels, premultiply_alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, // extra_memory, memory_required); // free(extra_memory); // // * input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. // * 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. -// * premultiplied_alpha_channel is 0 if there are no premultiplied alpha channels. If nonzero, the specified channel will be divided from all other channels before resampling, then multiplied back in after. +// * premultiply_alpha_channel - if nonzero, the specified channel will be multiplied into all other channels before resampling, then divided back out after. // * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. // * stbr_resize_advanced() will not allocate any memory, it will use the memory you pass in to do its work. Memory required grows // approximately linearly with input and output size, but with discontinuities at input_w == output_w and input_h == output_height. @@ -165,21 +165,21 @@ extern "C" { int channels, stbr_filter filter, stbr_edge edge); - STBRDEF int stbr_resize_uint8_premultiplied(const stbr_uint8* input_data, int input_w, int input_h, + STBRDEF int stbr_resize_uint8_premultiply(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, - int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge); + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge); - STBRDEF int stbr_resize_uint16_premultiplied(const stbr_uint16* input_data, int input_w, int input_h, + STBRDEF int stbr_resize_uint16_premultiply(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, - int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge); + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge); - STBRDEF int stbr_resize_uint32_premultiplied(const stbr_uint32* input_data, int input_w, int input_h, + STBRDEF int stbr_resize_uint32_premultiply(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, - int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge); + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge); - STBRDEF int stbr_resize_float_premultiplied(const float* input_data, int input_w, int input_h, + STBRDEF int stbr_resize_float_premultiply(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, - int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge); + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge); STBRDEF int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, @@ -206,7 +206,7 @@ extern "C" { STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int premultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + int channels, int premultiply_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); ////////////////////////////////////////////////////////////////////////////// @@ -219,7 +219,7 @@ extern "C" { STBRDEF int stbr_resize_advanced(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int premultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, + int channels, int premultiply_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes); @@ -847,7 +847,7 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) if (c == premul_alpha_channel) continue; - decode_buffer[decode_texel_index + c] /= decode_buffer[decode_texel_index + premul_alpha_channel]; + decode_buffer[decode_texel_index + c] *= decode_buffer[decode_texel_index + premul_alpha_channel]; } } } @@ -1011,7 +1011,7 @@ static stbr_inline void stbr__encode_scanline(void* output_buffer, int output_te if (n == premul_alpha_channel) continue; - encode_buffer[encode_texel_index + n] *= encode_buffer[encode_texel_index + premul_alpha_channel]; + encode_buffer[encode_texel_index + n] /= encode_buffer[encode_texel_index + premul_alpha_channel]; } } @@ -1070,7 +1070,7 @@ static stbr_inline void stbr__encode_scanline(void* output_buffer, int output_te if (n == premul_alpha_channel) continue; - encode_buffer[encode_texel_index + n] /= encode_buffer[encode_texel_index + premul_alpha_channel]; + encode_buffer[encode_texel_index + n] *= encode_buffer[encode_texel_index + premul_alpha_channel]; } } } @@ -1564,9 +1564,9 @@ STBRDEF stbr_inline int stbr_resize_float_srgb(const float* input_data, int inpu return result; } -STBRDEF stbr_inline int stbr_resize_uint8_premultiplied(const stbr_uint8* input_data, int input_w, int input_h, +STBRDEF stbr_inline int stbr_resize_uint8_premultiply(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, - int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge) + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1574,16 +1574,16 @@ STBRDEF stbr_inline int stbr_resize_uint8_premultiplied(const stbr_uint8* input_ if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiplied_alpha_channel, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); return result; } -STBRDEF stbr_inline int stbr_resize_uint16_premultiplied(const stbr_uint16* input_data, int input_w, int input_h, +STBRDEF stbr_inline int stbr_resize_uint16_premultiply(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, - int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge) + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1591,16 +1591,16 @@ STBRDEF stbr_inline int stbr_resize_uint16_premultiplied(const stbr_uint16* inpu if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiplied_alpha_channel, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); return result; } -STBRDEF stbr_inline int stbr_resize_uint32_premultiplied(const stbr_uint32* input_data, int input_w, int input_h, +STBRDEF stbr_inline int stbr_resize_uint32_premultiply(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, - int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge) + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1608,16 +1608,16 @@ STBRDEF stbr_inline int stbr_resize_uint32_premultiplied(const stbr_uint32* inpu if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiplied_alpha_channel, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); return result; } -STBRDEF stbr_inline int stbr_resize_float_premultiplied(const float* input_data, int input_w, int input_h, +STBRDEF stbr_inline int stbr_resize_float_premultiply(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, - int channels, int premultiplied_alpha_channel, stbr_filter filter, stbr_edge edge) + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1625,7 +1625,7 @@ STBRDEF stbr_inline int stbr_resize_float_premultiplied(const float* input_data, if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiplied_alpha_channel, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1707,7 +1707,7 @@ STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int premultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) + int channels, int premultiply_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1715,7 +1715,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, premultiplied_alpha_channel, type, filter, edge, colorspace, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, premultiply_alpha_channel, type, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index d68ece2..e7b5ec2 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -286,11 +286,14 @@ void test_premul(const char* file) unsigned char* input_data = stbi_load(file, &w, &h, &n, 4); n = 4; - // Premultiply the first texel. - input_data[0] /= 2; - input_data[1] /= 2; - input_data[2] /= 2; - input_data[3] = 255 / 2; + // Set alpha for the top half. + for (int x = 0; x < w; x++) + { + for (int y = 0; y < h / 2; y++) + input_data[(y*w + x)*n + 3] = input_data[(y*w + x)*n + 0]; + } + + stbi_write_png("test-output/premul-original.png", w, h, n, input_data, 0); int new_w = (int)(w * .5); int new_h = (int)(h * .5); @@ -302,14 +305,19 @@ void test_premul(const char* file) stbr_resize_advanced(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); - free(tempmem); - - stbi_image_free(input_data); - char output[200]; sprintf(output, "test-output/premul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); + stbr_resize_advanced(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); + + sprintf(output, "test-output/nopremul-%s", file); + stbi_write_png(output, new_w, new_h, n, output_data, 0); + + free(tempmem); + + stbi_image_free(input_data); + free(output_data); } From d75488b0e88137ea969c29aa74c2860dbb402950 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 31 Jul 2014 19:00:48 -0700 Subject: [PATCH 063/168] Do the multiply inline, it should be a tad faster and not corrupt our data. --- stb_resample.h | 63 ++++++++++++++++++++++++++----------------------- tests/resample_test.cpp | 4 ++-- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index b3cc92c..b8347f5 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -1003,76 +1003,81 @@ static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffe static stbr_inline void stbr__encode_scanline(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, int premul_alpha_channel, int decode) { int n; + float divide_alpha = 1; if (premul_alpha_channel) - { - for (n = 0; n < channels; n++) - { - if (n == premul_alpha_channel) - continue; - - encode_buffer[encode_texel_index + n] /= encode_buffer[encode_texel_index + premul_alpha_channel]; - } - } + divide_alpha = encode_buffer[encode_texel_index + premul_alpha_channel]; switch (decode) { case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) - ((unsigned char*)output_buffer)[output_texel_index + n] = (unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 255); + { + float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; + ((unsigned char*)output_buffer)[output_texel_index + n] = (unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel) * 255); + } break; case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) - ((unsigned char*)output_buffer)[output_texel_index + n] = stbr__linear_uchar_to_srgb_uchar[(unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 255)]; + { + float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; + ((unsigned char*)output_buffer)[output_texel_index + n] = stbr__linear_uchar_to_srgb_uchar[(unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel) * 255)]; + } break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) - ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 65535); + { + float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; + ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel) * 65535); + } break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) - ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n])) * 65535); + { + float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; + ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel)) * 65535); + } break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__saturate(encode_buffer[encode_texel_index + n])) * 4294967295); + { + float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; + ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel)) * 4294967295); + } break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n]))) * 4294967295); + { + float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; + ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel))) * 4294967295); + } break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) - ((float*)output_buffer)[output_texel_index + n] = stbr__saturate(encode_buffer[encode_texel_index + n]); + { + float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; + ((float*)output_buffer)[output_texel_index + n] = stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel); + } break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) - ((float*)output_buffer)[output_texel_index + n] = stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n])); + { + float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; + ((float*)output_buffer)[output_texel_index + n] = stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel)); + } break; default: STBR_UNIMPLEMENTED("Unknown type/colorspace/channels combination."); break; } - - // Put it back the way it was in case this is a ring buffer. - if (premul_alpha_channel) - { - for (n = 0; n < channels; n++) - { - if (n == premul_alpha_channel) - continue; - - encode_buffer[encode_texel_index + n] *= encode_buffer[encode_texel_index + premul_alpha_channel]; - } - } } static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index e7b5ec2..98e7166 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -295,8 +295,8 @@ void test_premul(const char* file) stbi_write_png("test-output/premul-original.png", w, h, n, input_data, 0); - int new_w = (int)(w * .5); - int new_h = (int)(h * .5); + int new_w = (int)(w * .1); + int new_h = (int)(h * .1); unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); From e05ebdbf1ef1153cfd73ba290f0498a5be4016c0 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 31 Jul 2014 19:37:42 -0700 Subject: [PATCH 064/168] My guess is people who care about premultiply also care about color space. --- stb_resample.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index b8347f5..9d2da3f 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -49,7 +49,7 @@ // // Other function groups are provided, one for each data type, for more advanced functionality: // -// stbr_resize_type_premultiply(input_data, input_w, input_h, output_data, output_w, output_h, channels, premultiply_alpha_channel, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP) +// stbr_resize_type_premultiply(input_data, input_w, input_h, output_data, output_w, output_h, channels, premultiply_alpha_channel, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB) // * premultiply_alpha_channel - if nonzero, the specified channel will be multiplied into all other channels before resampling, then divided back out after. // // stbr_resize_type_subpixel(input_data, input_w, input_h, output_data, output_w, output_h, s0, t0, s1, t1, channels, filter, edge) @@ -167,19 +167,19 @@ extern "C" { STBRDEF int stbr_resize_uint8_premultiply(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge); + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); STBRDEF int stbr_resize_uint16_premultiply(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge); + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); STBRDEF int stbr_resize_uint32_premultiply(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge); + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); STBRDEF int stbr_resize_float_premultiply(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge); + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); STBRDEF int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, @@ -1571,7 +1571,7 @@ STBRDEF stbr_inline int stbr_resize_float_srgb(const float* input_data, int inpu STBRDEF stbr_inline int stbr_resize_uint8_premultiply(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge) + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1579,7 +1579,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_premultiply(const stbr_uint8* input_da if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1588,7 +1588,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_premultiply(const stbr_uint8* input_da STBRDEF stbr_inline int stbr_resize_uint16_premultiply(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge) + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1596,7 +1596,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_premultiply(const stbr_uint16* input_ if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT16, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1605,7 +1605,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_premultiply(const stbr_uint16* input_ STBRDEF stbr_inline int stbr_resize_uint32_premultiply(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge) + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1613,7 +1613,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_premultiply(const stbr_uint32* input_ if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT32, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1622,7 +1622,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_premultiply(const stbr_uint32* input_ STBRDEF stbr_inline int stbr_resize_float_premultiply(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge) + int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1630,7 +1630,7 @@ STBRDEF stbr_inline int stbr_resize_float_premultiply(const float* input_data, i if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_FLOAT, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); From 3077bf5023de69d74c738e04172153db86850b10 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 11 Aug 2014 01:50:16 -0700 Subject: [PATCH 065/168] wrap long data lines --- stb_resample.h | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 9d2da3f..9900c7c 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -372,11 +372,44 @@ static stbr_inline float stbr__saturate(float x) } static float stbr__srgb_uchar_to_linear_float[256] = { - 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, 0.982251f, 0.991102f, 1.0f + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f }; static unsigned char stbr__linear_uchar_to_srgb_uchar[256] = { - 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255 + 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, + 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, + 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, + 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, + 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, + 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, + 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, + 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, + 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, + 251, 251, 252, 252, 253, 253, 254, 254, 255 }; static unsigned char stbr__type_size[] = { From a0537bfd04a04def4d49da1652a7dcbcdfdb10c0 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 11 Aug 2014 01:56:47 -0700 Subject: [PATCH 066/168] Allow compiling as C pre-C99 (don't rely on declare-anywhere) --- stb_resample.h | 64 +++++++++++++++++++++++++++++----------------- tests/test_c_compilation.c | 2 ++ 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 9900c7c..a451568 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -1134,12 +1134,14 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); + int n0,n1, output_row_index; + stbr__calculate_coefficients_upsample(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); - int n0 = vertical_contributors->n0; - int n1 = vertical_contributors->n1; + n0 = vertical_contributors->n0; + n1 = vertical_contributors->n1; - int output_row_index = n * stbr_info->output_stride_bytes / stbr__type_size[type]; + output_row_index = n * stbr_info->output_stride_bytes / stbr__type_size[type]; STBR_DEBUG_ASSERT(stbr__use_height_upsampling(stbr_info)); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); @@ -1187,12 +1189,13 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int int ring_buffer_first_scanline = stbr_info->ring_buffer_first_scanline; int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); + int n0,n1,max_n; stbr__calculate_coefficients_downsample(stbr_info, stbr_info->vertical_scale, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); - int n0 = vertical_contributors->n0; - int n1 = vertical_contributors->n1; - int max_n = stbr__min(n1, output_h - 1); + n0 = vertical_contributors->n0; + n1 = vertical_contributors->n1; + max_n = stbr__min(n1, output_h - 1); STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); STBR_DEBUG_ASSERT(n0 >= in_first_scanline); @@ -1368,6 +1371,8 @@ STBRDEF int stbr_resize_advanced(const void* input_data, int input_w, int input_ int channels, int premul_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes) { + stbr__info* stbr_info = (stbr__info*)tempmem; + stbr_size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w * stbr__type_size[type]; @@ -1421,8 +1426,6 @@ STBRDEF int stbr_resize_advanced(const void* input_data, int input_w, int input_ memset(tempmem, 0, tempmem_size_in_bytes); - stbr__info* stbr_info = (stbr__info*)tempmem; - stbr_info->input_data = input_data; stbr_info->input_w = input_w; stbr_info->input_h = input_h; @@ -1503,9 +1506,6 @@ STBRDEF int stbr_resize_advanced(const void* input_data, int input_w, int input_ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter) { - STBR_ASSERT(filter != 0); - STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); - float horizontal_scale = ((float)output_w / input_w) / (s1 - s0); float vertical_scale = ((float)output_h / input_h) / (t1 - t0); @@ -1521,6 +1521,9 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w int ring_buffer_size = output_w * channels * filter_height * sizeof(float); int encode_buffer_size = channels * sizeof(float); + STBR_ASSERT(filter != 0); + STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); // this now happens too late + if (stbr__use_upsampling(horizontal_scale)) // The horizontal buffer is for when we're downsampling the height and we // can't output the result of sampling the decode buffer directly into the @@ -1538,13 +1541,14 @@ STBRDEF stbr_inline int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int stbr_uint8* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1555,13 +1559,14 @@ STBRDEF stbr_inline int stbr_resize_uint16_srgb(const stbr_uint16* input_data, i stbr_uint16* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1572,13 +1577,14 @@ STBRDEF stbr_inline int stbr_resize_uint32_srgb(const stbr_uint32* input_data, i stbr_uint32* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1589,13 +1595,14 @@ STBRDEF stbr_inline int stbr_resize_float_srgb(const float* input_data, int inpu float* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1606,13 +1613,14 @@ STBRDEF stbr_inline int stbr_resize_uint8_premultiply(const stbr_uint8* input_da stbr_uint8* output_data, int output_w, int output_h, int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1623,13 +1631,14 @@ STBRDEF stbr_inline int stbr_resize_uint16_premultiply(const stbr_uint16* input_ stbr_uint16* output_data, int output_w, int output_h, int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT16, filter, edge, colorspace, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT16, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1640,13 +1649,14 @@ STBRDEF stbr_inline int stbr_resize_uint32_premultiply(const stbr_uint32* input_ stbr_uint32* output_data, int output_w, int output_h, int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT32, filter, edge, colorspace, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT32, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1657,13 +1667,14 @@ STBRDEF stbr_inline int stbr_resize_float_premultiply(const float* input_data, i float* output_data, int output_w, int output_h, int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_FLOAT, filter, edge, colorspace, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_FLOAT, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1675,13 +1686,14 @@ STBRDEF stbr_inline int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1693,13 +1705,14 @@ STBRDEF stbr_inline int stbr_resize_uint16_subpixel(const stbr_uint16* input_dat float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1711,13 +1724,14 @@ STBRDEF stbr_inline int stbr_resize_uint32_subpixel(const stbr_uint32* input_dat float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1729,13 +1743,14 @@ STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1747,13 +1762,14 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input float s0, float t0, float s1, float t1, int channels, int premultiply_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - int result = stbr_resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, premultiply_alpha_channel, type, filter, edge, colorspace, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, premultiply_alpha_channel, type, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); diff --git a/tests/test_c_compilation.c b/tests/test_c_compilation.c index 416416e..764f911 100644 --- a/tests/test_c_compilation.c +++ b/tests/test_c_compilation.c @@ -5,6 +5,7 @@ #define STB_DIVIDE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION #define STB_HERRINGBONE_WANG_TILE_IMEPLEMENTATIOn +#define STB_RESAMPLE_IMPLEMENTATION #include "stb_herringbone_wang_tile.h" #include "stb_image.h" @@ -13,3 +14,4 @@ #include "stb_dxt.h" #include "stb_c_lexer.h" #include "stb_divide.h" +#include "stb_resample.h" \ No newline at end of file From 25fae8c67c9b9a9b2f181b0433b7cb35fddf3019 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 11 Aug 2014 02:13:26 -0700 Subject: [PATCH 067/168] Avoid divides in encode_scanline if not doing unpremultiply. Rename stb__encode_scanline to stb__encode_pixel --- stb_resample.h | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index a451568..68b53e7 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -257,8 +257,8 @@ extern "C" { #ifndef STBR_MALLOC #include -#define STBR_MALLOC malloc -#define STBR_FREE free +#define STBR_MALLOC(x) malloc(x) +#define STBR_FREE(x) free(x) #endif @@ -1033,77 +1033,74 @@ static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffe } -static stbr_inline void stbr__encode_scanline(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, int premul_alpha_channel, int decode) +static stbr_inline void stbr__encode_pixel(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, int premul_alpha_channel, int decode) { int n; float divide_alpha = 1; - if (premul_alpha_channel) - divide_alpha = encode_buffer[encode_texel_index + premul_alpha_channel]; + if (premul_alpha_channel) { + float alpha = encode_buffer[encode_texel_index + premul_alpha_channel]; + float reciprocal_alpha = alpha ? 1.0 / alpha : 0; + for (n = 0; n < channels; n++) + if (n != premul_alpha_channel) + encode_buffer[encode_texel_index + n] *= reciprocal_alpha; + } switch (decode) { case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) { - float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; - ((unsigned char*)output_buffer)[output_texel_index + n] = (unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel) * 255); + ((unsigned char*)output_buffer)[output_texel_index + n] = (unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 255); } break; case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) { - float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; - ((unsigned char*)output_buffer)[output_texel_index + n] = stbr__linear_uchar_to_srgb_uchar[(unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel) * 255)]; + ((unsigned char*)output_buffer)[output_texel_index + n] = stbr__linear_uchar_to_srgb_uchar[(unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 255)]; } break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) { - float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; - ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel) * 65535); + ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 65535); } break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) { - float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; - ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel)) * 65535); + ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n])) * 65535); } break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) { - float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; - ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel)) * 4294967295); + ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__saturate(encode_buffer[encode_texel_index + n])) * 4294967295); } break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) { - float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; - ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel))) * 4294967295); + ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n]))) * 4294967295); } break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) { - float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; - ((float*)output_buffer)[output_texel_index + n] = stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel); + ((float*)output_buffer)[output_texel_index + n] = stbr__saturate(encode_buffer[encode_texel_index + n]); } break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) { - float divide_alpha_channel = (n == premul_alpha_channel) ? 1 : divide_alpha; - ((float*)output_buffer)[output_texel_index + n] = stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n] / divide_alpha_channel)); + ((float*)output_buffer)[output_texel_index + n] = stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n])); } break; @@ -1168,7 +1165,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i encode_buffer[c] += ring_buffer_entry[in_texel_index + c] * coefficient; } - stbr__encode_scanline(output_data, out_texel_index, encode_buffer, 0, channels, premul_alpha_channel, decode); + stbr__encode_pixel(output_data, out_texel_index, encode_buffer, 0, channels, premul_alpha_channel, decode); } } @@ -1303,7 +1300,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s int ring_texel_index = texel_index; int output_texel_index = output_row + texel_index; - stbr__encode_scanline(output_data, output_texel_index, ring_buffer_entry, ring_texel_index, channels, premul_alpha_channel, decode); + stbr__encode_pixel(output_data, output_texel_index, ring_buffer_entry, ring_texel_index, channels, premul_alpha_channel, decode); } } From fc4ca11a52d8a65187f88cece15a69bdfdbfe0e1 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 11 Aug 2014 02:16:23 -0700 Subject: [PATCH 068/168] Fix places in previous commits where tabs got replaced with spaces --- stb_resample.h | 100 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 68b53e7..7c2e54b 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -373,43 +373,43 @@ static stbr_inline float stbr__saturate(float x) static float stbr__srgb_uchar_to_linear_float[256] = { 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, - 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, - 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, - 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, - 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, - 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, - 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, - 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, - 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, - 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, - 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, - 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, - 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, - 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, - 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, - 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, - 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, - 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, - 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, - 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, - 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, - 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, - 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, - 0.982251f, 0.991102f, 1.0f + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f }; static unsigned char stbr__linear_uchar_to_srgb_uchar[256] = { 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, - 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, - 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, - 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, - 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, - 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, - 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, - 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, - 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, - 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, - 251, 251, 252, 252, 253, 253, 254, 254, 255 + 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, + 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, + 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, + 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, + 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, + 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, + 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, + 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, + 251, 251, 252, 252, 253, 253, 254, 254, 255 }; static unsigned char stbr__type_size[] = { @@ -1040,11 +1040,11 @@ static stbr_inline void stbr__encode_pixel(void* output_buffer, int output_texel if (premul_alpha_channel) { float alpha = encode_buffer[encode_texel_index + premul_alpha_channel]; - float reciprocal_alpha = alpha ? 1.0 / alpha : 0; + float reciprocal_alpha = alpha ? 1.0f / alpha : 0; for (n = 0; n < channels; n++) if (n != premul_alpha_channel) encode_buffer[encode_texel_index + n] *= reciprocal_alpha; - } + } switch (decode) { @@ -1131,7 +1131,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); - int n0,n1, output_row_index; + int n0,n1, output_row_index; stbr__calculate_coefficients_upsample(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); @@ -1186,7 +1186,7 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int int ring_buffer_first_scanline = stbr_info->ring_buffer_first_scanline; int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); - int n0,n1,max_n; + int n0,n1,max_n; stbr__calculate_coefficients_downsample(stbr_info, stbr_info->vertical_scale, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); @@ -1545,7 +1545,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1556,7 +1556,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_srgb(const stbr_uint16* input_data, i stbr_uint16* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { - int result; + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1574,7 +1574,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_srgb(const stbr_uint32* input_data, i stbr_uint32* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { - int result; + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1592,7 +1592,7 @@ STBRDEF stbr_inline int stbr_resize_float_srgb(const float* input_data, int inpu float* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { - int result; + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1610,7 +1610,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_premultiply(const stbr_uint8* input_da stbr_uint8* output_data, int output_w, int output_h, int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { - int result; + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1628,7 +1628,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_premultiply(const stbr_uint16* input_ stbr_uint16* output_data, int output_w, int output_h, int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { - int result; + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1646,7 +1646,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_premultiply(const stbr_uint32* input_ stbr_uint32* output_data, int output_w, int output_h, int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { - int result; + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1664,7 +1664,7 @@ STBRDEF stbr_inline int stbr_resize_float_premultiply(const float* input_data, i float* output_data, int output_w, int output_h, int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { - int result; + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1683,7 +1683,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) { - int result; + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1702,7 +1702,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_subpixel(const stbr_uint16* input_dat float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) { - int result; + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1721,7 +1721,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_subpixel(const stbr_uint32* input_dat float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) { - int result; + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1740,7 +1740,7 @@ STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) { - int result; + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); @@ -1759,7 +1759,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input float s0, float t0, float s1, float t1, int channels, int premultiply_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { - int result; + int result; size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); From 259c92b550d780bfd382ddb7ed00877da3719d4c Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 11 Aug 2014 02:17:40 -0700 Subject: [PATCH 069/168] replace "texel" with "pixel" in identifiers --- stb_resample.h | 286 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 143 insertions(+), 143 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 7c2e54b..602d928 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -38,8 +38,8 @@ // Basic usage: // result = stbr_resize_uint8_srgb(input_data, input_w, input_h, output_data, output_w, output_h, channels, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); -// * input_data is your supplied texels. -// * output_data will be the resized texels. It should be of size output_w * output_h * channels +// * input_data is your supplied pixels. +// * output_data will be the resized pixels. It should be of size output_w * output_h * channels // * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. // * If you're unsure of which filter to use, Catmull-Rom is a good upsampling filter and Mitchell is a good downsampling filter. // @@ -295,12 +295,12 @@ typedef struct float support; } stbr__filter_info; -// When upsampling, the contributors are which source texels contribute. -// When downsampling, the contributors are which destination texels are contributed to. +// When upsampling, the contributors are which source pixels contribute. +// When downsampling, the contributors are which destination pixels are contributed to. typedef struct { - int n0; // First contributing texel - int n1; // Last contributing texel + int n0; // First contributing pixel + int n1; // Last contributing pixel } stbr__contributors; typedef struct @@ -317,8 +317,8 @@ typedef struct float s0, t0, s1, t1; - float horizontal_shift; // Units: output texels - float vertical_shift; // Units: output texels + float horizontal_shift; // Units: output pixels + float vertical_shift; // Units: output pixels float horizontal_scale; float vertical_scale; @@ -335,12 +335,12 @@ typedef struct stbr__contributors vertical_contributors; float* vertical_coefficients; - int decode_buffer_texels; + int decode_buffer_pixels; float* decode_buffer; float* horizontal_buffer; - int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbr__get_filter_texel_width(filter) + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbr__get_filter_pixel_width(filter) int ring_buffer_first_scanline; int ring_buffer_last_scanline; int ring_buffer_begin_index; @@ -520,7 +520,7 @@ stbr_inline static int stbr__use_height_upsampling(stbr__info* stbr_info) // This is the maximum number of input samples that can affect an output sample // with the given filter -stbr_inline static int stbr__get_filter_texel_width(stbr_filter filter, int input_w, int output_w, float scale) +stbr_inline static int stbr__get_filter_pixel_width(stbr_filter filter, int input_w, int output_w, float scale) { STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); @@ -531,31 +531,31 @@ stbr_inline static int stbr__get_filter_texel_width(stbr_filter filter, int inpu return (int)ceil(stbr__filter_info_table[filter].support * 2 / scale); } -stbr_inline static int stbr__get_filter_texel_width_horizontal(stbr__info* stbr_info) +stbr_inline static int stbr__get_filter_pixel_width_horizontal(stbr__info* stbr_info) { - return stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale); + return stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale); } -stbr_inline static int stbr__get_filter_texel_width_vertical(stbr__info* stbr_info) +stbr_inline static int stbr__get_filter_pixel_width_vertical(stbr__info* stbr_info) { - return stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h, stbr_info->vertical_scale); + return stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h, stbr_info->vertical_scale); } // This is how much to expand buffers to account for filters seeking outside // the image boundaries. -stbr_inline static int stbr__get_filter_texel_margin(stbr_filter filter, int input_w, int output_w, float scale) +stbr_inline static int stbr__get_filter_pixel_margin(stbr_filter filter, int input_w, int output_w, float scale) { - return stbr__get_filter_texel_width(filter, input_w, output_w, scale) / 2; + return stbr__get_filter_pixel_width(filter, input_w, output_w, scale) / 2; } -stbr_inline static int stbr__get_filter_texel_margin_horizontal(stbr__info* stbr_info) +stbr_inline static int stbr__get_filter_pixel_margin_horizontal(stbr__info* stbr_info) { - return stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale) / 2; + return stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale) / 2; } -stbr_inline static int stbr__get_filter_texel_margin_vertical(stbr__info* stbr_info) +stbr_inline static int stbr__get_filter_pixel_margin_vertical(stbr__info* stbr_info) { - return stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h, stbr_info->vertical_scale) / 2; + return stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h, stbr_info->vertical_scale) / 2; } stbr_inline static int stbr__get_horizontal_contributors_noinfo(stbr_filter filter, int input_w, int output_w, float horizontal_scale) @@ -563,7 +563,7 @@ stbr_inline static int stbr__get_horizontal_contributors_noinfo(stbr_filter filt if (stbr__use_upsampling(horizontal_scale)) return output_w; else - return (input_w + stbr__get_filter_texel_margin(filter, input_w, output_w, horizontal_scale) * 2); + return (input_w + stbr__get_filter_pixel_margin(filter, input_w, output_w, horizontal_scale) * 2); } stbr_inline static int stbr__get_horizontal_contributors(stbr__info* stbr_info) @@ -573,7 +573,7 @@ stbr_inline static int stbr__get_horizontal_contributors(stbr__info* stbr_info) stbr_inline static int stbr__get_total_coefficients_noinfo(stbr_filter filter, int input_w, int output_w, float horizontal_scale) { - return stbr__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * stbr__get_filter_texel_width(filter, input_w, output_w, horizontal_scale); + return stbr__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * stbr__get_filter_pixel_width(filter, input_w, output_w, horizontal_scale); } stbr_inline static int stbr__get_total_coefficients(stbr__info* stbr_info) @@ -589,7 +589,7 @@ stbr_inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_in stbr_inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, int c) { - return &stbr_info->horizontal_coefficients[stbr__get_filter_texel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale)*n + c]; + return &stbr_info->horizontal_coefficients[stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale)*n + c]; } stbr_inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) @@ -646,54 +646,54 @@ stbr_inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) } } -// What input texels contribute to this output texel? -static void stbr__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_texel, int* in_last_texel, float* in_center_of_out) +// What input pixels contribute to this output pixel? +static void stbr__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) { - float out_texel_center = (float)n + 0.5f; - float out_texel_influence_lowerbound = out_texel_center - out_filter_radius; - float out_texel_influence_upperbound = out_texel_center + out_filter_radius; + float out_pixel_center = (float)n + 0.5f; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; - float in_texel_influence_lowerbound = (out_texel_influence_lowerbound + out_shift) / scale_ratio; - float in_texel_influence_upperbound = (out_texel_influence_upperbound + out_shift) / scale_ratio; + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; - *in_center_of_out = (out_texel_center + out_shift) / scale_ratio; - *in_first_texel = (int)(floor(in_texel_influence_lowerbound + 0.5)); - *in_last_texel = (int)(floor(in_texel_influence_upperbound - 0.5)); + *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; + *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); + *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); } -// What output texels does this input texel contribute to? -static void stbr__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_texel, int* out_last_texel, float* out_center_of_in) +// What output pixels does this input pixel contribute to? +static void stbr__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) { - float in_texel_center = (float)n + 0.5f; - float in_texel_influence_lowerbound = in_texel_center - in_pixels_radius; - float in_texel_influence_upperbound = in_texel_center + in_pixels_radius; + float in_pixel_center = (float)n + 0.5f; + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; - float out_texel_influence_lowerbound = in_texel_influence_lowerbound * scale_ratio - out_shift; - float out_texel_influence_upperbound = in_texel_influence_upperbound * scale_ratio - out_shift; + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; - *out_center_of_in = in_texel_center * scale_ratio - out_shift; - *out_first_texel = (int)(floor(out_texel_influence_lowerbound + 0.5)); - *out_last_texel = (int)(floor(out_texel_influence_upperbound - 0.5)); + *out_center_of_in = in_pixel_center * scale_ratio - out_shift; + *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); + *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); } -static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_first_texel, int in_last_texel, float in_center_of_out, stbr__contributors* contributor, float* coefficient_group) +static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbr__contributors* contributor, float* coefficient_group) { int i; float total_filter = 0; float filter_scale; stbr_filter filter = stbr_info->filter; - STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr__get_filter_texel_width_horizontal(stbr_info)); + STBR_DEBUG_ASSERT(in_last_pixel - in_first_pixel <= stbr__get_filter_pixel_width_horizontal(stbr_info)); - contributor->n0 = in_first_texel; - contributor->n1 = in_last_texel; + contributor->n0 = in_first_pixel; + contributor->n1 = in_last_pixel; STBR_DEBUG_ASSERT(contributor->n1 >= contributor->n0); - for (i = 0; i <= in_last_texel - in_first_texel; i++) + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) { - float in_texel_center = (float)(i + in_first_texel) + 0.5f; - total_filter += coefficient_group[i] = stbr__filter_info_table[filter].kernel(in_center_of_out - in_texel_center); + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + total_filter += coefficient_group[i] = stbr__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center); } STBR_DEBUG_ASSERT(total_filter > 0.9); @@ -702,26 +702,26 @@ static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_ // Make sure the sum of all coefficients is 1. filter_scale = 1 / total_filter; - for (i = 0; i <= in_last_texel - in_first_texel; i++) + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) coefficient_group[i] *= filter_scale; } -static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float scale_ratio, int out_first_texel, int out_last_texel, float out_center_of_in, stbr__contributors* contributor, float* coefficient_group) +static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbr__contributors* contributor, float* coefficient_group) { int i; stbr_filter filter = stbr_info->filter; - STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr__get_filter_texel_width_horizontal(stbr_info)); + STBR_DEBUG_ASSERT(out_last_pixel - out_first_pixel <= stbr__get_filter_pixel_width_horizontal(stbr_info)); - contributor->n0 = out_first_texel; - contributor->n1 = out_last_texel; + contributor->n0 = out_first_pixel; + contributor->n1 = out_last_pixel; STBR_DEBUG_ASSERT(contributor->n1 >= contributor->n0); - for (i = 0; i <= out_last_texel - out_first_texel; i++) + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) { - float in_texel_center = (float)(i + out_first_texel) + 0.5f; - coefficient_group[i] = stbr__filter_info_table[filter].kernel((out_center_of_in - in_texel_center)/scale_ratio); + float in_pixel_center = (float)(i + out_first_pixel) + 0.5f; + coefficient_group[i] = stbr__filter_info_table[filter].kernel((out_center_of_in - in_pixel_center)/scale_ratio); } } @@ -763,31 +763,31 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) { float out_pixels_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; - // Looping through out texels + // Looping through out pixels for (n = 0; n < total_contributors; n++) { - float in_center_of_out; // Center of the current out texel in the in texel space - int in_first_texel, in_last_texel; + float in_center_of_out; // Center of the current out pixel in the in pixel space + int in_first_pixel, in_last_pixel; - stbr__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, stbr_info->horizontal_shift, &in_first_texel, &in_last_texel, &in_center_of_out); + stbr__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, stbr_info->horizontal_shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); - stbr__calculate_coefficients_upsample(stbr_info, in_first_texel, in_last_texel, in_center_of_out, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); + stbr__calculate_coefficients_upsample(stbr_info, in_first_pixel, in_last_pixel, in_center_of_out, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); } } else { float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; - // Looping through in texels + // Looping through in pixels for (n = 0; n < total_contributors; n++) { - float out_center_of_in; // Center of the current out texel in the in texel space - int out_first_texel, out_last_texel; - int n_adjusted = n - stbr__get_filter_texel_margin_horizontal(stbr_info); + float out_center_of_in; // Center of the current out pixel in the in pixel space + int out_first_pixel, out_last_pixel; + int n_adjusted = n - stbr__get_filter_pixel_margin_horizontal(stbr_info); - stbr__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, stbr_info->horizontal_shift, &out_first_texel, &out_last_texel, &out_center_of_in); + stbr__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, stbr_info->horizontal_shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); - stbr__calculate_coefficients_downsample(stbr_info, scale_ratio, out_first_texel, out_last_texel, out_center_of_in, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); + stbr__calculate_coefficients_downsample(stbr_info, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); } #ifdef STBR_DEBUG @@ -800,7 +800,7 @@ static float* stbr__get_decode_buffer(stbr__info* stbr_info) { // The 0 index of the decode buffer starts after the margin. This makes // it okay to use negative indexes on the decode buffer. - return &stbr_info->decode_buffer[stbr__get_filter_texel_margin_horizontal(stbr_info) * stbr_info->channels]; + return &stbr_info->decode_buffer[stbr__get_filter_pixel_margin_horizontal(stbr_info) * stbr_info->channels]; } #define STBR__DECODE(type, colorspace) ((type) * (STBR_MAX_COLORSPACES) + (colorspace)) @@ -818,54 +818,54 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) float* decode_buffer = stbr__get_decode_buffer(stbr_info); stbr_edge edge = stbr_info->edge; int in_buffer_row_index = stbr__edge_wrap(edge, n, stbr_info->input_h) * input_stride; - int max_x = input_w + stbr__get_filter_texel_margin_horizontal(stbr_info); + int max_x = input_w + stbr__get_filter_pixel_margin_horizontal(stbr_info); int decode = STBR__DECODE(type, colorspace); - for (x = -stbr__get_filter_texel_margin_horizontal(stbr_info); x < max_x; x++) + for (x = -stbr__get_filter_pixel_margin_horizontal(stbr_info); x < max_x; x++) { - int decode_texel_index = x * channels; - int input_texel_index = in_buffer_row_index + stbr__edge_wrap(edge, x, input_w) * channels; + int decode_pixel_index = x * channels; + int input_pixel_index = in_buffer_row_index + stbr__edge_wrap(edge, x, input_w) * channels; switch (decode) { case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): for (c = 0; c < channels; c++) - decode_buffer[decode_texel_index + c] = ((float)((const unsigned char*)input_data)[input_texel_index + c]) / 255; + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255; break; case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): for (c = 0; c < channels; c++) - decode_buffer[decode_texel_index + c] = stbr__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_texel_index + c]]; + decode_buffer[decode_pixel_index + c] = stbr__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): for (c = 0; c < channels; c++) - decode_buffer[decode_texel_index + c] = ((float)((const unsigned short*)input_data)[input_texel_index + c]) / 65535; + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535; break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): for (c = 0; c < channels; c++) - decode_buffer[decode_texel_index + c] = stbr__srgb_to_linear(((float)((const unsigned short*)input_data)[input_texel_index + c]) / 65535); + decode_buffer[decode_pixel_index + c] = stbr__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): for (c = 0; c < channels; c++) - decode_buffer[decode_texel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_texel_index + c]) / 4294967295); + decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295); break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): for (c = 0; c < channels; c++) - decode_buffer[decode_texel_index + c] = stbr__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_texel_index + c]) / 4294967295)); + decode_buffer[decode_pixel_index + c] = stbr__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): for (c = 0; c < channels; c++) - decode_buffer[decode_texel_index + c] = ((const float*)input_data)[input_texel_index + c]; + decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): for (c = 0; c < channels; c++) - decode_buffer[decode_texel_index + c] = stbr__srgb_to_linear(((const float*)input_data)[input_texel_index + c]); + decode_buffer[decode_pixel_index + c] = stbr__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); break; default: @@ -880,7 +880,7 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) if (c == premul_alpha_channel) continue; - decode_buffer[decode_texel_index + c] *= decode_buffer[decode_texel_index + premul_alpha_channel]; + decode_buffer[decode_pixel_index + c] *= decode_buffer[decode_pixel_index + premul_alpha_channel]; } } } @@ -903,7 +903,7 @@ static float* stbr__add_empty_ring_buffer_entry(stbr__info* stbr_info, int n) } else { - ring_buffer_index = (stbr_info->ring_buffer_begin_index + (stbr_info->ring_buffer_last_scanline - stbr_info->ring_buffer_first_scanline) + 1) % stbr__get_filter_texel_width_vertical(stbr_info); + ring_buffer_index = (stbr_info->ring_buffer_begin_index + (stbr_info->ring_buffer_last_scanline - stbr_info->ring_buffer_first_scanline) + 1) % stbr__get_filter_pixel_width_vertical(stbr_info); STBR_DEBUG_ASSERT(ring_buffer_index != stbr_info->ring_buffer_begin_index); } @@ -920,7 +920,7 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo { int x, k; int output_w = stbr_info->output_w; - int kernel_texel_width = stbr__get_filter_texel_width_horizontal(stbr_info); + int kernel_pixel_width = stbr__get_filter_pixel_width_horizontal(stbr_info); int channels = stbr_info->channels; float* decode_buffer = stbr__get_decode_buffer(stbr_info); stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; @@ -931,25 +931,25 @@ static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, flo int n0 = horizontal_contributors[x].n0; int n1 = horizontal_contributors[x].n1; - int out_texel_index = x * channels; - int coefficient_group_index = x * kernel_texel_width; + int out_pixel_index = x * channels; + int coefficient_group_index = x * kernel_pixel_width; int coefficient_counter = 0; STBR_DEBUG_ASSERT(n1 >= n0); - STBR_DEBUG_ASSERT(n0 >= -stbr__get_filter_texel_margin_horizontal(stbr_info)); - STBR_DEBUG_ASSERT(n1 >= -stbr__get_filter_texel_margin_horizontal(stbr_info)); - STBR_DEBUG_ASSERT(n0 < stbr_info->input_w + stbr__get_filter_texel_margin_horizontal(stbr_info)); - STBR_DEBUG_ASSERT(n1 < stbr_info->input_w + stbr__get_filter_texel_margin_horizontal(stbr_info)); + STBR_DEBUG_ASSERT(n0 >= -stbr__get_filter_pixel_margin_horizontal(stbr_info)); + STBR_DEBUG_ASSERT(n1 >= -stbr__get_filter_pixel_margin_horizontal(stbr_info)); + STBR_DEBUG_ASSERT(n0 < stbr_info->input_w + stbr__get_filter_pixel_margin_horizontal(stbr_info)); + STBR_DEBUG_ASSERT(n1 < stbr_info->input_w + stbr__get_filter_pixel_margin_horizontal(stbr_info)); for (k = n0; k <= n1; k++) { int coefficient_index = coefficient_group_index + (coefficient_counter++); - int in_texel_index = k * channels; + int in_pixel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_index]; int c; for (c = 0; c < channels; c++) - output_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; } } } @@ -959,13 +959,13 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, f int x, k; int input_w = stbr_info->input_w; int output_w = stbr_info->output_w; - int kernel_texel_width = stbr__get_filter_texel_width_horizontal(stbr_info); + int kernel_pixel_width = stbr__get_filter_pixel_width_horizontal(stbr_info); int channels = stbr_info->channels; float* decode_buffer = stbr__get_decode_buffer(stbr_info); stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; float* horizontal_coefficients = stbr_info->horizontal_coefficients; - int filter_texel_margin = stbr__get_filter_texel_margin_horizontal(stbr_info); - int max_x = input_w + filter_texel_margin * 2; + int filter_pixel_margin = stbr__get_filter_pixel_margin_horizontal(stbr_info); + int max_x = input_w + filter_pixel_margin * 2; STBR_DEBUG_ASSERT(!stbr__use_width_upsampling(stbr_info)); @@ -974,23 +974,23 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, f int n0 = horizontal_contributors[x].n0; int n1 = horizontal_contributors[x].n1; - int in_x = x - filter_texel_margin; - int in_texel_index = in_x * channels; + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * channels; int max_n = stbr__min(n1, output_w-1); - int coefficient_group = x*kernel_texel_width; + int coefficient_group = x*kernel_pixel_width; STBR_DEBUG_ASSERT(n1 >= n0); - // Using min and max to avoid writing into invalid texels. + // Using min and max to avoid writing into invalid pixels. for (k = stbr__max(n0, 0); k <= max_n; k++) { int coefficient_index = (k - n0) + coefficient_group; - int out_texel_index = k * channels; + int out_pixel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_index]; int c; for (c = 0; c < channels; c++) - output_buffer[out_texel_index + c] += decode_buffer[in_texel_index + c] * coefficient; + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; } } } @@ -1033,17 +1033,17 @@ static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffe } -static stbr_inline void stbr__encode_pixel(void* output_buffer, int output_texel_index, float* encode_buffer, int encode_texel_index, int channels, int premul_alpha_channel, int decode) +static stbr_inline void stbr__encode_pixel(void* output_buffer, int output_pixel_index, float* encode_buffer, int encode_pixel_index, int channels, int premul_alpha_channel, int decode) { int n; float divide_alpha = 1; if (premul_alpha_channel) { - float alpha = encode_buffer[encode_texel_index + premul_alpha_channel]; + float alpha = encode_buffer[encode_pixel_index + premul_alpha_channel]; float reciprocal_alpha = alpha ? 1.0f / alpha : 0; for (n = 0; n < channels; n++) if (n != premul_alpha_channel) - encode_buffer[encode_texel_index + n] *= reciprocal_alpha; + encode_buffer[encode_pixel_index + n] *= reciprocal_alpha; } switch (decode) @@ -1051,56 +1051,56 @@ static stbr_inline void stbr__encode_pixel(void* output_buffer, int output_texel case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) { - ((unsigned char*)output_buffer)[output_texel_index + n] = (unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 255); + ((unsigned char*)output_buffer)[output_pixel_index + n] = (unsigned char)(stbr__saturate(encode_buffer[encode_pixel_index + n]) * 255); } break; case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) { - ((unsigned char*)output_buffer)[output_texel_index + n] = stbr__linear_uchar_to_srgb_uchar[(unsigned char)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 255)]; + ((unsigned char*)output_buffer)[output_pixel_index + n] = stbr__linear_uchar_to_srgb_uchar[(unsigned char)(stbr__saturate(encode_buffer[encode_pixel_index + n]) * 255)]; } break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) { - ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__saturate(encode_buffer[encode_texel_index + n]) * 65535); + ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbr__saturate(encode_buffer[encode_pixel_index + n]) * 65535); } break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) { - ((unsigned short*)output_buffer)[output_texel_index + n] = (unsigned short)(stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n])) * 65535); + ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_pixel_index + n])) * 65535); } break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) { - ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__saturate(encode_buffer[encode_texel_index + n])) * 4294967295); + ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbr__saturate(encode_buffer[encode_pixel_index + n])) * 4294967295); } break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) { - ((unsigned int*)output_buffer)[output_texel_index + n] = (unsigned int)(((double)stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n]))) * 4294967295); + ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_pixel_index + n]))) * 4294967295); } break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) { - ((float*)output_buffer)[output_texel_index + n] = stbr__saturate(encode_buffer[encode_texel_index + n]); + ((float*)output_buffer)[output_pixel_index + n] = stbr__saturate(encode_buffer[encode_pixel_index + n]); } break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) { - ((float*)output_buffer)[output_texel_index + n] = stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_texel_index + n])); + ((float*)output_buffer)[output_pixel_index + n] = stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_pixel_index + n])); } break; @@ -1120,7 +1120,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i int premul_alpha_channel = stbr_info->premul_alpha_channel; int type = stbr_info->type; int colorspace = stbr_info->colorspace; - int kernel_texel_width = stbr__get_filter_texel_width_vertical(stbr_info); + int kernel_pixel_width = stbr__get_filter_pixel_width_vertical(stbr_info); void* output_data = stbr_info->output_data; float* encode_buffer = stbr_info->encode_buffer; int decode = STBR__DECODE(type, colorspace); @@ -1146,8 +1146,8 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i for (x = 0; x < output_w; x++) { - int in_texel_index = x * channels; - int out_texel_index = output_row_index + x * channels; + int in_pixel_index = x * channels; + int out_pixel_index = output_row_index + x * channels; int coefficient_counter = 0; STBR_DEBUG_ASSERT(n1 >= n0); @@ -1157,15 +1157,15 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i for (k = n0; k <= n1; k++) { int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_texel_width, ring_buffer_length); + float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_index]; int c; for (c = 0; c < channels; c++) - encode_buffer[c] += ring_buffer_entry[in_texel_index + c] * coefficient; + encode_buffer[c] += ring_buffer_entry[in_pixel_index + c] * coefficient; } - stbr__encode_pixel(output_data, out_texel_index, encode_buffer, 0, channels, premul_alpha_channel, decode); + stbr__encode_pixel(output_data, out_pixel_index, encode_buffer, 0, channels, premul_alpha_channel, decode); } } @@ -1177,7 +1177,7 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; float* vertical_coefficients = stbr_info->vertical_coefficients; int channels = stbr_info->channels; - int kernel_texel_width = stbr__get_filter_texel_width_vertical(stbr_info); + int kernel_pixel_width = stbr__get_filter_pixel_width_vertical(stbr_info); void* output_data = stbr_info->output_data; float* horizontal_buffer = stbr_info->horizontal_buffer; @@ -1204,16 +1204,16 @@ static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int { int coefficient_index = k - n0; - float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_texel_width, ring_buffer_length); + float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_index]; for (x = 0; x < output_w; x++) { - int in_texel_index = x * channels; + int in_pixel_index = x * channels; int c; for (c = 0; c < channels; c++) - ring_buffer_entry[in_texel_index + c] += horizontal_buffer[in_texel_index + c] * coefficient; + ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; } } } @@ -1233,7 +1233,7 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) stbr__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbr_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); - STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr__get_filter_texel_width_vertical(stbr_info)); + STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr__get_filter_pixel_width_vertical(stbr_info)); if (stbr_info->ring_buffer_begin_index >= 0) { @@ -1252,7 +1252,7 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info) else { stbr_info->ring_buffer_first_scanline++; - stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_texel_width_horizontal(stbr_info); + stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_pixel_width_horizontal(stbr_info); } } } @@ -1296,11 +1296,11 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s for (x = 0; x < output_w; x++) { - int texel_index = x * channels; - int ring_texel_index = texel_index; - int output_texel_index = output_row + texel_index; + int pixel_index = x * channels; + int ring_pixel_index = pixel_index; + int output_pixel_index = output_row + pixel_index; - stbr__encode_pixel(output_data, output_texel_index, ring_buffer_entry, ring_texel_index, channels, premul_alpha_channel, decode); + stbr__encode_pixel(output_data, output_pixel_index, ring_buffer_entry, ring_pixel_index, channels, premul_alpha_channel, decode); } } @@ -1316,7 +1316,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s else { stbr_info->ring_buffer_first_scanline++; - stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_texel_width_vertical(stbr_info); + stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_pixel_width_vertical(stbr_info); } } } @@ -1328,18 +1328,18 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) float scale_ratio = stbr_info->vertical_scale; int output_h = stbr_info->output_h; float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; - int max_y = stbr_info->input_h + stbr__get_filter_texel_margin_vertical(stbr_info); + int max_y = stbr_info->input_h + stbr__get_filter_pixel_margin_vertical(stbr_info); STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); - for (y = -stbr__get_filter_texel_margin_vertical(stbr_info); y < max_y; y++) + for (y = -stbr__get_filter_pixel_margin_vertical(stbr_info); y < max_y; y++) { float out_center_of_in; // Center of the current out scanline in the in scanline space int out_first_scanline, out_last_scanline; stbr__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbr_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); - STBR_DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbr__get_filter_texel_width_vertical(stbr_info)); + STBR_DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbr__get_filter_pixel_width_vertical(stbr_info)); if (out_last_scanline < 0 || out_first_scanline >= output_h) continue; @@ -1452,30 +1452,30 @@ STBRDEF int stbr_resize_advanced(const void* input_data, int input_w, int input_ stbr_info->colorspace = colorspace; stbr_info->ring_buffer_length_bytes = output_w * channels * sizeof(float); - stbr_info->decode_buffer_texels = input_w + stbr__get_filter_texel_margin_horizontal(stbr_info) * 2; + stbr_info->decode_buffer_pixels = input_w + stbr__get_filter_pixel_margin_horizontal(stbr_info) * 2; #define STBR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) stbr_info->horizontal_contributors = STBR__NEXT_MEMPTR(stbr_info, sizeof(stbr__info), stbr__contributors); stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, stbr__get_horizontal_contributors(stbr_info) * sizeof(stbr__contributors), float); stbr_info->vertical_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr__get_total_coefficients(stbr_info) * sizeof(float), float); - stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr__get_filter_texel_width_vertical(stbr_info) * sizeof(float), float); + stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr__get_filter_pixel_width_vertical(stbr_info) * sizeof(float), float); if (stbr__use_height_upsampling(stbr_info)) { stbr_info->horizontal_buffer = NULL; - stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, stbr_info->decode_buffer_texels * channels * sizeof(float), float); - stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width_horizontal(stbr_info), float); + stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, stbr_info->decode_buffer_pixels * channels * sizeof(float), float); + stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_pixel_width_horizontal(stbr_info), float); STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->encode_buffer, stbr_info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } else { - stbr_info->horizontal_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, stbr_info->decode_buffer_texels * channels * sizeof(float), float); + stbr_info->horizontal_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, stbr_info->decode_buffer_pixels * channels * sizeof(float), float); stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->horizontal_buffer, output_w * channels * sizeof(float), float); stbr_info->encode_buffer = NULL; - STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_texel_width_vertical(stbr_info), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_pixel_width_vertical(stbr_info), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } #undef STBR__NEXT_MEMPTR @@ -1506,14 +1506,14 @@ STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w float horizontal_scale = ((float)output_w / input_w) / (s1 - s0); float vertical_scale = ((float)output_h / input_h) / (t1 - t0); - int texel_margin = stbr__get_filter_texel_margin(filter, input_w, output_w, horizontal_scale); - int filter_height = stbr__get_filter_texel_width(filter, input_h, output_h, vertical_scale); + int pixel_margin = stbr__get_filter_pixel_margin(filter, input_w, output_w, horizontal_scale); + int filter_height = stbr__get_filter_pixel_width(filter, input_h, output_h, vertical_scale); int info_size = sizeof(stbr__info); int contributors_size = stbr__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(stbr__contributors); int horizontal_coefficients_size = stbr__get_total_coefficients_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(float); int vertical_coefficients_size = filter_height * sizeof(float); - int decode_buffer_size = (input_w + texel_margin*2) * channels * sizeof(float); + int decode_buffer_size = (input_w + pixel_margin*2) * channels * sizeof(float); int horizontal_buffer_size = output_w * channels * sizeof(float); int ring_buffer_size = output_w * channels * filter_height * sizeof(float); int encode_buffer_size = channels * sizeof(float); From 392585130c509182059bffab95494510a0ff266c Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 11 Aug 2014 02:23:39 -0700 Subject: [PATCH 070/168] Get rid of "advanced" API with explicit temp memory because the STBR_MALLOC interface is sufficient --- stb_resample.h | 93 +++++++++++++++++++++------------------------------------- 1 file changed, 33 insertions(+), 60 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 602d928..649fbe4 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -63,25 +63,12 @@ // s0, t0, s1, t1, // channels, premultiply_alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); // -// -// Control over memory allocation is offered like so: -// -// size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, STBR_FILTER_CATMULLROM); -// void* extra_memory = malloc(memory_required); // Any memory allocation method of your choosing -// result = stbr_resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, -// output_data, output_w, output_h, output_stride_in_bytes, -// s0, t0, s1, t1, -// channels, premultiply_alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, -// extra_memory, memory_required); -// free(extra_memory); -// // * input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. // * 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. // * premultiply_alpha_channel - if nonzero, the specified channel will be multiplied into all other channels before resampling, then divided back out after. // * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. -// * stbr_resize_advanced() will not allocate any memory, it will use the memory you pass in to do its work. Memory required grows -// approximately linearly with input and output size, but with discontinuities at input_w == output_w and input_h == output_height. -// stbr_calculate_memory() is deterministic on its inputs. +// * Memory required grows approximately linearly with input and output size, but with discontinuities at input_w == output_w and input_h == output_height. +// * To use temporary memory, define an STBR_MALLOC that returns the temp memory and make STBR_FREE do nothing--each function only ever allocates one block typedef enum @@ -208,21 +195,6 @@ extern "C" { float s0, float t0, float s1, float t1, int channels, int premultiply_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); - - ////////////////////////////////////////////////////////////////////////////// - // - // ADVANCED API - // - - STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter); - - STBRDEF int stbr_resize_advanced(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, - int channels, int premultiply_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, - void* tempmem, stbr_size_t tempmem_size_in_bytes); - - #ifdef __cplusplus } #endif @@ -349,7 +321,6 @@ typedef struct float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. } stbr__info; - static stbr_inline int stbr__min(int a, int b) { return a < b ? a : b; @@ -1362,7 +1333,9 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) stbr__empty_ring_buffer(stbr_info, stbr_info->output_h); } -STBRDEF int stbr_resize_advanced(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, +static stbr_size_t stbr__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter); + +static int stbr__resize_advanced(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, int channels, int premul_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, @@ -1370,7 +1343,7 @@ STBRDEF int stbr_resize_advanced(const void* input_data, int input_w, int input_ { stbr__info* stbr_info = (stbr__info*)tempmem; - stbr_size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + stbr_size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w * stbr__type_size[type]; int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w * stbr__type_size[type]; @@ -1501,7 +1474,7 @@ STBRDEF int stbr_resize_advanced(const void* input_data, int input_w, int input_ } -STBRDEF stbr_size_t stbr_calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter) +STBRDEF stbr_size_t stbr__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter) { float horizontal_scale = ((float)output_w / input_w) / (s1 - s0); float vertical_scale = ((float)output_h / input_h) / (t1 - t0); @@ -1539,13 +1512,13 @@ STBRDEF stbr_inline int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int int channels, stbr_filter filter, stbr_edge edge) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1557,13 +1530,13 @@ STBRDEF stbr_inline int stbr_resize_uint16_srgb(const stbr_uint16* input_data, i int channels, stbr_filter filter, stbr_edge edge) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1575,13 +1548,13 @@ STBRDEF stbr_inline int stbr_resize_uint32_srgb(const stbr_uint32* input_data, i int channels, stbr_filter filter, stbr_edge edge) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1593,13 +1566,13 @@ STBRDEF stbr_inline int stbr_resize_float_srgb(const float* input_data, int inpu int channels, stbr_filter filter, stbr_edge edge) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1611,13 +1584,13 @@ STBRDEF stbr_inline int stbr_resize_uint8_premultiply(const stbr_uint8* input_da int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1629,13 +1602,13 @@ STBRDEF stbr_inline int stbr_resize_uint16_premultiply(const stbr_uint16* input_ int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT16, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT16, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1647,13 +1620,13 @@ STBRDEF stbr_inline int stbr_resize_uint32_premultiply(const stbr_uint32* input_ int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT32, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT32, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1665,13 +1638,13 @@ STBRDEF stbr_inline int stbr_resize_float_premultiply(const float* input_data, i int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_FLOAT, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_FLOAT, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1684,13 +1657,13 @@ STBRDEF stbr_inline int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int channels, stbr_filter filter, stbr_edge edge) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1703,13 +1676,13 @@ STBRDEF stbr_inline int stbr_resize_uint16_subpixel(const stbr_uint16* input_dat int channels, stbr_filter filter, stbr_edge edge) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1722,13 +1695,13 @@ STBRDEF stbr_inline int stbr_resize_uint32_subpixel(const stbr_uint32* input_dat int channels, stbr_filter filter, stbr_edge edge) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1741,13 +1714,13 @@ STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int int channels, stbr_filter filter, stbr_edge edge) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1760,13 +1733,13 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input int channels, int premultiply_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; - size_t memory_required = stbr_calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); void* extra_memory = STBR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbr_resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, premultiply_alpha_channel, type, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, premultiply_alpha_channel, type, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); From 42556fec82f03f9061dc574e7ab542c28d22486e Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 11 Aug 2014 02:26:17 -0700 Subject: [PATCH 071/168] don't require manual synchronization of STBR_MAX_COLORSPACES/MAX_TYPES --- stb_resample.h | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 649fbe4..8a3fc28 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -89,23 +89,21 @@ typedef enum typedef enum { - STBR_COLORSPACE_LINEAR = 1, - STBR_COLORSPACE_SRGB = 2, - // If you add here, update STBR_MAX_COLORSPACES -} stbr_colorspace; + STBR_COLORSPACE_LINEAR, + STBR_COLORSPACE_SRGB, -#define STBR_MAX_COLORSPACES 2 + STBR_MAX_COLORSPACES, +} stbr_colorspace; typedef enum { - STBR_TYPE_UINT8 = 1, - STBR_TYPE_UINT16 = 2, - STBR_TYPE_UINT32 = 3, - STBR_TYPE_FLOAT = 4, - // If you add here, update STBR_MAX_TYPES and stbr__type_size -} stbr_type; + STBR_TYPE_UINT8 , + STBR_TYPE_UINT16, + STBR_TYPE_UINT32, + STBR_TYPE_FLOAT , -#define STBR_MAX_TYPES 4 + STBR_MAX_TYPES +} stbr_type; typedef unsigned char stbr_uint8; From f502cae91a1a49debe567fa4ad5ca0034b8fd7fe Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 11 Aug 2014 02:32:07 -0700 Subject: [PATCH 072/168] rename everything to do with premultiplied alpha since the whole point is that this is for handling *non*-premultiplied alpha (since correct handling of premultiplied alpha requires doing nothing) --- stb_resample.h | 88 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 8a3fc28..42400aa 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -49,8 +49,8 @@ // // Other function groups are provided, one for each data type, for more advanced functionality: // -// stbr_resize_type_premultiply(input_data, input_w, input_h, output_data, output_w, output_h, channels, premultiply_alpha_channel, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB) -// * premultiply_alpha_channel - if nonzero, the specified channel will be multiplied into all other channels before resampling, then divided back out after. +// stbr_resize_type_alphaweighted(input_data, input_w, input_h, output_data, output_w, output_h, channels, nonpremultiplied_alpha_channel, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB) +// * nonpremultiplied_alpha_channel - if nonnegative, the specified channel will be multiplied into all other channels before resampling, then divided back out after. // // stbr_resize_type_subpixel(input_data, input_w, input_h, output_data, output_w, output_h, s0, t0, s1, t1, channels, filter, edge) // * 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. @@ -61,11 +61,11 @@ // result = stbr_resize_arbitrary(input_data, input_w, input_h, input_stride_in_bytes, // output_data, output_w, output_h, output_stride_in_bytes, // s0, t0, s1, t1, -// channels, premultiply_alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); +// channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); // // * input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. // * 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. -// * premultiply_alpha_channel - if nonzero, the specified channel will be multiplied into all other channels before resampling, then divided back out after. +// * nonpremultiplied_alpha_channel - if nonnegative, the specified channel will be multiplied into all other channels before resampling, then divided back out after. // * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. // * Memory required grows approximately linearly with input and output size, but with discontinuities at input_w == output_w and input_h == output_height. // * To use temporary memory, define an STBR_MALLOC that returns the temp memory and make STBR_FREE do nothing--each function only ever allocates one block @@ -150,21 +150,21 @@ extern "C" { int channels, stbr_filter filter, stbr_edge edge); - STBRDEF int stbr_resize_uint8_premultiply(const stbr_uint8* input_data, int input_w, int input_h, + STBRDEF int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); - STBRDEF int stbr_resize_uint16_premultiply(const stbr_uint16* input_data, int input_w, int input_h, + STBRDEF int stbr_resize_uint16_alphaweighted(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); - STBRDEF int stbr_resize_uint32_premultiply(const stbr_uint32* input_data, int input_w, int input_h, + STBRDEF int stbr_resize_uint32_alphaweighted(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); - STBRDEF int stbr_resize_float_premultiply(const float* input_data, int input_w, int input_h, + STBRDEF int stbr_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); STBRDEF int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, @@ -191,7 +191,7 @@ extern "C" { STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int premultiply_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + int channels, int nonpremultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); #ifdef __cplusplus } @@ -293,7 +293,7 @@ typedef struct float vertical_scale; int channels; - int premul_alpha_channel; + int alpha_channel; stbr_type type; stbr_filter filter; stbr_edge edge; @@ -778,7 +778,7 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) { int x, c; int channels = stbr_info->channels; - int premul_alpha_channel = stbr_info->premul_alpha_channel; + int alpha_channel = stbr_info->alpha_channel; int type = stbr_info->type; int colorspace = stbr_info->colorspace; int input_w = stbr_info->input_w; @@ -842,14 +842,14 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) break; } - if (premul_alpha_channel) + if (alpha_channel) { for (c = 0; c < channels; c++) { - if (c == premul_alpha_channel) + if (c == alpha_channel) continue; - decode_buffer[decode_pixel_index + c] *= decode_buffer[decode_pixel_index + premul_alpha_channel]; + decode_buffer[decode_pixel_index + c] *= decode_buffer[decode_pixel_index + alpha_channel]; } } } @@ -1002,16 +1002,16 @@ static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffe } -static stbr_inline void stbr__encode_pixel(void* output_buffer, int output_pixel_index, float* encode_buffer, int encode_pixel_index, int channels, int premul_alpha_channel, int decode) +static stbr_inline void stbr__encode_pixel(void* output_buffer, int output_pixel_index, float* encode_buffer, int encode_pixel_index, int channels, int alpha_channel, int decode) { int n; float divide_alpha = 1; - if (premul_alpha_channel) { - float alpha = encode_buffer[encode_pixel_index + premul_alpha_channel]; + if (alpha_channel) { + float alpha = encode_buffer[encode_pixel_index + alpha_channel]; float reciprocal_alpha = alpha ? 1.0f / alpha : 0; for (n = 0; n < channels; n++) - if (n != premul_alpha_channel) + if (n != alpha_channel) encode_buffer[encode_pixel_index + n] *= reciprocal_alpha; } @@ -1086,7 +1086,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; float* vertical_coefficients = stbr_info->vertical_coefficients; int channels = stbr_info->channels; - int premul_alpha_channel = stbr_info->premul_alpha_channel; + int alpha_channel = stbr_info->alpha_channel; int type = stbr_info->type; int colorspace = stbr_info->colorspace; int kernel_pixel_width = stbr__get_filter_pixel_width_vertical(stbr_info); @@ -1134,7 +1134,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i encode_buffer[c] += ring_buffer_entry[in_pixel_index + c] * coefficient; } - stbr__encode_pixel(output_data, out_pixel_index, encode_buffer, 0, channels, premul_alpha_channel, decode); + stbr__encode_pixel(output_data, out_pixel_index, encode_buffer, 0, channels, alpha_channel, decode); } } @@ -1242,7 +1242,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s { int output_stride = stbr_info->output_stride_bytes / stbr__type_size[stbr_info->type]; int channels = stbr_info->channels; - int premul_alpha_channel = stbr_info->premul_alpha_channel; + int alpha_channel = stbr_info->alpha_channel; int type = stbr_info->type; int colorspace = stbr_info->colorspace; int output_w = stbr_info->output_w; @@ -1269,7 +1269,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s int ring_pixel_index = pixel_index; int output_pixel_index = output_row + pixel_index; - stbr__encode_pixel(output_data, output_pixel_index, ring_buffer_entry, ring_pixel_index, channels, premul_alpha_channel, decode); + stbr__encode_pixel(output_data, output_pixel_index, ring_buffer_entry, ring_pixel_index, channels, alpha_channel, decode); } } @@ -1336,7 +1336,7 @@ static stbr_size_t stbr__calculate_memory(int input_w, int input_h, int output_w static int stbr__resize_advanced(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int premul_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, + int channels, int alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes) { stbr__info* stbr_info = (stbr__info*)tempmem; @@ -1377,9 +1377,9 @@ static int stbr__resize_advanced(const void* input_data, int input_w, int input_ if (s1 > 1 || s0 < 0 || t1 > 1 || t0 < 0) return 0; - STBR_ASSERT(premul_alpha_channel >= 0 && premul_alpha_channel < channels); + STBR_ASSERT(alpha_channel >= 0 && alpha_channel < channels); - if (premul_alpha_channel < 0 || premul_alpha_channel >= channels) + if (alpha_channel < 0 || alpha_channel >= channels) return 0; STBR_ASSERT(tempmem); @@ -1416,7 +1416,7 @@ static int stbr__resize_advanced(const void* input_data, int input_w, int input_ stbr_info->vertical_shift = t0 * input_h / (t1 - t0); stbr_info->channels = channels; - stbr_info->premul_alpha_channel = premul_alpha_channel; + stbr_info->alpha_channel = alpha_channel; stbr_info->type = type; stbr_info->filter = filter; stbr_info->edge = edge; @@ -1577,9 +1577,9 @@ STBRDEF stbr_inline int stbr_resize_float_srgb(const float* input_data, int inpu return result; } -STBRDEF stbr_inline int stbr_resize_uint8_premultiply(const stbr_uint8* input_data, int input_w, int input_h, +STBRDEF stbr_inline int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) + int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); @@ -1588,16 +1588,16 @@ STBRDEF stbr_inline int stbr_resize_uint8_premultiply(const stbr_uint8* input_da if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); return result; } -STBRDEF stbr_inline int stbr_resize_uint16_premultiply(const stbr_uint16* input_data, int input_w, int input_h, +STBRDEF stbr_inline int stbr_resize_uint16_alphaweighted(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) + int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); @@ -1606,16 +1606,16 @@ STBRDEF stbr_inline int stbr_resize_uint16_premultiply(const stbr_uint16* input_ if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT16, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT16, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); return result; } -STBRDEF stbr_inline int stbr_resize_uint32_premultiply(const stbr_uint32* input_data, int input_w, int input_h, +STBRDEF stbr_inline int stbr_resize_uint32_alphaweighted(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) + int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); @@ -1624,16 +1624,16 @@ STBRDEF stbr_inline int stbr_resize_uint32_premultiply(const stbr_uint32* input_ if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_UINT32, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT32, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); return result; } -STBRDEF stbr_inline int stbr_resize_float_premultiply(const float* input_data, int input_w, int input_h, +STBRDEF stbr_inline int stbr_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, - int channels, int premultiply_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) + int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); @@ -1642,7 +1642,7 @@ STBRDEF stbr_inline int stbr_resize_float_premultiply(const float* input_data, i if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, premultiply_alpha_channel, STBR_TYPE_FLOAT, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_FLOAT, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1728,7 +1728,7 @@ STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int premultiply_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) + int channels, int nonpremultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); @@ -1737,7 +1737,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, premultiply_alpha_channel, type, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, nonpremultiplied_alpha_channel, type, filter, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); From 6ade66182c68a17bc83dcc2a3b348be309c6d516 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 11 Aug 2014 02:34:50 -0700 Subject: [PATCH 073/168] allow alpha channel to be channel #0 --- stb_resample.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 42400aa..711f75a 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -842,14 +842,15 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) break; } - if (alpha_channel) + if (alpha_channel >= 0) { + float alpha = decode_buffer[decode_pixel_index + alpha_channel]; for (c = 0; c < channels; c++) { if (c == alpha_channel) continue; - decode_buffer[decode_pixel_index + c] *= decode_buffer[decode_pixel_index + alpha_channel]; + decode_buffer[decode_pixel_index + c] *= alpha; } } } @@ -1007,7 +1008,7 @@ static stbr_inline void stbr__encode_pixel(void* output_buffer, int output_pixel int n; float divide_alpha = 1; - if (alpha_channel) { + if (alpha_channel >= 0) { float alpha = encode_buffer[encode_pixel_index + alpha_channel]; float reciprocal_alpha = alpha ? 1.0f / alpha : 0; for (n = 0; n < channels; n++) @@ -1377,9 +1378,9 @@ static int stbr__resize_advanced(const void* input_data, int input_w, int input_ if (s1 > 1 || s0 < 0 || t1 > 1 || t0 < 0) return 0; - STBR_ASSERT(alpha_channel >= 0 && alpha_channel < channels); + STBR_ASSERT(alpha_channel < channels); - if (alpha_channel < 0 || alpha_channel >= channels) + if (alpha_channel >= channels) return 0; STBR_ASSERT(tempmem); From e75ed1d38161f6d67a5aaf44e2eb5bebcc705f18 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Mon, 11 Aug 2014 12:21:55 -0700 Subject: [PATCH 074/168] Update test cases for no more advanced API. --- tests/resample_test.cpp | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 98e7166..f785739 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -63,9 +63,6 @@ int main(int argc, char** argv) float s1 = 0.75f; float t1 = 0.75f; - size_t memory_required = stbr_calculate_memory(in_w, in_h, out_w, out_h, s0, t0, s1, t1, n, STBR_FILTER_CATMULLROM); - void* extra_memory = malloc(memory_required); - // Cut out the outside 64 pixels all around to test the stride. int border = 64; STBR_ASSERT(in_w + border <= w); @@ -79,7 +76,7 @@ int main(int argc, char** argv) { ftime(&initial_time_millis); for (int i = 0; i < 100; i++) - stbr_resize_advanced(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + stbr_resize_advanced(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); ftime(&final_time_millis); long lapsed_ms = (long)(final_time_millis.time - initial_time_millis.time) * 1000 + (final_time_millis.millitm - initial_time_millis.millitm); printf("Resample: %dms\n", lapsed_ms); @@ -91,10 +88,9 @@ int main(int argc, char** argv) printf("Average: %dms\n", average); #else - stbr_resize_advanced(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); #endif - free(extra_memory); stbi_image_free(input_data); stbi_write_png("output.png", out_w, out_h, n, output_data, out_stride); @@ -120,12 +116,8 @@ void resize_image(const char* filename, float width_percent, float height_percen unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); - size_t memory_required = stbr_calculate_memory(w, h, out_w, out_h, 0, 0, 1, 1, n, filter); - void* extra_memory = malloc(memory_required); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, 0, STBR_TYPE_UINT8, filter, edge, colorspace); - stbr_resize_advanced(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, 0, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); - - free(extra_memory); stbi_image_free(input_data); stbi_write_png(output_filename, out_w, out_h, n, output_data, 0); @@ -199,10 +191,7 @@ void test_float(const char* file, float width_percent, float height_percent, stb float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); - size_t required = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, 1, 1, n, STBR_FILTER_CATMULLROM); - void* extra_memory = malloc(required); - stbr_resize_advanced(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); - free(extra_memory); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -264,12 +253,7 @@ void test_subpixel(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - size_t tempmem_size = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, s1, t1, n, STBR_FILTER_CATMULLROM); - void* tempmem = malloc(tempmem_size); - - stbr_resize_advanced(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); - - free(tempmem); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); stbi_image_free(input_data); @@ -300,22 +284,17 @@ void test_premul(const char* file) unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - size_t tempmem_size = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, 1, 1, n, STBR_FILTER_CATMULLROM); - void* tempmem = malloc(tempmem_size); - - stbr_resize_advanced(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); char output[200]; sprintf(output, "test-output/premul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); - stbr_resize_advanced(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, tempmem, tempmem_size); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); sprintf(output, "test-output/nopremul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); - free(tempmem); - stbi_image_free(input_data); free(output_data); From 45fa6ec900031eb29dddf8c7253008bc244acafb Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Mon, 11 Aug 2014 12:37:10 -0700 Subject: [PATCH 075/168] Update test cases for premul channel -1 meaning don't do any premul handling, fix another no-more-advanced. --- tests/resample_test.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index f785739..f87ff66 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -88,7 +88,7 @@ int main(int argc, char** argv) printf("Average: %dms\n", average); #else - stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); #endif stbi_image_free(input_data); @@ -116,7 +116,7 @@ void resize_image(const char* filename, float width_percent, float height_percen unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, 0, STBR_TYPE_UINT8, filter, edge, colorspace); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, -1, STBR_TYPE_UINT8, filter, edge, colorspace); stbi_image_free(input_data); @@ -147,10 +147,7 @@ void test_format(const char* file, float width_percent, float height_percent, st T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); - size_t required = stbr_calculate_memory(w, h, new_w, new_h, 0, 0, 1, 1, n, STBR_FILTER_CATMULLROM); - void* extra_memory = malloc(required); - stbr_resize_advanced(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace, extra_memory, required); - free(extra_memory); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -191,7 +188,7 @@ void test_float(const char* file, float width_percent, float height_percent, stb float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -253,7 +250,7 @@ void test_subpixel(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); stbi_image_free(input_data); @@ -290,7 +287,7 @@ void test_premul(const char* file) sprintf(output, "test-output/premul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); sprintf(output, "test-output/nopremul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); From 419a5ba10fc8a9bb34cb911fd2289b1b1c1c33a6 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 12 Aug 2014 11:55:27 -0700 Subject: [PATCH 076/168] Fix stbr__type_size for updated stbr_type --- stb_resample.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 711f75a..d9b3166 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -105,6 +105,13 @@ typedef enum STBR_MAX_TYPES } stbr_type; +static unsigned char stbr__type_size[] = { + 1, // STBR_TYPE_UINT8 + 2, // STBR_TYPE_UINT16 + 4, // STBR_TYPE_UINT32 + 4, // STBR_TYPE_FLOAT +}; + typedef unsigned char stbr_uint8; #ifdef _MSC_VER @@ -381,14 +388,6 @@ static unsigned char stbr__linear_uchar_to_srgb_uchar[256] = { 251, 251, 252, 252, 253, 253, 254, 254, 255 }; -static unsigned char stbr__type_size[] = { - 0, - 1, // STBR_TYPE_UINT8 - 2, // STBR_TYPE_UINT16 - 4, // STBR_TYPE_UINT32 - 4, // STBR_TYPE_FLOAT -}; - float stbr__srgb_to_linear(float f) { if (f <= 0.04045f) From 6ae729d61ad819e46535ba5626502d271553194c Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 12 Aug 2014 12:15:12 -0700 Subject: [PATCH 077/168] Four new tests of subpixel stuff. --- tests/resample_test.cpp | 159 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index f87ff66..653dfe9 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -21,6 +21,58 @@ #include #endif +#define MT_SIZE 624 +static size_t g_aiMT[MT_SIZE]; +static size_t g_iMTI = 0; + +// Mersenne Twister implementation from Wikipedia. +// Avoiding use of the system rand() to be sure that our tests generate the same test data on any system. +void mtsrand(size_t iSeed) +{ + g_aiMT[0] = iSeed; + for (size_t i = 1; i < MT_SIZE; i++) + { + size_t inner1 = g_aiMT[i - 1]; + size_t inner2 = (g_aiMT[i - 1] >> 30); + size_t inner = inner1 ^ inner2; + g_aiMT[i] = (0x6c078965 * inner) + i; + } + + g_iMTI = 0; +} + +size_t mtrand() +{ + if (g_iMTI == 0) + { + for (size_t i = 0; i < MT_SIZE; i++) + { + size_t y = (0x80000000 & (g_aiMT[i])) + (0x7fffffff & (g_aiMT[(i + 1) % MT_SIZE])); + g_aiMT[i] = g_aiMT[(i + 397) % MT_SIZE] ^ (y >> 1); + if ((y % 2) == 1) + g_aiMT[i] = g_aiMT[i] ^ 0x9908b0df; + } + } + + size_t y = g_aiMT[g_iMTI]; + y = y ^ (y >> 11); + y = y ^ ((y << 7) & (0x9d2c5680)); + y = y ^ ((y << 15) & (0xefc60000)); + y = y ^ (y >> 18); + + g_iMTI = (g_iMTI + 1) % MT_SIZE; + + return y; +} + + +inline float mtfrand() +{ + const int ninenine = 999999; + return (float)(mtrand() % ninenine)/ninenine; +} + + void test_suite(); int main(int argc, char** argv) @@ -297,8 +349,115 @@ void test_premul(const char* file) free(output_data); } +void test_subpixel_1() +{ + unsigned char image[8 * 8]; + + mtsrand(0); + + for (int i = 0; i < sizeof(image); i++) + image[i] = mtrand() % 255; + + unsigned char output_data[16 * 16]; + + stbr_resize_arbitrary(image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + + unsigned char output_left[8 * 16]; + unsigned char output_right[8 * 16]; + + stbr_resize_arbitrary(image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + + for (int x = 0; x < 8; x++) + { + for (int y = 0; y < 16; y++) + { + STBR_ASSERT(output_data[y * 16 + x] == output_left[y * 8 + x]); + STBR_ASSERT(output_data[y * 16 + x + 8] == output_right[y * 8 + x]); + } + } +} + +void test_subpixel_2() +{ + unsigned char image[8 * 8]; + + mtsrand(0); + + for (int i = 0; i < sizeof(image); i++) + image[i] = mtrand() % 255; + + unsigned char large_image[32 * 32]; + + for (int x = 0; x < 8; x++) + { + for (int y = 0; y < 8; y++) + { + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + large_image[j*4*8*8 + i*8 + y*4*8 + x] = image[y*8 + x]; + } + } + } + + unsigned char output_data_1[16 * 16]; + unsigned char output_data_2[16 * 16]; + + stbr_resize_arbitrary(image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_WRAP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + + for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + STBR_ASSERT(output_data_1[y * 16 + x] == output_data_2[y * 16 + x]); + } +} + +void test_subpixel_3() +{ + unsigned char image[8 * 8]; + + mtsrand(0); + + for (int i = 0; i < sizeof(image); i++) + image[i] = mtrand() % 255; + + unsigned char output_data_1[32 * 32]; + unsigned char output_data_2[32 * 32]; + + stbr_resize_uint8_subpixel(image, 8, 8, output_data_1, 32, 32, 0, 0, 1, 1, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); + stbr_resize_uint8_srgb(image, 8, 8, output_data_2, 32, 32, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); + + for (int x = 0; x < 32; x++) + { + for (int y = 0; y < 32; y++) + STBR_ASSERT(output_data_1[y * 32 + x] == output_data_2[y * 32 + x]); + } +} + +void test_subpixel_4() +{ + unsigned char image[8 * 8]; + + mtsrand(0); + + for (int i = 0; i < sizeof(image); i++) + image[i] = mtrand() % 255; + + unsigned char output[8 * 8]; + + stbr_resize_arbitrary(image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_BILINEAR, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR); + STBR_ASSERT(memcmp(image, output, 8 * 8) == 0); +} + void test_suite() { + test_subpixel_1(); + test_subpixel_2(); + test_subpixel_3(); + test_subpixel_4(); + test_premul("barbara.png"); for (int i = 0; i < 10; i++) From bbc340d4816b3091494050d23e6ba895745308ce Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 12 Aug 2014 12:22:38 -0700 Subject: [PATCH 078/168] Support different edge behavior on vertical and horizontal axis. --- stb_resample.h | 45 ++++++++++++++++++++++++--------------------- tests/resample_test.cpp | 26 +++++++++++++------------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index d9b3166..fe9e133 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -198,7 +198,7 @@ extern "C" { STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int nonpremultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + int channels, int nonpremultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace); #ifdef __cplusplus } @@ -303,7 +303,8 @@ typedef struct int alpha_channel; stbr_type type; stbr_filter filter; - stbr_edge edge; + stbr_edge edge_horizontal; + stbr_edge edge_vertical; stbr_colorspace colorspace; stbr__contributors* horizontal_contributors; @@ -784,15 +785,16 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) int input_stride = stbr_info->input_stride_bytes / stbr__type_size[stbr_info->type]; const void* input_data = stbr_info->input_data; float* decode_buffer = stbr__get_decode_buffer(stbr_info); - stbr_edge edge = stbr_info->edge; - int in_buffer_row_index = stbr__edge_wrap(edge, n, stbr_info->input_h) * input_stride; + stbr_edge edge_horizontal = stbr_info->edge_horizontal; + stbr_edge edge_vertical = stbr_info->edge_vertical; + int in_buffer_row_index = stbr__edge_wrap(edge_vertical, n, stbr_info->input_h) * input_stride; int max_x = input_w + stbr__get_filter_pixel_margin_horizontal(stbr_info); int decode = STBR__DECODE(type, colorspace); for (x = -stbr__get_filter_pixel_margin_horizontal(stbr_info); x < max_x; x++) { int decode_pixel_index = x * channels; - int input_pixel_index = in_buffer_row_index + stbr__edge_wrap(edge, x, input_w) * channels; + int input_pixel_index = in_buffer_row_index + stbr__edge_wrap(edge_horizontal, x, input_w) * channels; switch (decode) { @@ -1336,7 +1338,7 @@ static stbr_size_t stbr__calculate_memory(int input_w, int input_h, int output_w static int stbr__resize_advanced(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, + int channels, int alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes) { stbr__info* stbr_info = (stbr__info*)tempmem; @@ -1419,7 +1421,8 @@ static int stbr__resize_advanced(const void* input_data, int input_w, int input_ stbr_info->alpha_channel = alpha_channel; stbr_info->type = type; stbr_info->filter = filter; - stbr_info->edge = edge; + stbr_info->edge_horizontal = edge_horizontal; + stbr_info->edge_vertical = edge_vertical; stbr_info->colorspace = colorspace; stbr_info->ring_buffer_length_bytes = output_w * channels * sizeof(float); @@ -1516,7 +1519,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1534,7 +1537,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_srgb(const stbr_uint16* input_data, i if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1552,7 +1555,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_srgb(const stbr_uint32* input_data, i if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1570,7 +1573,7 @@ STBRDEF stbr_inline int stbr_resize_float_srgb(const float* input_data, int inpu if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1588,7 +1591,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_ if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT8, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT8, filter, edge, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1606,7 +1609,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_alphaweighted(const stbr_uint16* inpu if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT16, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT16, filter, edge, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1624,7 +1627,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_alphaweighted(const stbr_uint32* inpu if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT32, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT32, filter, edge, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1642,7 +1645,7 @@ STBRDEF stbr_inline int stbr_resize_float_alphaweighted(const float* input_data, if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_FLOAT, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_FLOAT, filter, edge, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1661,7 +1664,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT8, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1680,7 +1683,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_subpixel(const stbr_uint16* input_dat if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT16, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1699,7 +1702,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_subpixel(const stbr_uint32* input_dat if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT32, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1718,7 +1721,7 @@ STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_FLOAT, filter, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1728,7 +1731,7 @@ STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int nonpremultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) + int channels, int nonpremultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace) { int result; size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); @@ -1737,7 +1740,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, nonpremultiplied_alpha_channel, type, filter, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, nonpremultiplied_alpha_channel, type, filter, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 653dfe9..ff19d13 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -140,7 +140,7 @@ int main(int argc, char** argv) printf("Average: %dms\n", average); #else - stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); #endif stbi_image_free(input_data); @@ -168,7 +168,7 @@ void resize_image(const char* filename, float width_percent, float height_percen unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, -1, STBR_TYPE_UINT8, filter, edge, colorspace); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, -1, STBR_TYPE_UINT8, filter, edge, edge, colorspace); stbi_image_free(input_data); @@ -199,7 +199,7 @@ void test_format(const char* file, float width_percent, float height_percent, st T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -240,7 +240,7 @@ void test_float(const char* file, float width_percent, float height_percent, stb float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, colorspace); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -302,7 +302,7 @@ void test_subpixel(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); stbi_image_free(input_data); @@ -333,13 +333,13 @@ void test_premul(const char* file) unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); char output[200]; sprintf(output, "test-output/premul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); sprintf(output, "test-output/nopremul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); @@ -360,13 +360,13 @@ void test_subpixel_1() unsigned char output_data[16 * 16]; - stbr_resize_arbitrary(image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); unsigned char output_left[8 * 16]; unsigned char output_right[8 * 16]; - stbr_resize_arbitrary(image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); - stbr_resize_arbitrary(image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); for (int x = 0; x < 8; x++) { @@ -404,8 +404,8 @@ void test_subpixel_2() unsigned char output_data_1[16 * 16]; unsigned char output_data_2[16 * 16]; - stbr_resize_arbitrary(image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_WRAP, STBR_COLORSPACE_SRGB); - stbr_resize_arbitrary(large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_WRAP, STBR_EDGE_WRAP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); for (int x = 0; x < 16; x++) { @@ -447,7 +447,7 @@ void test_subpixel_4() unsigned char output[8 * 8]; - stbr_resize_arbitrary(image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_BILINEAR, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR); + stbr_resize_arbitrary(image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_BILINEAR, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR); STBR_ASSERT(memcmp(image, output, 8 * 8) == 0); } From 1353909477a9c824be4c8c47976bc94ad007dc7d Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 12 Aug 2014 13:37:06 -0700 Subject: [PATCH 079/168] Allow user to force the alpha channel to be handled as a linear value even if the color channels are sRGB. --- stb_resample.h | 127 +++++++++++++++++++++++++++++------------------- tests/resample_test.cpp | 28 +++++------ 2 files changed, 91 insertions(+), 64 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index fe9e133..0cd7795 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -49,8 +49,8 @@ // // Other function groups are provided, one for each data type, for more advanced functionality: // -// stbr_resize_type_alphaweighted(input_data, input_w, input_h, output_data, output_w, output_h, channels, nonpremultiplied_alpha_channel, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB) -// * nonpremultiplied_alpha_channel - if nonnegative, the specified channel will be multiplied into all other channels before resampling, then divided back out after. +// stbr_resize_type_alphaweighted(input_data, input_w, input_h, output_data, output_w, output_h, channels, alpha_channel, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB) +// * alpha_channel - if nonnegative, this channel will be multiplied into all other channels before resampling, then divided back out after. // // stbr_resize_type_subpixel(input_data, input_w, input_h, output_data, output_w, output_h, s0, t0, s1, t1, channels, filter, edge) // * 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. @@ -61,11 +61,12 @@ // result = stbr_resize_arbitrary(input_data, input_w, input_h, input_stride_in_bytes, // output_data, output_w, output_h, output_stride_in_bytes, // s0, t0, s1, t1, -// channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); +// channels, alpha_channel, flags, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); // // * input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. // * 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. -// * nonpremultiplied_alpha_channel - if nonnegative, the specified channel will be multiplied into all other channels before resampling, then divided back out after. +// * flags are from the stbr_flags enum and should be bitwise OR'd together. +// * First edge parameter is for horizontal edge behavior, second is for vertical. // * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. // * Memory required grows approximately linearly with input and output size, but with discontinuities at input_w == output_w and input_h == output_height. // * To use temporary memory, define an STBR_MALLOC that returns the temp memory and make STBR_FREE do nothing--each function only ever allocates one block @@ -112,6 +113,12 @@ static unsigned char stbr__type_size[] = { 4, // STBR_TYPE_FLOAT }; +typedef enum +{ + STBR_FLAG_NONPREMUL_ALPHA = (1 << 0), // The specified alpha channel will be multiplied into all other channels before resampling, then divided back out after. + STBR_FLAG_FORCE_LINEAR_ALPHA = (1 << 1), // The specified alpha channel should be handled as a linear value even when doing sRGB operations. +} stbr_flags; + typedef unsigned char stbr_uint8; #ifdef _MSC_VER @@ -159,19 +166,19 @@ extern "C" { STBRDEF int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, - int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); STBRDEF int stbr_resize_uint16_alphaweighted(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, - int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); STBRDEF int stbr_resize_uint32_alphaweighted(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, - int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); STBRDEF int stbr_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, - int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); STBRDEF int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, @@ -198,7 +205,7 @@ extern "C" { STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int nonpremultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace); + int channels, int alpha_channel, stbr_uint32 flags, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace); #ifdef __cplusplus } @@ -301,6 +308,7 @@ typedef struct int channels; int alpha_channel; + stbr_uint32 flags; stbr_type type; stbr_filter filter; stbr_edge edge_horizontal; @@ -801,11 +809,16 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255; + break; case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbr__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; + + if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255; + break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): @@ -816,6 +829,10 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbr__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); + + if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535; + break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): @@ -826,6 +843,10 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbr__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); + + if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) + decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295); + break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): @@ -836,6 +857,10 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbr__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); + + if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) + decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; + break; default: @@ -843,7 +868,7 @@ static void stbr__decode_scanline(stbr__info* stbr_info, int n) break; } - if (alpha_channel >= 0) + if (stbr_info->flags&STBR_FLAG_NONPREMUL_ALPHA) { float alpha = decode_buffer[decode_pixel_index + alpha_channel]; for (c = 0; c < channels; c++) @@ -1004,12 +1029,12 @@ static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffe } -static stbr_inline void stbr__encode_pixel(void* output_buffer, int output_pixel_index, float* encode_buffer, int encode_pixel_index, int channels, int alpha_channel, int decode) +static stbr_inline void stbr__encode_pixel(stbr__info* stbr_info, void* output_buffer, int output_pixel_index, float* encode_buffer, int encode_pixel_index, int channels, int alpha_channel, int decode) { int n; float divide_alpha = 1; - if (alpha_channel >= 0) { + if (stbr_info->flags&STBR_FLAG_NONPREMUL_ALPHA) { float alpha = encode_buffer[encode_pixel_index + alpha_channel]; float reciprocal_alpha = alpha ? 1.0f / alpha : 0; for (n = 0; n < channels; n++) @@ -1021,58 +1046,58 @@ static stbr_inline void stbr__encode_pixel(void* output_buffer, int output_pixel { case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) - { ((unsigned char*)output_buffer)[output_pixel_index + n] = (unsigned char)(stbr__saturate(encode_buffer[encode_pixel_index + n]) * 255); - } break; case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) - { ((unsigned char*)output_buffer)[output_pixel_index + n] = stbr__linear_uchar_to_srgb_uchar[(unsigned char)(stbr__saturate(encode_buffer[encode_pixel_index + n]) * 255)]; - } + + if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) + ((unsigned char*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbr__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); + break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) - { ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbr__saturate(encode_buffer[encode_pixel_index + n]) * 65535); - } break; case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) - { ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_pixel_index + n])) * 65535); - } + + if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) + ((unsigned short*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbr__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); + break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) - { ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbr__saturate(encode_buffer[encode_pixel_index + n])) * 4294967295); - } break; case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) - { ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_pixel_index + n]))) * 4294967295); - } + + if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) + ((unsigned int*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned int)(((double)stbr__saturate(encode_buffer[encode_pixel_index + alpha_channel])) * 4294967295); + break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) - { ((float*)output_buffer)[output_pixel_index + n] = stbr__saturate(encode_buffer[encode_pixel_index + n]); - } break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) - { ((float*)output_buffer)[output_pixel_index + n] = stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_pixel_index + n])); - } + + if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) + ((float*)output_buffer)[output_pixel_index + alpha_channel] = stbr__saturate(encode_buffer[encode_pixel_index + alpha_channel]); + break; default: @@ -1136,7 +1161,7 @@ static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int i encode_buffer[c] += ring_buffer_entry[in_pixel_index + c] * coefficient; } - stbr__encode_pixel(output_data, out_pixel_index, encode_buffer, 0, channels, alpha_channel, decode); + stbr__encode_pixel(stbr_info, output_data, out_pixel_index, encode_buffer, 0, channels, alpha_channel, decode); } } @@ -1271,7 +1296,7 @@ static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_s int ring_pixel_index = pixel_index; int output_pixel_index = output_row + pixel_index; - stbr__encode_pixel(output_data, output_pixel_index, ring_buffer_entry, ring_pixel_index, channels, alpha_channel, decode); + stbr__encode_pixel(stbr_info, output_data, output_pixel_index, ring_buffer_entry, ring_pixel_index, channels, alpha_channel, decode); } } @@ -1338,7 +1363,7 @@ static stbr_size_t stbr__calculate_memory(int input_w, int input_h, int output_w static int stbr__resize_advanced(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace, + int channels, int alpha_channel, stbr_uint32 flags, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace, void* tempmem, stbr_size_t tempmem_size_in_bytes) { stbr__info* stbr_info = (stbr__info*)tempmem; @@ -1379,7 +1404,8 @@ static int stbr__resize_advanced(const void* input_data, int input_w, int input_ if (s1 > 1 || s0 < 0 || t1 > 1 || t0 < 0) return 0; - STBR_ASSERT(alpha_channel < channels); + if (flags&(STBR_FLAG_FORCE_LINEAR_ALPHA | STBR_FLAG_NONPREMUL_ALPHA)) + STBR_ASSERT(alpha_channel >= 0 && alpha_channel < channels); if (alpha_channel >= channels) return 0; @@ -1419,6 +1445,7 @@ static int stbr__resize_advanced(const void* input_data, int input_w, int input_ stbr_info->channels = channels; stbr_info->alpha_channel = alpha_channel; + stbr_info->flags = flags; stbr_info->type = type; stbr_info->filter = filter; stbr_info->edge_horizontal = edge_horizontal; @@ -1519,7 +1546,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1537,7 +1564,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_srgb(const stbr_uint16* input_data, i if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1555,7 +1582,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_srgb(const stbr_uint32* input_data, i if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1573,7 +1600,7 @@ STBRDEF stbr_inline int stbr_resize_float_srgb(const float* input_data, int inpu if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1582,7 +1609,7 @@ STBRDEF stbr_inline int stbr_resize_float_srgb(const float* input_data, int inpu STBRDEF stbr_inline int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, - int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) + int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); @@ -1591,7 +1618,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_ if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT8, filter, edge, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT8, filter, edge, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1600,7 +1627,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_ STBRDEF stbr_inline int stbr_resize_uint16_alphaweighted(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, - int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) + int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); @@ -1609,7 +1636,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_alphaweighted(const stbr_uint16* inpu if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT16, filter, edge, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT16, filter, edge, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1618,7 +1645,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_alphaweighted(const stbr_uint16* inpu STBRDEF stbr_inline int stbr_resize_uint32_alphaweighted(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, - int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) + int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); @@ -1627,7 +1654,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_alphaweighted(const stbr_uint32* inpu if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_UINT32, filter, edge, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT32, filter, edge, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1636,7 +1663,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_alphaweighted(const stbr_uint32* inpu STBRDEF stbr_inline int stbr_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, - int channels, int nonpremultiplied_alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) + int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { int result; size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); @@ -1645,7 +1672,7 @@ STBRDEF stbr_inline int stbr_resize_float_alphaweighted(const float* input_data, if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, nonpremultiplied_alpha_channel, STBR_TYPE_FLOAT, filter, edge, edge, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_FLOAT, filter, edge, edge, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1664,7 +1691,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1683,7 +1710,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_subpixel(const stbr_uint16* input_dat if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1702,7 +1729,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_subpixel(const stbr_uint32* input_dat if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1721,7 +1748,7 @@ STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); STBR_FREE(extra_memory); @@ -1731,7 +1758,7 @@ STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int nonpremultiplied_alpha_channel, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace) + int channels, int alpha_channel, stbr_uint32 flags, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace) { int result; size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); @@ -1740,7 +1767,7 @@ STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, nonpremultiplied_alpha_channel, type, filter, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); + result = stbr__resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, alpha_channel, flags, type, filter, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index ff19d13..8a25d43 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -128,7 +128,7 @@ int main(int argc, char** argv) { ftime(&initial_time_millis); for (int i = 0; i < 100; i++) - stbr_resize_advanced(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, n, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, 0, 0, 1, 1, n, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); ftime(&final_time_millis); long lapsed_ms = (long)(final_time_millis.time - initial_time_millis.time) * 1000 + (final_time_millis.millitm - initial_time_millis.millitm); printf("Resample: %dms\n", lapsed_ms); @@ -140,7 +140,7 @@ int main(int argc, char** argv) printf("Average: %dms\n", average); #else - stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); #endif stbi_image_free(input_data); @@ -168,7 +168,7 @@ void resize_image(const char* filename, float width_percent, float height_percen unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, -1, STBR_TYPE_UINT8, filter, edge, edge, colorspace); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, -1, 0, STBR_TYPE_UINT8, filter, edge, edge, colorspace); stbi_image_free(input_data); @@ -199,7 +199,7 @@ void test_format(const char* file, float width_percent, float height_percent, st T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, colorspace); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -240,7 +240,7 @@ void test_float(const char* file, float width_percent, float height_percent, stb float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, colorspace); + stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -302,7 +302,7 @@ void test_subpixel(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); stbi_image_free(input_data); @@ -333,13 +333,13 @@ void test_premul(const char* file) unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); char output[200]; sprintf(output, "test-output/premul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); sprintf(output, "test-output/nopremul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); @@ -360,13 +360,13 @@ void test_subpixel_1() unsigned char output_data[16 * 16]; - stbr_resize_arbitrary(image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); unsigned char output_left[8 * 16]; unsigned char output_right[8 * 16]; - stbr_resize_arbitrary(image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); - stbr_resize_arbitrary(image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); for (int x = 0; x < 8; x++) { @@ -404,8 +404,8 @@ void test_subpixel_2() unsigned char output_data_1[16 * 16]; unsigned char output_data_2[16 * 16]; - stbr_resize_arbitrary(image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_WRAP, STBR_EDGE_WRAP, STBR_COLORSPACE_SRGB); - stbr_resize_arbitrary(large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_WRAP, STBR_EDGE_WRAP, STBR_COLORSPACE_SRGB); + stbr_resize_arbitrary(large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); for (int x = 0; x < 16; x++) { @@ -447,7 +447,7 @@ void test_subpixel_4() unsigned char output[8 * 8]; - stbr_resize_arbitrary(image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, 1, -1, STBR_TYPE_UINT8, STBR_FILTER_BILINEAR, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR); + stbr_resize_arbitrary(image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_BILINEAR, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR); STBR_ASSERT(memcmp(image, output, 8 * 8) == 0); } From c9caec11239717787151f642a96faad4bdcba90e Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 12 Aug 2014 13:50:17 -0700 Subject: [PATCH 080/168] Refactoring to reduce duplicated code. --- stb_resample.h | 184 ++++++++------------------------------------------------- 1 file changed, 25 insertions(+), 159 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 0cd7795..6a299f2 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -1358,9 +1358,7 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) stbr__empty_ring_buffer(stbr_info, stbr_info->output_h); } -static stbr_size_t stbr__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter); - -static int stbr__resize_advanced(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, +static int stbr__resize_allocated(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, int channels, int alpha_channel, stbr_uint32 flags, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace, @@ -1502,7 +1500,7 @@ static int stbr__resize_advanced(const void* input_data, int input_w, int input_ } -STBRDEF stbr_size_t stbr__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter) +static stbr_inline stbr_size_t stbr__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter) { float horizontal_scale = ((float)output_w / input_w) / (s1 - s0); float vertical_scale = ((float)output_h / input_h) / (t1 - t0); @@ -1535,9 +1533,10 @@ STBRDEF stbr_size_t stbr__calculate_memory(int input_w, int input_h, int output_ return info_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size; } -STBRDEF stbr_inline int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int input_w, int input_h, - stbr_uint8* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge) +STBRDEF stbr_inline int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, + int channels, int alpha_channel, stbr_uint32 flags, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace) { int result; size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); @@ -1546,137 +1545,67 @@ STBRDEF stbr_inline int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int if (!extra_memory) return 0; - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); + result = stbr__resize_allocated(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, alpha_channel, flags, type, filter, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); STBR_FREE(extra_memory); return result; } +STBRDEF stbr_inline int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int input_w, int input_h, + stbr_uint8* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge) +{ + return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB); +} + STBRDEF stbr_inline int stbr_resize_uint16_srgb(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; + return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB); } STBRDEF stbr_inline int stbr_resize_uint32_srgb(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; + return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB); } STBRDEF stbr_inline int stbr_resize_float_srgb(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; + return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB); } STBRDEF stbr_inline int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT8, filter, edge, edge, colorspace, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; + return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT8, filter, edge, edge, colorspace); } STBRDEF stbr_inline int stbr_resize_uint16_alphaweighted(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT16, filter, edge, edge, colorspace, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; + return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT16, filter, edge, edge, colorspace); } STBRDEF stbr_inline int stbr_resize_uint32_alphaweighted(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT32, filter, edge, edge, colorspace, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; + return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT32, filter, edge, edge, colorspace); } STBRDEF stbr_inline int stbr_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_FLOAT, filter, edge, edge, colorspace, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; + return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_FLOAT, filter, edge, edge, colorspace); } STBRDEF stbr_inline int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, @@ -1684,18 +1613,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) { - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; + return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB); } STBRDEF stbr_inline int stbr_resize_uint16_subpixel(const stbr_uint16* input_data, int input_w, int input_h, @@ -1703,18 +1621,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_subpixel(const stbr_uint16* input_dat float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) { - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; + return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB); } STBRDEF stbr_inline int stbr_resize_uint32_subpixel(const stbr_uint32* input_data, int input_w, int input_h, @@ -1722,18 +1629,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_subpixel(const stbr_uint32* input_dat float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) { - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; + return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB); } STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int input_w, int input_h, @@ -1741,37 +1637,7 @@ STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) { - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_advanced(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; -} - -STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbr_uint32 flags, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace) -{ - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_advanced(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, alpha_channel, flags, type, filter, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; + return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB); } #endif // STB_RESAMPLE_IMPLEMENTATION From fc09a5d1984aab9c509667e5e164fbf39c56b383 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 12 Aug 2014 14:15:05 -0700 Subject: [PATCH 081/168] Don't saturate floats. --- stb_resample.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stb_resample.h b/stb_resample.h index 6a299f2..3321987 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -1088,15 +1088,15 @@ static stbr_inline void stbr__encode_pixel(stbr__info* stbr_info, void* output_b case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): for (n = 0; n < channels; n++) - ((float*)output_buffer)[output_pixel_index + n] = stbr__saturate(encode_buffer[encode_pixel_index + n]); + ((float*)output_buffer)[output_pixel_index + n] = encode_buffer[encode_pixel_index + n]; break; case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) - ((float*)output_buffer)[output_pixel_index + n] = stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_pixel_index + n])); + ((float*)output_buffer)[output_pixel_index + n] = stbr__linear_to_srgb(encode_buffer[encode_pixel_index + n]); if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) - ((float*)output_buffer)[output_pixel_index + alpha_channel] = stbr__saturate(encode_buffer[encode_pixel_index + alpha_channel]); + ((float*)output_buffer)[output_pixel_index + alpha_channel] = encode_buffer[encode_pixel_index + alpha_channel]; break; From 9ba3dc1fc5609df72e990cb832e22c15a1a2a202 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 18 Aug 2014 08:56:44 -0700 Subject: [PATCH 082/168] make resample tests work in VC6 --- stb_resample.h | 278 ++++++++++++++++++++++++------------------------ tests/resample_test.cpp | 31 +++--- tests/resize.dsp | 88 +++++++++++++++ tests/stb.dsw | 12 +++ 4 files changed, 254 insertions(+), 155 deletions(-) create mode 100644 tests/resize.dsp diff --git a/stb_resample.h b/stb_resample.h index 3321987..240ecb9 100644 --- a/stb_resample.h +++ b/stb_resample.h @@ -132,85 +132,81 @@ typedef uint32_t stbr_uint32; typedef unsigned int stbr_size_t; // to avoid including a header for size_t -#ifdef __cplusplus -extern "C" { -#endif - #ifdef STB_RESAMPLE_STATIC #define STBRDEF static #else +#ifdef __cplusplus +#define STBRDEF extern "C" +#else #define STBRDEF extern #endif - - ////////////////////////////////////////////////////////////////////////////// - // - // PRIMARY API - sRGB type-safe image resizing. - // - - STBRDEF int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int input_w, int input_h, - stbr_uint8* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge); - - STBRDEF int stbr_resize_uint16_srgb(const stbr_uint16* input_data, int input_w, int input_h, - stbr_uint16* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge); - - STBRDEF int stbr_resize_uint32_srgb(const stbr_uint32* input_data, int input_w, int input_h, - stbr_uint32* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge); - - STBRDEF int stbr_resize_float_srgb(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge); - - - STBRDEF int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_data, int input_w, int input_h, - stbr_uint8* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); - - STBRDEF int stbr_resize_uint16_alphaweighted(const stbr_uint16* input_data, int input_w, int input_h, - stbr_uint16* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); - - STBRDEF int stbr_resize_uint32_alphaweighted(const stbr_uint32* input_data, int input_w, int input_h, - stbr_uint32* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); - - STBRDEF int stbr_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); - - - STBRDEF int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, - stbr_uint8* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbr_filter filter, stbr_edge edge); - - STBRDEF int stbr_resize_uint16_subpixel(const stbr_uint16* input_data, int input_w, int input_h, - stbr_uint16* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbr_filter filter, stbr_edge edge); - - STBRDEF int stbr_resize_uint32_subpixel(const stbr_uint32* input_data, int input_w, int input_h, - stbr_uint32* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbr_filter filter, stbr_edge edge); - - STBRDEF int stbr_resize_float_subpixel(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbr_filter filter, stbr_edge edge); - - - STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbr_uint32 flags, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace); - -#ifdef __cplusplus -} #endif +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - sRGB type-safe image resizing. +// + +STBRDEF int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int input_w, int input_h, + stbr_uint8* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge); + +STBRDEF int stbr_resize_uint16_srgb(const stbr_uint16* input_data, int input_w, int input_h, + stbr_uint16* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge); + +STBRDEF int stbr_resize_uint32_srgb(const stbr_uint32* input_data, int input_w, int input_h, + stbr_uint32* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge); + +STBRDEF int stbr_resize_float_srgb(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + int channels, stbr_filter filter, stbr_edge edge); + + +STBRDEF int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_data, int input_w, int input_h, + stbr_uint8* output_data, int output_w, int output_h, + int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + +STBRDEF int stbr_resize_uint16_alphaweighted(const stbr_uint16* input_data, int input_w, int input_h, + stbr_uint16* output_data, int output_w, int output_h, + int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + +STBRDEF int stbr_resize_uint32_alphaweighted(const stbr_uint32* input_data, int input_w, int input_h, + stbr_uint32* output_data, int output_w, int output_h, + int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + +STBRDEF int stbr_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); + + +STBRDEF int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, + stbr_uint8* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbr_filter filter, stbr_edge edge); + +STBRDEF int stbr_resize_uint16_subpixel(const stbr_uint16* input_data, int input_w, int input_h, + stbr_uint16* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbr_filter filter, stbr_edge edge); + +STBRDEF int stbr_resize_uint32_subpixel(const stbr_uint32* input_data, int input_w, int input_h, + stbr_uint32* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbr_filter filter, stbr_edge edge); + +STBRDEF int stbr_resize_float_subpixel(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbr_filter filter, stbr_edge edge); + + +STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, + int channels, int alpha_channel, stbr_uint32 flags, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace); + // // //// end header file ///////////////////////////////////////////////////// @@ -248,12 +244,12 @@ extern "C" { #ifndef _MSC_VER #ifdef __cplusplus -#define stbr_inline inline +#define stbr__inline inline #else -#define stbr_inline +#define stbr__inline #endif #else -#define stbr_inline __forceinline +#define stbr__inline __forceinline #endif @@ -335,17 +331,17 @@ typedef struct float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. } stbr__info; -static stbr_inline int stbr__min(int a, int b) +static stbr__inline int stbr__min(int a, int b) { return a < b ? a : b; } -static stbr_inline int stbr__max(int a, int b) +static stbr__inline int stbr__max(int a, int b) { return a > b ? a : b; } -static stbr_inline float stbr__saturate(float x) +static stbr__inline float stbr__saturate(float x) { if (x < 0) return 0; @@ -480,24 +476,24 @@ static stbr__filter_info stbr__filter_info_table[] = { { stbr__filter_mitchell, 2.0f }, }; -stbr_inline static int stbr__use_upsampling(float ratio) +stbr__inline static int stbr__use_upsampling(float ratio) { return ratio > 1; } -stbr_inline static int stbr__use_width_upsampling(stbr__info* stbr_info) +stbr__inline static int stbr__use_width_upsampling(stbr__info* stbr_info) { return stbr__use_upsampling(stbr_info->horizontal_scale); } -stbr_inline static int stbr__use_height_upsampling(stbr__info* stbr_info) +stbr__inline static int stbr__use_height_upsampling(stbr__info* stbr_info) { return stbr__use_upsampling(stbr_info->vertical_scale); } // This is the maximum number of input samples that can affect an output sample // with the given filter -stbr_inline static int stbr__get_filter_pixel_width(stbr_filter filter, int input_w, int output_w, float scale) +stbr__inline static int stbr__get_filter_pixel_width(stbr_filter filter, int input_w, int output_w, float scale) { STBR_ASSERT(filter != 0); STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); @@ -508,34 +504,34 @@ stbr_inline static int stbr__get_filter_pixel_width(stbr_filter filter, int inpu return (int)ceil(stbr__filter_info_table[filter].support * 2 / scale); } -stbr_inline static int stbr__get_filter_pixel_width_horizontal(stbr__info* stbr_info) +stbr__inline static int stbr__get_filter_pixel_width_horizontal(stbr__info* stbr_info) { return stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale); } -stbr_inline static int stbr__get_filter_pixel_width_vertical(stbr__info* stbr_info) +stbr__inline static int stbr__get_filter_pixel_width_vertical(stbr__info* stbr_info) { return stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h, stbr_info->vertical_scale); } // This is how much to expand buffers to account for filters seeking outside // the image boundaries. -stbr_inline static int stbr__get_filter_pixel_margin(stbr_filter filter, int input_w, int output_w, float scale) +stbr__inline static int stbr__get_filter_pixel_margin(stbr_filter filter, int input_w, int output_w, float scale) { return stbr__get_filter_pixel_width(filter, input_w, output_w, scale) / 2; } -stbr_inline static int stbr__get_filter_pixel_margin_horizontal(stbr__info* stbr_info) +stbr__inline static int stbr__get_filter_pixel_margin_horizontal(stbr__info* stbr_info) { return stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale) / 2; } -stbr_inline static int stbr__get_filter_pixel_margin_vertical(stbr__info* stbr_info) +stbr__inline static int stbr__get_filter_pixel_margin_vertical(stbr__info* stbr_info) { return stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h, stbr_info->vertical_scale) / 2; } -stbr_inline static int stbr__get_horizontal_contributors_noinfo(stbr_filter filter, int input_w, int output_w, float horizontal_scale) +stbr__inline static int stbr__get_horizontal_contributors_noinfo(stbr_filter filter, int input_w, int output_w, float horizontal_scale) { if (stbr__use_upsampling(horizontal_scale)) return output_w; @@ -543,33 +539,33 @@ stbr_inline static int stbr__get_horizontal_contributors_noinfo(stbr_filter filt return (input_w + stbr__get_filter_pixel_margin(filter, input_w, output_w, horizontal_scale) * 2); } -stbr_inline static int stbr__get_horizontal_contributors(stbr__info* stbr_info) +stbr__inline static int stbr__get_horizontal_contributors(stbr__info* stbr_info) { return stbr__get_horizontal_contributors_noinfo(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale); } -stbr_inline static int stbr__get_total_coefficients_noinfo(stbr_filter filter, int input_w, int output_w, float horizontal_scale) +stbr__inline static int stbr__get_total_coefficients_noinfo(stbr_filter filter, int input_w, int output_w, float horizontal_scale) { return stbr__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * stbr__get_filter_pixel_width(filter, input_w, output_w, horizontal_scale); } -stbr_inline static int stbr__get_total_coefficients(stbr__info* stbr_info) +stbr__inline static int stbr__get_total_coefficients(stbr__info* stbr_info) { return stbr__get_total_coefficients_noinfo(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale); } -stbr_inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_info, int n) +stbr__inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_info, int n) { STBR_DEBUG_ASSERT(n >= 0 && n < stbr__get_horizontal_contributors(stbr_info)); return &stbr_info->horizontal_contributors[n]; } -stbr_inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, int c) +stbr__inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, int c) { return &stbr_info->horizontal_coefficients[stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale)*n + c]; } -stbr_inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) +stbr__inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) { switch (edge) { @@ -1029,7 +1025,7 @@ static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffe } -static stbr_inline void stbr__encode_pixel(stbr__info* stbr_info, void* output_buffer, int output_pixel_index, float* encode_buffer, int encode_pixel_index, int channels, int alpha_channel, int decode) +static stbr__inline void stbr__encode_pixel(stbr__info* stbr_info, void* output_buffer, int output_pixel_index, float* encode_buffer, int encode_pixel_index, int channels, int alpha_channel, int decode) { int n; float divide_alpha = 1; @@ -1358,6 +1354,39 @@ static void stbr__buffer_loop_downsample(stbr__info* stbr_info) stbr__empty_ring_buffer(stbr_info, stbr_info->output_h); } +static stbr__inline stbr_size_t stbr__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter) +{ + float horizontal_scale = ((float)output_w / input_w) / (s1 - s0); + float vertical_scale = ((float)output_h / input_h) / (t1 - t0); + + int pixel_margin = stbr__get_filter_pixel_margin(filter, input_w, output_w, horizontal_scale); + int filter_height = stbr__get_filter_pixel_width(filter, input_h, output_h, vertical_scale); + + int info_size = sizeof(stbr__info); + int contributors_size = stbr__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(stbr__contributors); + int horizontal_coefficients_size = stbr__get_total_coefficients_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(float); + int vertical_coefficients_size = filter_height * sizeof(float); + int decode_buffer_size = (input_w + pixel_margin*2) * channels * sizeof(float); + int horizontal_buffer_size = output_w * channels * sizeof(float); + int ring_buffer_size = output_w * channels * filter_height * sizeof(float); + int encode_buffer_size = channels * sizeof(float); + + STBR_ASSERT(filter != 0); + STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); // this now happens too late + + if (stbr__use_upsampling(horizontal_scale)) + // The horizontal buffer is for when we're downsampling the height and we + // can't output the result of sampling the decode buffer directly into the + // ring buffers. + horizontal_buffer_size = 0; + else + // The encode buffer is to retain precision in the height upsampling method + // and isn't used when height downsampling. + encode_buffer_size = 0; + + return info_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size; +} + static int stbr__resize_allocated(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, @@ -1500,40 +1529,7 @@ static int stbr__resize_allocated(const void* input_data, int input_w, int input } -static stbr_inline stbr_size_t stbr__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter) -{ - float horizontal_scale = ((float)output_w / input_w) / (s1 - s0); - float vertical_scale = ((float)output_h / input_h) / (t1 - t0); - - int pixel_margin = stbr__get_filter_pixel_margin(filter, input_w, output_w, horizontal_scale); - int filter_height = stbr__get_filter_pixel_width(filter, input_h, output_h, vertical_scale); - - int info_size = sizeof(stbr__info); - int contributors_size = stbr__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(stbr__contributors); - int horizontal_coefficients_size = stbr__get_total_coefficients_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(float); - int vertical_coefficients_size = filter_height * sizeof(float); - int decode_buffer_size = (input_w + pixel_margin*2) * channels * sizeof(float); - int horizontal_buffer_size = output_w * channels * sizeof(float); - int ring_buffer_size = output_w * channels * filter_height * sizeof(float); - int encode_buffer_size = channels * sizeof(float); - - STBR_ASSERT(filter != 0); - STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); // this now happens too late - - if (stbr__use_upsampling(horizontal_scale)) - // The horizontal buffer is for when we're downsampling the height and we - // can't output the result of sampling the decode buffer directly into the - // ring buffers. - horizontal_buffer_size = 0; - else - // The encode buffer is to retain precision in the height upsampling method - // and isn't used when height downsampling. - encode_buffer_size = 0; - - return info_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size; -} - -STBRDEF stbr_inline int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, +STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, int channels, int alpha_channel, stbr_uint32 flags, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace) @@ -1552,63 +1548,63 @@ STBRDEF stbr_inline int stbr_resize_arbitrary(const void* input_data, int input_ return result; } -STBRDEF stbr_inline int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int input_w, int input_h, +STBRDEF int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB); } -STBRDEF stbr_inline int stbr_resize_uint16_srgb(const stbr_uint16* input_data, int input_w, int input_h, +STBRDEF int stbr_resize_uint16_srgb(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB); } -STBRDEF stbr_inline int stbr_resize_uint32_srgb(const stbr_uint32* input_data, int input_w, int input_h, +STBRDEF int stbr_resize_uint32_srgb(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB); } -STBRDEF stbr_inline int stbr_resize_float_srgb(const float* input_data, int input_w, int input_h, +STBRDEF int stbr_resize_float_srgb(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, int channels, stbr_filter filter, stbr_edge edge) { return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB); } -STBRDEF stbr_inline int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_data, int input_w, int input_h, +STBRDEF int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT8, filter, edge, edge, colorspace); } -STBRDEF stbr_inline int stbr_resize_uint16_alphaweighted(const stbr_uint16* input_data, int input_w, int input_h, +STBRDEF int stbr_resize_uint16_alphaweighted(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT16, filter, edge, edge, colorspace); } -STBRDEF stbr_inline int stbr_resize_uint32_alphaweighted(const stbr_uint32* input_data, int input_w, int input_h, +STBRDEF int stbr_resize_uint32_alphaweighted(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT32, filter, edge, edge, colorspace); } -STBRDEF stbr_inline int stbr_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, +STBRDEF int stbr_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) { return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_FLOAT, filter, edge, edge, colorspace); } -STBRDEF stbr_inline int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, +STBRDEF int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, stbr_uint8* output_data, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) @@ -1616,7 +1612,7 @@ STBRDEF stbr_inline int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB); } -STBRDEF stbr_inline int stbr_resize_uint16_subpixel(const stbr_uint16* input_data, int input_w, int input_h, +STBRDEF int stbr_resize_uint16_subpixel(const stbr_uint16* input_data, int input_w, int input_h, stbr_uint16* output_data, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) @@ -1624,7 +1620,7 @@ STBRDEF stbr_inline int stbr_resize_uint16_subpixel(const stbr_uint16* input_dat return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB); } -STBRDEF stbr_inline int stbr_resize_uint32_subpixel(const stbr_uint32* input_data, int input_w, int input_h, +STBRDEF int stbr_resize_uint32_subpixel(const stbr_uint32* input_data, int input_w, int input_h, stbr_uint32* output_data, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) @@ -1632,7 +1628,7 @@ STBRDEF stbr_inline int stbr_resize_uint32_subpixel(const stbr_uint32* input_dat return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB); } -STBRDEF stbr_inline int stbr_resize_float_subpixel(const float* input_data, int input_w, int input_h, +STBRDEF int stbr_resize_float_subpixel(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter, stbr_edge edge) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 8a25d43..ac9e200 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -1,7 +1,8 @@ -#ifdef _WIN32 +#if defined(_WIN32) && _MSC_VER > 1200 #define STBR_ASSERT(x) \ - if (!(x)) \ - __debugbreak(); + if (!(x)) { \ + __debugbreak(); \ + } else #else #include #define STBR_ASSERT(x) assert(x) @@ -407,11 +408,11 @@ void test_subpixel_2() stbr_resize_arbitrary(image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_WRAP, STBR_EDGE_WRAP, STBR_COLORSPACE_SRGB); stbr_resize_arbitrary(large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); - for (int x = 0; x < 16; x++) + {for (int x = 0; x < 16; x++) { for (int y = 0; y < 16; y++) STBR_ASSERT(output_data_1[y * 16 + x] == output_data_2[y * 16 + x]); - } + }} } void test_subpixel_3() @@ -453,6 +454,8 @@ void test_subpixel_4() void test_suite() { + int i; + test_subpixel_1(); test_subpixel_2(); test_subpixel_3(); @@ -460,16 +463,16 @@ void test_suite() test_premul("barbara.png"); - for (int i = 0; i < 10; i++) + for (i = 0; i < 10; i++) test_subpixel("barbara.png", 0.5f, 0.5f, (float)i / 10, 1); - for (int i = 0; i < 10; i++) + for (i = 0; i < 10; i++) test_subpixel("barbara.png", 0.5f, 0.5f, 1, (float)i / 10); - for (int i = 0; i < 10; i++) + for (i = 0; i < 10; i++) test_subpixel("barbara.png", 2, 2, (float)i / 10, 1); - for (int i = 0; i < 10; i++) + for (i = 0; i < 10; i++) test_subpixel("barbara.png", 2, 2, 1, (float)i / 10); // Channels test @@ -511,35 +514,35 @@ void test_suite() resize_image("barbara.png", 0.5f, 0.5f, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png"); resize_image("barbara.png", 0.5f, 0.5f, STBR_FILTER_MITCHELL, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); - for (int i = 10; i < 100; i++) + for (i = 10; i < 100; i++) { char outname[200]; sprintf(outname, "test-output/barbara-width-%d.jpg", i); resize_image("barbara.png", (float)i / 100, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); } - for (int i = 110; i < 500; i += 10) + for (i = 110; i < 500; i += 10) { char outname[200]; sprintf(outname, "test-output/barbara-width-%d.jpg", i); resize_image("barbara.png", (float)i / 100, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); } - for (int i = 10; i < 100; i++) + for (i = 10; i < 100; i++) { char outname[200]; sprintf(outname, "test-output/barbara-height-%d.jpg", i); resize_image("barbara.png", 1, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); } - for (int i = 110; i < 500; i += 10) + for (i = 110; i < 500; i += 10) { char outname[200]; sprintf(outname, "test-output/barbara-height-%d.jpg", i); resize_image("barbara.png", 1, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); } - for (int i = 50; i < 200; i += 10) + for (i = 50; i < 200; i += 10) { char outname[200]; sprintf(outname, "test-output/barbara-width-height-%d.jpg", i); diff --git a/tests/resize.dsp b/tests/resize.dsp new file mode 100644 index 0000000..232b1c4 --- /dev/null +++ b/tests/resize.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="resize" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=resize - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "resize.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "resize.mak" CFG="resize - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "resize - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "resize - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "resize - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "resize - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "resize - Win32 Release" +# Name "resize - Win32 Debug" +# Begin Source File + +SOURCE=..\resample_test.cpp +# End Source File +# End Target +# End Project diff --git a/tests/stb.dsw b/tests/stb.dsw index fc4aed8..c8d1388 100644 --- a/tests/stb.dsw +++ b/tests/stb.dsw @@ -63,6 +63,18 @@ Package=<4> ############################################################################### +Project: "resize"=.\resize\resize.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + Project: "stb"=.\stb.dsp - Package Owner=<4> Package=<5> From eb0781fda0bc31c5397a8a4fff3ce45fa4a8aef2 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 18 Aug 2014 09:01:33 -0700 Subject: [PATCH 083/168] add comments describing the purpose of the s,t-rectangle tests --- tests/resample_test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index ac9e200..2a87eca 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -350,6 +350,7 @@ void test_premul(const char* file) free(output_data); } +// test that splitting a pow-2 image into tiles produces identical results void test_subpixel_1() { unsigned char image[8 * 8]; @@ -379,6 +380,7 @@ void test_subpixel_1() } } +// test that replicating an image and using a subtile of it produces same results as wraparound void test_subpixel_2() { unsigned char image[8 * 8]; @@ -415,6 +417,7 @@ void test_subpixel_2() }} } +// test that 0,0,1,1 subpixel produces same result as no-rect void test_subpixel_3() { unsigned char image[8 * 8]; @@ -437,6 +440,7 @@ void test_subpixel_3() } } +// test that 1:1 resample using s,t=0,0,1,1 with bilinear produces original image void test_subpixel_4() { unsigned char image[8 * 8]; From 6ef563d08950fc9b405ee85b7c4f5aec64b82926 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 18 Aug 2014 09:12:59 -0700 Subject: [PATCH 084/168] rename to stb_image_resize.h --- stb_image_resize.h | 1646 ++++++++++++++++++++++++++++++++++++++++++++ stb_resample.h | 1645 ------------------------------------------- tests/resample_test.cpp | 138 ++-- tests/resample_test_c.c | 12 +- tests/test_c_compilation.c | 4 +- 5 files changed, 1720 insertions(+), 1725 deletions(-) create mode 100644 stb_image_resize.h delete mode 100644 stb_resample.h diff --git a/stb_image_resize.h b/stb_image_resize.h new file mode 100644 index 0000000..461f74f --- /dev/null +++ b/stb_image_resize.h @@ -0,0 +1,1646 @@ +/* stb_image_resize - v0.50 - public domain image resampling + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_RESIZE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + #define STBIR_ASSERT(x) to avoid using assert.h. + + #define STBIR_MALLOC and STBIR_FREE to avoid using stdlib.h malloc. This will apply + to all functions except stbir_resize_arbitrary(), which doesn't allocate memory. + + QUICK NOTES: + Written with emphasis on usage and speed. Only the resize operation is + currently supported, no rotations or translations. + + 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. + + Latest revisions: + 0.50 (2014-07-29) first released version + + See end of file for full revision history. + + 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 ) + + Initial implementation by Jorge L Rodriguez, @VinoBS +*/ + +#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H +#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H + +// Basic usage: +// result = stbir_resize_uint8_srgb(input_data, input_w, input_h, output_data, output_w, output_h, channels, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP); +// * input_data is your supplied pixels. +// * output_data will be the resized pixels. It should be of size output_w * output_h * channels +// * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBIR_ASSERT() to see it. +// * If you're unsure of which filter to use, Catmull-Rom is a good upsampling filter and Mitchell is a good downsampling filter. +// +// +// Data types provided: uint8, uint16, uint32, float. +// +// +// Other function groups are provided, one for each data type, for more advanced functionality: +// +// stbir_resize_type_alphaweighted(input_data, input_w, input_h, output_data, output_w, output_h, channels, alpha_channel, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB) +// * alpha_channel - if nonnegative, this channel will be multiplied into all other channels before resampling, then divided back out after. +// +// stbir_resize_type_subpixel(input_data, input_w, input_h, output_data, output_w, output_h, s0, t0, s1, t1, channels, filter, edge) +// * 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. +// +// +// All functionality is offered in this function: +// +// result = stbir_resize_arbitrary(input_data, input_w, input_h, input_stride_in_bytes, +// output_data, output_w, output_h, output_stride_in_bytes, +// s0, t0, s1, t1, +// channels, alpha_channel, flags, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); +// +// * input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. +// * 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. +// * flags are from the stbir_flags enum and should be bitwise OR'd together. +// * First edge parameter is for horizontal edge behavior, second is for vertical. +// * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBIR_ASSERT() to see it. +// * Memory required grows approximately linearly with input and output size, but with discontinuities at input_w == output_w and input_h == output_height. +// * To use temporary memory, define an STBIR_MALLOC that returns the temp memory and make STBIR_FREE do nothing--each function only ever allocates one block + + +typedef enum +{ + STBIR_FILTER_NEAREST = 1, + STBIR_FILTER_BILINEAR = 2, + STBIR_FILTER_BICUBIC = 3, // A cubic b spline + STBIR_FILTER_CATMULLROM = 4, + STBIR_FILTER_MITCHELL = 5, +} stbir_filter; + +typedef enum +{ + STBIR_EDGE_CLAMP = 1, + STBIR_EDGE_REFLECT = 2, + STBIR_EDGE_WRAP = 3, +} stbir_edge; + +typedef enum +{ + STBIR_COLORSPACE_LINEAR, + STBIR_COLORSPACE_SRGB, + + STBIR_MAX_COLORSPACES, +} stbir_colorspace; + +typedef enum +{ + STBIR_TYPE_UINT8 , + STBIR_TYPE_UINT16, + STBIR_TYPE_UINT32, + STBIR_TYPE_FLOAT , + + STBIR_MAX_TYPES +} stbir_type; + +typedef enum +{ + STBIR_FLAG_NONPREMUL_ALPHA = (1 << 0), // The specified alpha channel will be multiplied into all other channels before resampling, then divided back out after. + STBIR_FLAG_FORCE_LINEAR_ALPHA = (1 << 1), // The specified alpha channel should be handled as a linear value even when doing sRGB operations. +} stbir_flags; + +typedef unsigned char stbir_uint8; + +#ifdef _MSC_VER +typedef unsigned short stbir_uint16; +typedef unsigned int stbir_uint32; +#else +#include +typedef uint16_t stbir_uint16; +typedef uint32_t stbir_uint32; +#endif + +typedef unsigned int stbir_size_t; // to avoid including a header for size_t + +#ifdef STB_IMAGE_RESIZE_STATIC +#define STBRDEF static +#else +#ifdef __cplusplus +#define STBRDEF extern "C" +#else +#define STBRDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - sRGB type-safe image resizing. +// + +STBRDEF int stbir_resize_uint8_srgb(const stbir_uint8* input_data, int input_w, int input_h, + stbir_uint8* output_data, int output_w, int output_h, + int channels, stbir_filter filter, stbir_edge edge); + +STBRDEF int stbir_resize_uint16_srgb(const stbir_uint16* input_data, int input_w, int input_h, + stbir_uint16* output_data, int output_w, int output_h, + int channels, stbir_filter filter, stbir_edge edge); + +STBRDEF int stbir_resize_uint32_srgb(const stbir_uint32* input_data, int input_w, int input_h, + stbir_uint32* output_data, int output_w, int output_h, + int channels, stbir_filter filter, stbir_edge edge); + +STBRDEF int stbir_resize_float_srgb(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + int channels, stbir_filter filter, stbir_edge edge); + + +STBRDEF int stbir_resize_uint8_alphaweighted(const stbir_uint8* input_data, int input_w, int input_h, + stbir_uint8* output_data, int output_w, int output_h, + int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace); + +STBRDEF int stbir_resize_uint16_alphaweighted(const stbir_uint16* input_data, int input_w, int input_h, + stbir_uint16* output_data, int output_w, int output_h, + int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace); + +STBRDEF int stbir_resize_uint32_alphaweighted(const stbir_uint32* input_data, int input_w, int input_h, + stbir_uint32* output_data, int output_w, int output_h, + int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace); + +STBRDEF int stbir_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace); + + +STBRDEF int stbir_resize_uint8_subpixel(const stbir_uint8* input_data, int input_w, int input_h, + stbir_uint8* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbir_filter filter, stbir_edge edge); + +STBRDEF int stbir_resize_uint16_subpixel(const stbir_uint16* input_data, int input_w, int input_h, + stbir_uint16* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbir_filter filter, stbir_edge edge); + +STBRDEF int stbir_resize_uint32_subpixel(const stbir_uint32* input_data, int input_w, int input_h, + stbir_uint32* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbir_filter filter, stbir_edge edge); + +STBRDEF int stbir_resize_float_subpixel(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbir_filter filter, stbir_edge edge); + + +STBRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, + int channels, int alpha_channel, stbir_uint32 flags, stbir_type type, stbir_filter filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace); + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H + +#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION + +#ifndef STBIR_ASSERT +#include +#define STBIR_ASSERT(x) assert(x) +#endif + +#ifdef STBIR_DEBUG +#define STBIR__DEBUG_ASSERT STBIR_ASSERT +#else +#define STBIR__DEBUG_ASSERT +#endif + +// If you hit this it means I haven't done it yet. +#define STBIR__UNIMPLEMENTED(x) STBIR_ASSERT(!(x)) + +// For memset +#include + +#include + +#ifndef STBIR_MALLOC +#include + +#define STBIR_MALLOC(x) malloc(x) +#define STBIR_FREE(x) free(x) +#endif + + +#ifndef _MSC_VER +#ifdef __cplusplus +#define stbir__inline inline +#else +#define stbir__inline +#endif +#else +#define stbir__inline __forceinline +#endif + + +// should produce compiler error if size is wrong +typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBIR_NOTUSED(v) (void)(v) +#else +#define STBIR_NOTUSED(v) (void)sizeof(v) +#endif + +#define STBIR_ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) + +#define STBIR__MAX_UNROLLED_CHANNELS 4 + + +// must match stbir_type +static unsigned char stbir__type_size[] = { + 1, // STBIR_TYPE_UINT8 + 2, // STBIR_TYPE_UINT16 + 4, // STBIR_TYPE_UINT32 + 4, // STBIR_TYPE_FLOAT +}; + +// Kernel function centered at 0 +typedef float (stbir__kernel_fn)(float x); + +typedef struct +{ + stbir__kernel_fn* kernel; + float support; +} stbir__filter_info; + +// When upsampling, the contributors are which source pixels contribute. +// When downsampling, the contributors are which destination pixels are contributed to. +typedef struct +{ + int n0; // First contributing pixel + int n1; // Last contributing pixel +} stbir__contributors; + +typedef struct +{ + const void* input_data; + int input_w; + int input_h; + int input_stride_bytes; + + void* output_data; + int output_w; + int output_h; + int output_stride_bytes; + + float s0, t0, s1, t1; + + float horizontal_shift; // Units: output pixels + float vertical_shift; // Units: output pixels + float horizontal_scale; + float vertical_scale; + + int channels; + int alpha_channel; + stbir_uint32 flags; + stbir_type type; + stbir_filter filter; + stbir_edge edge_horizontal; + stbir_edge edge_vertical; + stbir_colorspace colorspace; + + stbir__contributors* horizontal_contributors; + float* horizontal_coefficients; + + stbir__contributors vertical_contributors; + float* vertical_coefficients; + + int decode_buffer_pixels; + float* decode_buffer; + + float* horizontal_buffer; + + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; + float* ring_buffer; + + float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. +} stbir__info; + +static stbir__inline int stbir__min(int a, int b) +{ + return a < b ? a : b; +} + +static stbir__inline int stbir__max(int a, int b) +{ + return a > b ? a : b; +} + +static stbir__inline float stbir__saturate(float x) +{ + if (x < 0) + return 0; + + if (x > 1) + return 1; + + return x; +} + +static float stbir__srgb_uchar_to_linear_float[256] = { + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f +}; + +static unsigned char stbir__linear_uchar_to_srgb_uchar[256] = { + 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, + 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, + 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, + 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, + 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, + 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, + 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, + 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, + 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, + 251, 251, 252, 252, 253, 253, 254, 254, 255 +}; + +float stbir__srgb_to_linear(float f) +{ + if (f <= 0.04045f) + return f / 12.92f; + else + return (float)pow((f + 0.055f) / 1.055f, 2.4f); +} + +float stbir__linear_to_srgb(float f) +{ + if (f <= 0.0031308f) + return f * 12.92f; + else + return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; +} + + + +static float stbir__filter_nearest(float x) +{ + if (x <= -0.5f) + return 0; + else if (x > 0.5f) + return 0; + else + return 1; +} + +static float stbir__filter_bilinear(float x) +{ + x = (float)fabs(x); + + if (x <= 1.0f) + return 1 - x; + else + return 0; +} + +static float stbir__filter_bicubic(float x) +{ + x = (float)fabs(x); + + if (x < 1.0f) + return 0.66666666666f + x*x*(0.5f*x - 1); + else if (x < 2.0f) + return 1.3333333333f + x*(-2 + x*(1 - 0.16666666f * x)); + + return (0.0f); +} + +static float stbir__filter_catmullrom(float x) +{ + x = (float)fabs(x); + + if (x < 1.0f) + return 1 - x*x*(2.5f - 1.5f*x); + else if (x < 2.0f) + return 2 - x*(4 + x*(0.5f*x - 2.5f)); + + return (0.0f); +} + +static float stbir__filter_mitchell(float x) +{ + x = (float)fabs(x); + + if (x < 1.0f) + return 0.8888888888f + x*x*(1.1666666666666f * x - 2.0f); + else if (x < 2.0f) + return 1.777777777777f + x*(-3.3333333333f + x*(2 - 0.3888888888888f*x)); + + return (0.0f); +} + +static stbir__filter_info stbir__filter_info_table[] = { + { NULL, 0.0f }, + { stbir__filter_nearest, 0.5f }, + { stbir__filter_bilinear, 1.0f }, + { stbir__filter_bicubic, 2.0f }, + { stbir__filter_catmullrom, 2.0f }, + { stbir__filter_mitchell, 2.0f }, +}; + +stbir__inline static int stbir__use_upsampling(float ratio) +{ + return ratio > 1; +} + +stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->horizontal_scale); +} + +stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->vertical_scale); +} + +// This is the maximum number of input samples that can affect an output sample +// with the given filter +stbir__inline static int stbir__get_filter_pixel_width(stbir_filter filter, int input_w, int output_w, float scale) +{ + STBIR_ASSERT(filter != 0); + STBIR_ASSERT(filter < STBIR_ARRAY_SIZE(stbir__filter_info_table)); + + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support * 2 / scale); +} + +stbir__inline static int stbir__get_filter_pixel_width_horizontal(stbir__info* stbir_info) +{ + return stbir__get_filter_pixel_width(stbir_info->filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); +} + +stbir__inline static int stbir__get_filter_pixel_width_vertical(stbir__info* stbir_info) +{ + return stbir__get_filter_pixel_width(stbir_info->filter, stbir_info->input_h, stbir_info->output_h, stbir_info->vertical_scale); +} + +// This is how much to expand buffers to account for filters seeking outside +// the image boundaries. +stbir__inline static int stbir__get_filter_pixel_margin(stbir_filter filter, int input_w, int output_w, float scale) +{ + return stbir__get_filter_pixel_width(filter, input_w, output_w, scale) / 2; +} + +stbir__inline static int stbir__get_filter_pixel_margin_horizontal(stbir__info* stbir_info) +{ + return stbir__get_filter_pixel_width(stbir_info->filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale) / 2; +} + +stbir__inline static int stbir__get_filter_pixel_margin_vertical(stbir__info* stbir_info) +{ + return stbir__get_filter_pixel_width(stbir_info->filter, stbir_info->input_h, stbir_info->output_h, stbir_info->vertical_scale) / 2; +} + +stbir__inline static int stbir__get_horizontal_contributors_noinfo(stbir_filter filter, int input_w, int output_w, float horizontal_scale) +{ + if (stbir__use_upsampling(horizontal_scale)) + return output_w; + else + return (input_w + stbir__get_filter_pixel_margin(filter, input_w, output_w, horizontal_scale) * 2); +} + +stbir__inline static int stbir__get_horizontal_contributors(stbir__info* stbir_info) +{ + return stbir__get_horizontal_contributors_noinfo(stbir_info->filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); +} + +stbir__inline static int stbir__get_total_coefficients_noinfo(stbir_filter filter, int input_w, int output_w, float horizontal_scale) +{ + return stbir__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * stbir__get_filter_pixel_width(filter, input_w, output_w, horizontal_scale); +} + +stbir__inline static int stbir__get_total_coefficients(stbir__info* stbir_info) +{ + return stbir__get_total_coefficients_noinfo(stbir_info->filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); +} + +stbir__inline static stbir__contributors* stbir__get_contributor(stbir__info* stbir_info, int n) +{ + STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_horizontal_contributors(stbir_info)); + return &stbir_info->horizontal_contributors[n]; +} + +stbir__inline static float* stbir__get_coefficient(stbir__info* stbir_info, int n, int c) +{ + return &stbir_info->horizontal_coefficients[stbir__get_filter_pixel_width(stbir_info->filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale)*n + c]; +} + +stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +{ + switch (edge) + { + case STBIR_EDGE_CLAMP: + if (n < 0) + return 0; + + if (n >= max) + return max - 1; + + return n; + + case STBIR_EDGE_REFLECT: + { + if (n < 0) + { + if (n < max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } + + return n; + } + + case STBIR_EDGE_WRAP: + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; + + if (m != 0) + m = max - m; + + return (m); + } + + default: + STBIR__UNIMPLEMENTED("Unimplemented edge type"); + return 0; + } +} + +// What input pixels contribute to this output pixel? +static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) +{ + float out_pixel_center = (float)n + 0.5f; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; + + *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; + *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); + *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); +} + +// What output pixels does this input pixel contribute to? +static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) +{ + float in_pixel_center = (float)n + 0.5f; + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; + + *out_center_of_in = in_pixel_center * scale_ratio - out_shift; + *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); + *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); +} + +static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + float total_filter = 0; + float filter_scale; + stbir_filter filter = stbir_info->filter; + + STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= stbir__get_filter_pixel_width_horizontal(stbir_info)); + + contributor->n0 = in_first_pixel; + contributor->n1 = in_last_pixel; + + STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + total_filter += coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center); + } + + STBIR__DEBUG_ASSERT(total_filter > 0.9); + STBIR__DEBUG_ASSERT(total_filter < 1.1f); // Make sure it's not way off. + + // Make sure the sum of all coefficients is 1. + filter_scale = 1 / total_filter; + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + coefficient_group[i] *= filter_scale; +} + +static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + stbir_filter filter = stbir_info->filter; + + STBIR__DEBUG_ASSERT(out_last_pixel - out_first_pixel <= stbir__get_filter_pixel_width_horizontal(stbir_info)); + + contributor->n0 = out_first_pixel; + contributor->n1 = out_last_pixel; + + STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float in_pixel_center = (float)(i + out_first_pixel) + 0.5f; + coefficient_group[i] = stbir__filter_info_table[filter].kernel((out_center_of_in - in_pixel_center)/scale_ratio); + } +} + +#ifdef STBIR_DEBUG +static void stbir__check_downsample_coefficients(stbir__info* stbir_info) +{ + int i; + for (i = 0; i < stbir_info->output_w; i++) + { + float total = 0; + int j; + for (j = 0; j < stbir__get_horizontal_contributors(stbir_info); j++) + { + if (i >= stbir_info->horizontal_contributors[j].n0 && i <= stbir_info->horizontal_contributors[j].n1) + { + float coefficient = *stbir__get_coefficient(stbir_info, j, i - stbir_info->horizontal_contributors[j].n0); + total += coefficient; + } + else if (i < stbir_info->horizontal_contributors[j].n0) + break; + } + + STBIR__DEBUG_ASSERT(total > 0.9f); + STBIR__DEBUG_ASSERT(total <= 1.0f + 1.0f / (pow(2.0f, 8.0f * stbir__type_size[stbir_info->type]) - 1)); + } +} +#endif + +// Each scan line uses the same kernel values so we should calculate the kernel +// values once and then we can use them for every scan line. +static void stbir__calculate_horizontal_filters(stbir__info* stbir_info) +{ + int n; + float scale_ratio = stbir_info->horizontal_scale; + + int total_contributors = stbir__get_horizontal_contributors(stbir_info); + + if (stbir__use_width_upsampling(stbir_info)) + { + float out_pixels_radius = stbir__filter_info_table[stbir_info->filter].support * scale_ratio; + + // Looping through out pixels + for (n = 0; n < total_contributors; n++) + { + float in_center_of_out; // Center of the current out pixel in the in pixel space + int in_first_pixel, in_last_pixel; + + stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, stbir_info->horizontal_shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); + + stbir__calculate_coefficients_upsample(stbir_info, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(stbir_info, n), stbir__get_coefficient(stbir_info, n, 0)); + } + } + else + { + float in_pixels_radius = stbir__filter_info_table[stbir_info->filter].support / scale_ratio; + + // Looping through in pixels + for (n = 0; n < total_contributors; n++) + { + float out_center_of_in; // Center of the current out pixel in the in pixel space + int out_first_pixel, out_last_pixel; + int n_adjusted = n - stbir__get_filter_pixel_margin_horizontal(stbir_info); + + stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, stbir_info->horizontal_shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); + + stbir__calculate_coefficients_downsample(stbir_info, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(stbir_info, n), stbir__get_coefficient(stbir_info, n, 0)); + } + +#ifdef STBIR_DEBUG + stbir__check_downsample_coefficients(stbir_info); +#endif + } +} + +static float* stbir__get_decode_buffer(stbir__info* stbir_info) +{ + // The 0 index of the decode buffer starts after the margin. This makes + // it okay to use negative indexes on the decode buffer. + return &stbir_info->decode_buffer[stbir__get_filter_pixel_margin_horizontal(stbir_info) * stbir_info->channels]; +} + +#define STBIR__DECODE(type, colorspace) ((type) * (STBIR_MAX_COLORSPACES) + (colorspace)) + +static void stbir__decode_scanline(stbir__info* stbir_info, int n) +{ + int x, c; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int input_w = stbir_info->input_w; + int input_stride = stbir_info->input_stride_bytes / stbir__type_size[stbir_info->type]; + const void* input_data = stbir_info->input_data; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir_edge edge_horizontal = stbir_info->edge_horizontal; + stbir_edge edge_vertical = stbir_info->edge_vertical; + int in_buffer_row_index = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride; + int max_x = input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info); + int decode = STBIR__DECODE(type, colorspace); + + for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255; + + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; + + if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255; + + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535; + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); + + if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535; + + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295); + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); + + if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295); + + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); + + if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; + + break; + + default: + STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; + } + + if (stbir_info->flags&STBIR_FLAG_NONPREMUL_ALPHA) + { + float alpha = decode_buffer[decode_pixel_index + alpha_channel]; + for (c = 0; c < channels; c++) + { + if (c == alpha_channel) + continue; + + decode_buffer[decode_pixel_index + c] *= alpha; + } + } + } +} + +static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) +{ + return &ring_buffer[index * ring_buffer_length]; +} + +static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) +{ + int ring_buffer_index; + float* ring_buffer; + + if (stbir_info->ring_buffer_begin_index < 0) + { + ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; + stbir_info->ring_buffer_first_scanline = n; + } + else + { + ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline) + 1) % stbir__get_filter_pixel_width_vertical(stbir_info); + STBIR__DEBUG_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); + } + + ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); + memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); + + stbir_info->ring_buffer_last_scanline = n; + + return ring_buffer; +} + + +static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, float* output_buffer) +{ + int x, k; + int output_w = stbir_info->output_w; + int kernel_pixel_width = stbir__get_filter_pixel_width_horizontal(stbir_info); + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + + for (x = 0; x < output_w; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int out_pixel_index = x * channels; + int coefficient_group_index = x * kernel_pixel_width; + int coefficient_counter = 0; + + STBIR__DEBUG_ASSERT(n1 >= n0); + STBIR__DEBUG_ASSERT(n0 >= -stbir__get_filter_pixel_margin_horizontal(stbir_info)); + STBIR__DEBUG_ASSERT(n1 >= -stbir__get_filter_pixel_margin_horizontal(stbir_info)); + STBIR__DEBUG_ASSERT(n0 < stbir_info->input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info)); + STBIR__DEBUG_ASSERT(n1 < stbir_info->input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info)); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_group_index + (coefficient_counter++); + int in_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_index]; + + int c; + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + } +} + +static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n, float* output_buffer) +{ + int x, k; + int input_w = stbir_info->input_w; + int output_w = stbir_info->output_w; + int kernel_pixel_width = stbir__get_filter_pixel_width_horizontal(stbir_info); + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int filter_pixel_margin = stbir__get_filter_pixel_margin_horizontal(stbir_info); + int max_x = input_w + filter_pixel_margin * 2; + + STBIR__DEBUG_ASSERT(!stbir__use_width_upsampling(stbir_info)); + + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * channels; + int max_n = stbir__min(n1, output_w-1); + int coefficient_group = x*kernel_pixel_width; + + STBIR__DEBUG_ASSERT(n1 >= n0); + + // Using min and max to avoid writing into invalid pixels. + for (k = stbir__max(n0, 0); k <= max_n; k++) + { + int coefficient_index = (k - n0) + coefficient_group; + int out_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_index]; + + int c; + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + } +} + +static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + // Now resample it into the ring buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + else + stbir__resample_horizontal_downsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. +} + +static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); + + // Now resample it into the horizontal buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, n, stbir_info->horizontal_buffer); + else + stbir__resample_horizontal_downsample(stbir_info, n, stbir_info->horizontal_buffer); + + // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. +} + +// Get the specified scan line from the ring buffer. +static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_size, int ring_buffer_length) +{ + int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_size; + return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); +} + + +static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* output_buffer, int output_pixel_index, float* encode_buffer, int encode_pixel_index, int channels, int alpha_channel, int decode) +{ + int n; + float divide_alpha = 1; + + if (stbir_info->flags&STBIR_FLAG_NONPREMUL_ALPHA) { + float alpha = encode_buffer[encode_pixel_index + alpha_channel]; + float reciprocal_alpha = alpha ? 1.0f / alpha : 0; + for (n = 0; n < channels; n++) + if (n != alpha_channel) + encode_buffer[encode_pixel_index + n] *= reciprocal_alpha; + } + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (n = 0; n < channels; n++) + ((unsigned char*)output_buffer)[output_pixel_index + n] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 255); + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (n = 0; n < channels; n++) + ((unsigned char*)output_buffer)[output_pixel_index + n] = stbir__linear_uchar_to_srgb_uchar[(unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 255)]; + + if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + ((unsigned char*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); + + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (n = 0; n < channels; n++) + ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 65535); + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (n = 0; n < channels; n++) + ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n])) * 65535); + + if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + ((unsigned short*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); + + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (n = 0; n < channels; n++) + ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbir__saturate(encode_buffer[encode_pixel_index + n])) * 4294967295); + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (n = 0; n < channels; n++) + ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n]))) * 4294967295); + + if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + ((unsigned int*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned int)(((double)stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel])) * 4294967295); + + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (n = 0; n < channels; n++) + ((float*)output_buffer)[output_pixel_index + n] = encode_buffer[encode_pixel_index + n]; + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (n = 0; n < channels; n++) + ((float*)output_buffer)[output_pixel_index + n] = stbir__linear_to_srgb(encode_buffer[encode_pixel_index + n]); + + if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + ((float*)output_buffer)[output_pixel_index + alpha_channel] = encode_buffer[encode_pixel_index + alpha_channel]; + + break; + + default: + STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; + } +} + +static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) +{ + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors* vertical_contributors = &stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int kernel_pixel_width = stbir__get_filter_pixel_width_vertical(stbir_info); + void* output_data = stbir_info->output_data; + float* encode_buffer = stbir_info->encode_buffer; + int decode = STBIR__DECODE(type, colorspace); + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + int n0,n1, output_row_index; + + stbir__calculate_coefficients_upsample(stbir_info, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); + + n0 = vertical_contributors->n0; + n1 = vertical_contributors->n1; + + output_row_index = n * stbir_info->output_stride_bytes / stbir__type_size[type]; + + STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); + STBIR__DEBUG_ASSERT(n0 >= in_first_scanline); + STBIR__DEBUG_ASSERT(n1 <= in_last_scanline); + + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + int out_pixel_index = output_row_index + x * channels; + int coefficient_counter = 0; + + STBIR__DEBUG_ASSERT(n1 >= n0); + + memset(encode_buffer, 0, sizeof(float) * channels); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_index]; + + int c; + for (c = 0; c < channels; c++) + encode_buffer[c] += ring_buffer_entry[in_pixel_index + c] * coefficient; + } + + stbir__encode_pixel(stbir_info, output_data, out_pixel_index, encode_buffer, 0, channels, alpha_channel, decode); + } +} + +static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) +{ + int x, k; + int output_w = stbir_info->output_w; + int output_h = stbir_info->output_h; + stbir__contributors* vertical_contributors = &stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int kernel_pixel_width = stbir__get_filter_pixel_width_vertical(stbir_info); + void* output_data = stbir_info->output_data; + float* horizontal_buffer = stbir_info->horizontal_buffer; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + int n0,n1,max_n; + + stbir__calculate_coefficients_downsample(stbir_info, stbir_info->vertical_scale, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); + + n0 = vertical_contributors->n0; + n1 = vertical_contributors->n1; + max_n = stbir__min(n1, output_h - 1); + + STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); + STBIR__DEBUG_ASSERT(n0 >= in_first_scanline); + STBIR__DEBUG_ASSERT(n1 <= in_last_scanline); + STBIR__DEBUG_ASSERT(n1 >= n0); + + // Using min and max to avoid writing into ring buffers that will be thrown out. + for (k = stbir__max(n0, 0); k <= max_n; k++) + { + int coefficient_index = k - n0; + + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_index]; + + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + + int c; + for (c = 0; c < channels; c++) + ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; + } + } +} + +static void stbir__buffer_loop_upsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + float out_scanlines_radius = stbir__filter_info_table[stbir_info->filter].support * scale_ratio; + + STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); + + for (y = 0; y < stbir_info->output_h; y++) + { + float in_center_of_out = 0; // Center of the current out scanline in the in scanline space + int in_first_scanline = 0, in_last_scanline = 0; + + stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); + + STBIR__DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbir__get_filter_pixel_width_vertical(stbir_info)); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (in_first_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir__get_filter_pixel_width_horizontal(stbir_info); + } + } + } + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); + + while (in_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now all buffers should be ready to write a row of vertical sampling. + stbir__resample_vertical_upsample(stbir_info, y, in_first_scanline, in_last_scanline, in_center_of_out); + } +} + +static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) +{ + int output_stride = stbir_info->output_stride_bytes / stbir__type_size[stbir_info->type]; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int output_w = stbir_info->output_w; + void* output_data = stbir_info->output_data; + int decode = STBIR__DECODE(type, colorspace); + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) + { + int x; + int output_row = stbir_info->ring_buffer_first_scanline * output_stride; + float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); + + for (x = 0; x < output_w; x++) + { + int pixel_index = x * channels; + int ring_pixel_index = pixel_index; + int output_pixel_index = output_row + pixel_index; + + stbir__encode_pixel(stbir_info, output_data, output_pixel_index, ring_buffer_entry, ring_pixel_index, channels, alpha_channel, decode); + } + } + + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir__get_filter_pixel_width_vertical(stbir_info); + } + } + } +} + +static void stbir__buffer_loop_downsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + int output_h = stbir_info->output_h; + float in_pixels_radius = stbir__filter_info_table[stbir_info->filter].support / scale_ratio; + int max_y = stbir_info->input_h + stbir__get_filter_pixel_margin_vertical(stbir_info); + + STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (y = -stbir__get_filter_pixel_margin_vertical(stbir_info); y < max_y; y++) + { + float out_center_of_in; // Center of the current out scanline in the in scanline space + int out_first_scanline, out_last_scanline; + + stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); + + STBIR__DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbir__get_filter_pixel_width_vertical(stbir_info)); + + if (out_last_scanline < 0 || out_first_scanline >= output_h) + continue; + + stbir__empty_ring_buffer(stbir_info, out_first_scanline); + + stbir__decode_and_resample_downsample(stbir_info, y); + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); + + while (out_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now the horizontal buffer is ready to write to all ring buffer rows. + stbir__resample_vertical_downsample(stbir_info, y, out_first_scanline, out_last_scanline, out_center_of_in); + } + + stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); +} + +static stbir__inline stbir_size_t stbir__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbir_filter filter) +{ + float horizontal_scale = ((float)output_w / input_w) / (s1 - s0); + float vertical_scale = ((float)output_h / input_h) / (t1 - t0); + + int pixel_margin = stbir__get_filter_pixel_margin(filter, input_w, output_w, horizontal_scale); + int filter_height = stbir__get_filter_pixel_width(filter, input_h, output_h, vertical_scale); + + int info_size = sizeof(stbir__info); + int contributors_size = stbir__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(stbir__contributors); + int horizontal_coefficients_size = stbir__get_total_coefficients_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(float); + int vertical_coefficients_size = filter_height * sizeof(float); + int decode_buffer_size = (input_w + pixel_margin*2) * channels * sizeof(float); + int horizontal_buffer_size = output_w * channels * sizeof(float); + int ring_buffer_size = output_w * channels * filter_height * sizeof(float); + int encode_buffer_size = channels * sizeof(float); + + STBIR_ASSERT(filter != 0); + STBIR_ASSERT(filter < STBIR_ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + + if (stbir__use_upsampling(horizontal_scale)) + // The horizontal buffer is for when we're downsampling the height and we + // can't output the result of sampling the decode buffer directly into the + // ring buffers. + horizontal_buffer_size = 0; + else + // The encode buffer is to retain precision in the height upsampling method + // and isn't used when height downsampling. + encode_buffer_size = 0; + + return info_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size; +} + +static int stbir__resize_allocated(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, + int channels, int alpha_channel, stbir_uint32 flags, stbir_type type, stbir_filter filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, + void* tempmem, stbir_size_t tempmem_size_in_bytes) +{ + stbir__info* stbir_info = (stbir__info*)tempmem; + + stbir_size_t memory_required = stbir__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + + int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w * stbir__type_size[type]; + int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w * stbir__type_size[type]; + +#ifdef STBIR_DEBUG_OVERWRITE_TEST +#define OVERWRITE_ARRAY_SIZE 8 + unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; + + stbir_size_t begin_forbidden = width_stride_output * (output_h - 1) + output_w * channels * stbir__type_size[type]; + memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); +#endif + + STBIR_ASSERT(filter != 0); + STBIR_ASSERT(filter < STBIR_ARRAY_SIZE(stbir__filter_info_table)); + + if (!filter || filter >= STBIR_ARRAY_SIZE(stbir__filter_info_table)) + return 0; + + STBIR_ASSERT(s1 > s0); + STBIR_ASSERT(t1 > t0); + + if (s1 <= s0 || t1 <= t0) + return 0; + + STBIR_ASSERT(s1 <= 1 && s0 >= 0 && t1 <= 1 && t0 >= 0); + + if (s1 > 1 || s0 < 0 || t1 > 1 || t0 < 0) + return 0; + + if (flags&(STBIR_FLAG_FORCE_LINEAR_ALPHA | STBIR_FLAG_NONPREMUL_ALPHA)) + STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < channels); + + if (alpha_channel >= channels) + return 0; + + STBIR_ASSERT(tempmem); + + if (!tempmem) + return 0; + + STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); + + if (tempmem_size_in_bytes < memory_required) + return 0; + + memset(tempmem, 0, tempmem_size_in_bytes); + + stbir_info->input_data = input_data; + stbir_info->input_w = input_w; + stbir_info->input_h = input_h; + stbir_info->input_stride_bytes = width_stride_input; + + stbir_info->output_data = output_data; + stbir_info->output_w = output_w; + stbir_info->output_h = output_h; + stbir_info->output_stride_bytes = width_stride_output; + + stbir_info->s0 = s0; + stbir_info->t0 = t0; + stbir_info->s1 = s1; + stbir_info->t1 = t1; + + stbir_info->horizontal_scale = ((float)output_w / input_w) / (s1 - s0); + stbir_info->vertical_scale = ((float)output_h / input_h) / (t1 - t0); + + stbir_info->horizontal_shift = s0 * input_w / (s1 - s0); + stbir_info->vertical_shift = t0 * input_h / (t1 - t0); + + stbir_info->channels = channels; + stbir_info->alpha_channel = alpha_channel; + stbir_info->flags = flags; + stbir_info->type = type; + stbir_info->filter = filter; + stbir_info->edge_horizontal = edge_horizontal; + stbir_info->edge_vertical = edge_vertical; + stbir_info->colorspace = colorspace; + + stbir_info->ring_buffer_length_bytes = output_w * channels * sizeof(float); + stbir_info->decode_buffer_pixels = input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info) * 2; + +#define STBIR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) + + stbir_info->horizontal_contributors = STBIR__NEXT_MEMPTR(stbir_info, sizeof(stbir__info), stbir__contributors); + stbir_info->horizontal_coefficients = STBIR__NEXT_MEMPTR(stbir_info->horizontal_contributors, stbir__get_horizontal_contributors(stbir_info) * sizeof(stbir__contributors), float); + stbir_info->vertical_coefficients = STBIR__NEXT_MEMPTR(stbir_info->horizontal_coefficients, stbir__get_total_coefficients(stbir_info) * sizeof(float), float); + stbir_info->decode_buffer = STBIR__NEXT_MEMPTR(stbir_info->vertical_coefficients, stbir__get_filter_pixel_width_vertical(stbir_info) * sizeof(float), float); + + if (stbir__use_height_upsampling(stbir_info)) + { + stbir_info->horizontal_buffer = NULL; + stbir_info->ring_buffer = STBIR__NEXT_MEMPTR(stbir_info->decode_buffer, stbir_info->decode_buffer_pixels * channels * sizeof(float), float); + stbir_info->encode_buffer = STBIR__NEXT_MEMPTR(stbir_info->ring_buffer, stbir_info->ring_buffer_length_bytes * stbir__get_filter_pixel_width_horizontal(stbir_info), float); + + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(stbir_info->encode_buffer, stbir_info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + else + { + stbir_info->horizontal_buffer = STBIR__NEXT_MEMPTR(stbir_info->decode_buffer, stbir_info->decode_buffer_pixels * channels * sizeof(float), float); + stbir_info->ring_buffer = STBIR__NEXT_MEMPTR(stbir_info->horizontal_buffer, output_w * channels * sizeof(float), float); + stbir_info->encode_buffer = NULL; + + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(stbir_info->ring_buffer, stbir_info->ring_buffer_length_bytes * stbir__get_filter_pixel_width_vertical(stbir_info), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + +#undef STBIR__NEXT_MEMPTR + + // This signals that the ring buffer is empty + stbir_info->ring_buffer_begin_index = -1; + + stbir__calculate_horizontal_filters(stbir_info); + + if (stbir__use_height_upsampling(stbir_info)) + stbir__buffer_loop_upsample(stbir_info); + else + stbir__buffer_loop_downsample(stbir_info); + +#ifdef STBIR_DEBUG_OVERWRITE_TEST + STBIR__DEBUG_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); +#endif + + return 1; +} + + +STBRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, + int channels, int alpha_channel, stbir_uint32 flags, stbir_type type, stbir_filter filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) +{ + int result; + size_t memory_required = stbir__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + void* extra_memory = STBIR_MALLOC(memory_required); + + if (!extra_memory) + return 0; + + result = stbir__resize_allocated(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, alpha_channel, flags, type, filter, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); + + STBIR_FREE(extra_memory); + + return result; +} + +STBRDEF int stbir_resize_uint8_srgb(const stbir_uint8* input_data, int input_w, int input_h, + stbir_uint8* output_data, int output_w, int output_h, + int channels, stbir_filter filter, stbir_edge edge) +{ + return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBIR_TYPE_UINT8, filter, edge, edge, STBIR_COLORSPACE_SRGB); +} + +STBRDEF int stbir_resize_uint16_srgb(const stbir_uint16* input_data, int input_w, int input_h, + stbir_uint16* output_data, int output_w, int output_h, + int channels, stbir_filter filter, stbir_edge edge) +{ + return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBIR_TYPE_UINT16, filter, edge, edge, STBIR_COLORSPACE_SRGB); +} + +STBRDEF int stbir_resize_uint32_srgb(const stbir_uint32* input_data, int input_w, int input_h, + stbir_uint32* output_data, int output_w, int output_h, + int channels, stbir_filter filter, stbir_edge edge) +{ + return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBIR_TYPE_UINT32, filter, edge, edge, STBIR_COLORSPACE_SRGB); +} + +STBRDEF int stbir_resize_float_srgb(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + int channels, stbir_filter filter, stbir_edge edge) +{ + return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBIR_TYPE_FLOAT, filter, edge, edge, STBIR_COLORSPACE_SRGB); +} + +STBRDEF int stbir_resize_uint8_alphaweighted(const stbir_uint8* input_data, int input_w, int input_h, + stbir_uint8* output_data, int output_w, int output_h, + int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace) +{ + return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT8, filter, edge, edge, colorspace); +} + +STBRDEF int stbir_resize_uint16_alphaweighted(const stbir_uint16* input_data, int input_w, int input_h, + stbir_uint16* output_data, int output_w, int output_h, + int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace) +{ + return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT16, filter, edge, edge, colorspace); +} + +STBRDEF int stbir_resize_uint32_alphaweighted(const stbir_uint32* input_data, int input_w, int input_h, + stbir_uint32* output_data, int output_w, int output_h, + int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace) +{ + return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT32, filter, edge, edge, colorspace); +} + +STBRDEF int stbir_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace) +{ + return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_FLOAT, filter, edge, edge, colorspace); +} + +STBRDEF int stbir_resize_uint8_subpixel(const stbir_uint8* input_data, int input_w, int input_h, + stbir_uint8* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbir_filter filter, stbir_edge edge) +{ + return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_UINT8, filter, edge, edge, STBIR_COLORSPACE_SRGB); +} + +STBRDEF int stbir_resize_uint16_subpixel(const stbir_uint16* input_data, int input_w, int input_h, + stbir_uint16* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbir_filter filter, stbir_edge edge) +{ + return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_UINT16, filter, edge, edge, STBIR_COLORSPACE_SRGB); +} + +STBRDEF int stbir_resize_uint32_subpixel(const stbir_uint32* input_data, int input_w, int input_h, + stbir_uint32* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbir_filter filter, stbir_edge edge) +{ + return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_UINT32, filter, edge, edge, STBIR_COLORSPACE_SRGB); +} + +STBRDEF int stbir_resize_float_subpixel(const float* input_data, int input_w, int input_h, + float* output_data, int output_w, int output_h, + float s0, float t0, float s1, float t1, + int channels, stbir_filter filter, stbir_edge edge) +{ + return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_FLOAT, filter, edge, edge, STBIR_COLORSPACE_SRGB); +} + +#endif // STB_IMAGE_RESIZE_IMPLEMENTATION + +/* +revision history: + 0.50 (2014-07-29) + first released version +*/ diff --git a/stb_resample.h b/stb_resample.h deleted file mode 100644 index 240ecb9..0000000 --- a/stb_resample.h +++ /dev/null @@ -1,1645 +0,0 @@ -/* stb_resample - v0.50 - public domain image resampling - no warranty implied; use at your own risk - - Do this: - #define STB_RESAMPLE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - #define STBR_ASSERT(x) to avoid using assert.h. - - #define STBR_MALLOC and STBR_FREE to avoid using stdlib.h malloc. This will apply - to all functions except stbr_resize_arbitrary(), which doesn't allocate memory. - - QUICK NOTES: - Written with emphasis on usage and speed. Only the resize operation is - currently supported, no rotations or translations. - - Supports arbitrary resize for separable filters. For a list of - supported filters see the stbr_filter enum. To add a new filter, - write a filter function and add it to stbr__filter_info_table. - - Latest revisions: - 0.50 (2014-07-29) first released version - - See end of file for full revision history. - - 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 ) - - Initial implementation by Jorge L Rodriguez, @VinoBS -*/ - -#ifndef STBR_INCLUDE_STB_RESAMPLE_H -#define STBR_INCLUDE_STB_RESAMPLE_H - -// Basic usage: -// result = stbr_resize_uint8_srgb(input_data, input_w, input_h, output_data, output_w, output_h, channels, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); -// * input_data is your supplied pixels. -// * output_data will be the resized pixels. It should be of size output_w * output_h * channels -// * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. -// * If you're unsure of which filter to use, Catmull-Rom is a good upsampling filter and Mitchell is a good downsampling filter. -// -// -// Data types provided: uint8, uint16, uint32, float. -// -// -// Other function groups are provided, one for each data type, for more advanced functionality: -// -// stbr_resize_type_alphaweighted(input_data, input_w, input_h, output_data, output_w, output_h, channels, alpha_channel, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB) -// * alpha_channel - if nonnegative, this channel will be multiplied into all other channels before resampling, then divided back out after. -// -// stbr_resize_type_subpixel(input_data, input_w, input_h, output_data, output_w, output_h, s0, t0, s1, t1, channels, filter, edge) -// * 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. -// -// -// All functionality is offered in this function: -// -// result = stbr_resize_arbitrary(input_data, input_w, input_h, input_stride_in_bytes, -// output_data, output_w, output_h, output_stride_in_bytes, -// s0, t0, s1, t1, -// channels, alpha_channel, flags, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); -// -// * input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. -// * 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. -// * flags are from the stbr_flags enum and should be bitwise OR'd together. -// * First edge parameter is for horizontal edge behavior, second is for vertical. -// * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBR_ASSERT() to see it. -// * Memory required grows approximately linearly with input and output size, but with discontinuities at input_w == output_w and input_h == output_height. -// * To use temporary memory, define an STBR_MALLOC that returns the temp memory and make STBR_FREE do nothing--each function only ever allocates one block - - -typedef enum -{ - STBR_FILTER_NEAREST = 1, - STBR_FILTER_BILINEAR = 2, - STBR_FILTER_BICUBIC = 3, // A cubic b spline - STBR_FILTER_CATMULLROM = 4, - STBR_FILTER_MITCHELL = 5, -} stbr_filter; - -typedef enum -{ - STBR_EDGE_CLAMP = 1, - STBR_EDGE_REFLECT = 2, - STBR_EDGE_WRAP = 3, -} stbr_edge; - -typedef enum -{ - STBR_COLORSPACE_LINEAR, - STBR_COLORSPACE_SRGB, - - STBR_MAX_COLORSPACES, -} stbr_colorspace; - -typedef enum -{ - STBR_TYPE_UINT8 , - STBR_TYPE_UINT16, - STBR_TYPE_UINT32, - STBR_TYPE_FLOAT , - - STBR_MAX_TYPES -} stbr_type; - -static unsigned char stbr__type_size[] = { - 1, // STBR_TYPE_UINT8 - 2, // STBR_TYPE_UINT16 - 4, // STBR_TYPE_UINT32 - 4, // STBR_TYPE_FLOAT -}; - -typedef enum -{ - STBR_FLAG_NONPREMUL_ALPHA = (1 << 0), // The specified alpha channel will be multiplied into all other channels before resampling, then divided back out after. - STBR_FLAG_FORCE_LINEAR_ALPHA = (1 << 1), // The specified alpha channel should be handled as a linear value even when doing sRGB operations. -} stbr_flags; - -typedef unsigned char stbr_uint8; - -#ifdef _MSC_VER -typedef unsigned short stbr_uint16; -typedef unsigned int stbr_uint32; -#else -#include -typedef uint16_t stbr_uint16; -typedef uint32_t stbr_uint32; -#endif - -typedef unsigned int stbr_size_t; // to avoid including a header for size_t - -#ifdef STB_RESAMPLE_STATIC -#define STBRDEF static -#else -#ifdef __cplusplus -#define STBRDEF extern "C" -#else -#define STBRDEF extern -#endif -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - sRGB type-safe image resizing. -// - -STBRDEF int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int input_w, int input_h, - stbr_uint8* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge); - -STBRDEF int stbr_resize_uint16_srgb(const stbr_uint16* input_data, int input_w, int input_h, - stbr_uint16* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge); - -STBRDEF int stbr_resize_uint32_srgb(const stbr_uint32* input_data, int input_w, int input_h, - stbr_uint32* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge); - -STBRDEF int stbr_resize_float_srgb(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge); - - -STBRDEF int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_data, int input_w, int input_h, - stbr_uint8* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); - -STBRDEF int stbr_resize_uint16_alphaweighted(const stbr_uint16* input_data, int input_w, int input_h, - stbr_uint16* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); - -STBRDEF int stbr_resize_uint32_alphaweighted(const stbr_uint32* input_data, int input_w, int input_h, - stbr_uint32* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); - -STBRDEF int stbr_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace); - - -STBRDEF int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, - stbr_uint8* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbr_filter filter, stbr_edge edge); - -STBRDEF int stbr_resize_uint16_subpixel(const stbr_uint16* input_data, int input_w, int input_h, - stbr_uint16* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbr_filter filter, stbr_edge edge); - -STBRDEF int stbr_resize_uint32_subpixel(const stbr_uint32* input_data, int input_w, int input_h, - stbr_uint32* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbr_filter filter, stbr_edge edge); - -STBRDEF int stbr_resize_float_subpixel(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbr_filter filter, stbr_edge edge); - - -STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbr_uint32 flags, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace); - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBR_INCLUDE_STB_RESAMPLE_H - -#ifdef STB_RESAMPLE_IMPLEMENTATION - -#ifndef STBR_ASSERT -#include -#define STBR_ASSERT(x) assert(x) -#endif - -#ifdef STBR_DEBUG -#define STBR_DEBUG_ASSERT STBR_ASSERT -#else -#define STBR_DEBUG_ASSERT -#endif - -// If you hit this it means I haven't done it yet. -#define STBR_UNIMPLEMENTED(x) STBR_ASSERT(!(x)) - - -// For memset -#include - -#include - -#ifndef STBR_MALLOC -#include - -#define STBR_MALLOC(x) malloc(x) -#define STBR_FREE(x) free(x) -#endif - - -#ifndef _MSC_VER -#ifdef __cplusplus -#define stbr__inline inline -#else -#define stbr__inline -#endif -#else -#define stbr__inline __forceinline -#endif - - -// should produce compiler error if size is wrong -typedef unsigned char stbr__validate_uint32[sizeof(stbr_uint32) == 4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBR_NOTUSED(v) (void)(v) -#else -#define STBR_NOTUSED(v) (void)sizeof(v) -#endif - -#define STBR_ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) - -#define STBR__MAX_UNROLLED_CHANNELS 4 - -// Kernel function centered at 0 -typedef float (stbr__kernel_fn)(float x); - -typedef struct -{ - stbr__kernel_fn* kernel; - float support; -} stbr__filter_info; - -// When upsampling, the contributors are which source pixels contribute. -// When downsampling, the contributors are which destination pixels are contributed to. -typedef struct -{ - int n0; // First contributing pixel - int n1; // Last contributing pixel -} stbr__contributors; - -typedef struct -{ - const void* input_data; - int input_w; - int input_h; - int input_stride_bytes; - - void* output_data; - int output_w; - int output_h; - int output_stride_bytes; - - float s0, t0, s1, t1; - - float horizontal_shift; // Units: output pixels - float vertical_shift; // Units: output pixels - float horizontal_scale; - float vertical_scale; - - int channels; - int alpha_channel; - stbr_uint32 flags; - stbr_type type; - stbr_filter filter; - stbr_edge edge_horizontal; - stbr_edge edge_vertical; - stbr_colorspace colorspace; - - stbr__contributors* horizontal_contributors; - float* horizontal_coefficients; - - stbr__contributors vertical_contributors; - float* vertical_coefficients; - - int decode_buffer_pixels; - float* decode_buffer; - - float* horizontal_buffer; - - int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbr__get_filter_pixel_width(filter) - int ring_buffer_first_scanline; - int ring_buffer_last_scanline; - int ring_buffer_begin_index; - float* ring_buffer; - - float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. -} stbr__info; - -static stbr__inline int stbr__min(int a, int b) -{ - return a < b ? a : b; -} - -static stbr__inline int stbr__max(int a, int b) -{ - return a > b ? a : b; -} - -static stbr__inline float stbr__saturate(float x) -{ - if (x < 0) - return 0; - - if (x > 1) - return 1; - - return x; -} - -static float stbr__srgb_uchar_to_linear_float[256] = { - 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, - 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, - 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, - 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, - 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, - 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, - 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, - 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, - 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, - 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, - 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, - 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, - 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, - 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, - 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, - 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, - 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, - 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, - 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, - 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, - 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, - 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, - 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, - 0.982251f, 0.991102f, 1.0f -}; - -static unsigned char stbr__linear_uchar_to_srgb_uchar[256] = { - 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, - 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, - 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, - 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, - 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, - 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, - 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, - 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, - 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, - 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, - 251, 251, 252, 252, 253, 253, 254, 254, 255 -}; - -float stbr__srgb_to_linear(float f) -{ - if (f <= 0.04045f) - return f / 12.92f; - else - return (float)pow((f + 0.055f) / 1.055f, 2.4f); -} - -float stbr__linear_to_srgb(float f) -{ - if (f <= 0.0031308f) - return f * 12.92f; - else - return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; -} - - - -static float stbr__filter_nearest(float x) -{ - if (x <= -0.5f) - return 0; - else if (x > 0.5f) - return 0; - else - return 1; -} - -static float stbr__filter_bilinear(float x) -{ - x = (float)fabs(x); - - if (x <= 1.0f) - return 1 - x; - else - return 0; -} - -static float stbr__filter_bicubic(float x) -{ - x = (float)fabs(x); - - if (x < 1.0f) - return 0.66666666666f + x*x*(0.5f*x - 1); - else if (x < 2.0f) - return 1.3333333333f + x*(-2 + x*(1 - 0.16666666f * x)); - - return (0.0f); -} - -static float stbr__filter_catmullrom(float x) -{ - x = (float)fabs(x); - - if (x < 1.0f) - return 1 - x*x*(2.5f - 1.5f*x); - else if (x < 2.0f) - return 2 - x*(4 + x*(0.5f*x - 2.5f)); - - return (0.0f); -} - -static float stbr__filter_mitchell(float x) -{ - x = (float)fabs(x); - - if (x < 1.0f) - return 0.8888888888f + x*x*(1.1666666666666f * x - 2.0f); - else if (x < 2.0f) - return 1.777777777777f + x*(-3.3333333333f + x*(2 - 0.3888888888888f*x)); - - return (0.0f); -} - -static stbr__filter_info stbr__filter_info_table[] = { - { NULL, 0.0f }, - { stbr__filter_nearest, 0.5f }, - { stbr__filter_bilinear, 1.0f }, - { stbr__filter_bicubic, 2.0f }, - { stbr__filter_catmullrom, 2.0f }, - { stbr__filter_mitchell, 2.0f }, -}; - -stbr__inline static int stbr__use_upsampling(float ratio) -{ - return ratio > 1; -} - -stbr__inline static int stbr__use_width_upsampling(stbr__info* stbr_info) -{ - return stbr__use_upsampling(stbr_info->horizontal_scale); -} - -stbr__inline static int stbr__use_height_upsampling(stbr__info* stbr_info) -{ - return stbr__use_upsampling(stbr_info->vertical_scale); -} - -// This is the maximum number of input samples that can affect an output sample -// with the given filter -stbr__inline static int stbr__get_filter_pixel_width(stbr_filter filter, int input_w, int output_w, float scale) -{ - STBR_ASSERT(filter != 0); - STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); - - if (stbr__use_upsampling(scale)) - return (int)ceil(stbr__filter_info_table[filter].support * 2); - else - return (int)ceil(stbr__filter_info_table[filter].support * 2 / scale); -} - -stbr__inline static int stbr__get_filter_pixel_width_horizontal(stbr__info* stbr_info) -{ - return stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale); -} - -stbr__inline static int stbr__get_filter_pixel_width_vertical(stbr__info* stbr_info) -{ - return stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h, stbr_info->vertical_scale); -} - -// This is how much to expand buffers to account for filters seeking outside -// the image boundaries. -stbr__inline static int stbr__get_filter_pixel_margin(stbr_filter filter, int input_w, int output_w, float scale) -{ - return stbr__get_filter_pixel_width(filter, input_w, output_w, scale) / 2; -} - -stbr__inline static int stbr__get_filter_pixel_margin_horizontal(stbr__info* stbr_info) -{ - return stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale) / 2; -} - -stbr__inline static int stbr__get_filter_pixel_margin_vertical(stbr__info* stbr_info) -{ - return stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_h, stbr_info->output_h, stbr_info->vertical_scale) / 2; -} - -stbr__inline static int stbr__get_horizontal_contributors_noinfo(stbr_filter filter, int input_w, int output_w, float horizontal_scale) -{ - if (stbr__use_upsampling(horizontal_scale)) - return output_w; - else - return (input_w + stbr__get_filter_pixel_margin(filter, input_w, output_w, horizontal_scale) * 2); -} - -stbr__inline static int stbr__get_horizontal_contributors(stbr__info* stbr_info) -{ - return stbr__get_horizontal_contributors_noinfo(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale); -} - -stbr__inline static int stbr__get_total_coefficients_noinfo(stbr_filter filter, int input_w, int output_w, float horizontal_scale) -{ - return stbr__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * stbr__get_filter_pixel_width(filter, input_w, output_w, horizontal_scale); -} - -stbr__inline static int stbr__get_total_coefficients(stbr__info* stbr_info) -{ - return stbr__get_total_coefficients_noinfo(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale); -} - -stbr__inline static stbr__contributors* stbr__get_contributor(stbr__info* stbr_info, int n) -{ - STBR_DEBUG_ASSERT(n >= 0 && n < stbr__get_horizontal_contributors(stbr_info)); - return &stbr_info->horizontal_contributors[n]; -} - -stbr__inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, int c) -{ - return &stbr_info->horizontal_coefficients[stbr__get_filter_pixel_width(stbr_info->filter, stbr_info->input_w, stbr_info->output_w, stbr_info->horizontal_scale)*n + c]; -} - -stbr__inline static int stbr__edge_wrap(stbr_edge edge, int n, int max) -{ - switch (edge) - { - case STBR_EDGE_CLAMP: - if (n < 0) - return 0; - - if (n >= max) - return max - 1; - - return n; - - case STBR_EDGE_REFLECT: - { - if (n < 0) - { - if (n < max) - return -n; - else - return max - 1; - } - - if (n >= max) - { - int max2 = max * 2; - if (n >= max2) - return 0; - else - return max2 - n - 1; - } - - return n; - } - - case STBR_EDGE_WRAP: - if (n >= 0) - return (n % max); - else - { - int m = (-n) % max; - - if (m != 0) - m = max - m; - - return (m); - } - - default: - STBR_UNIMPLEMENTED("Unimplemented edge type"); - return 0; - } -} - -// What input pixels contribute to this output pixel? -static void stbr__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) -{ - float out_pixel_center = (float)n + 0.5f; - float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; - float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; - - float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; - float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; - - *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; - *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); - *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); -} - -// What output pixels does this input pixel contribute to? -static void stbr__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) -{ - float in_pixel_center = (float)n + 0.5f; - float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; - float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; - - float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; - float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; - - *out_center_of_in = in_pixel_center * scale_ratio - out_shift; - *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); - *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); -} - -static void stbr__calculate_coefficients_upsample(stbr__info* stbr_info, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbr__contributors* contributor, float* coefficient_group) -{ - int i; - float total_filter = 0; - float filter_scale; - stbr_filter filter = stbr_info->filter; - - STBR_DEBUG_ASSERT(in_last_pixel - in_first_pixel <= stbr__get_filter_pixel_width_horizontal(stbr_info)); - - contributor->n0 = in_first_pixel; - contributor->n1 = in_last_pixel; - - STBR_DEBUG_ASSERT(contributor->n1 >= contributor->n0); - - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - { - float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; - total_filter += coefficient_group[i] = stbr__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center); - } - - STBR_DEBUG_ASSERT(total_filter > 0.9); - STBR_DEBUG_ASSERT(total_filter < 1.1f); // Make sure it's not way off. - - // Make sure the sum of all coefficients is 1. - filter_scale = 1 / total_filter; - - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - coefficient_group[i] *= filter_scale; -} - -static void stbr__calculate_coefficients_downsample(stbr__info* stbr_info, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbr__contributors* contributor, float* coefficient_group) -{ - int i; - stbr_filter filter = stbr_info->filter; - - STBR_DEBUG_ASSERT(out_last_pixel - out_first_pixel <= stbr__get_filter_pixel_width_horizontal(stbr_info)); - - contributor->n0 = out_first_pixel; - contributor->n1 = out_last_pixel; - - STBR_DEBUG_ASSERT(contributor->n1 >= contributor->n0); - - for (i = 0; i <= out_last_pixel - out_first_pixel; i++) - { - float in_pixel_center = (float)(i + out_first_pixel) + 0.5f; - coefficient_group[i] = stbr__filter_info_table[filter].kernel((out_center_of_in - in_pixel_center)/scale_ratio); - } -} - -#ifdef STBR_DEBUG -static void stbr__check_downsample_coefficients(stbr__info* stbr_info) -{ - int i; - for (i = 0; i < stbr_info->output_w; i++) - { - float total = 0; - int j; - for (j = 0; j < stbr__get_horizontal_contributors(stbr_info); j++) - { - if (i >= stbr_info->horizontal_contributors[j].n0 && i <= stbr_info->horizontal_contributors[j].n1) - { - float coefficient = *stbr__get_coefficient(stbr_info, j, i - stbr_info->horizontal_contributors[j].n0); - total += coefficient; - } - else if (i < stbr_info->horizontal_contributors[j].n0) - break; - } - - STBR_DEBUG_ASSERT(total > 0.9f); - STBR_DEBUG_ASSERT(total <= 1.0f + 1.0f / (pow(2.0f, 8.0f * stbr__type_size[stbr_info->type]) - 1)); - } -} -#endif - -// Each scan line uses the same kernel values so we should calculate the kernel -// values once and then we can use them for every scan line. -static void stbr__calculate_horizontal_filters(stbr__info* stbr_info) -{ - int n; - float scale_ratio = stbr_info->horizontal_scale; - - int total_contributors = stbr__get_horizontal_contributors(stbr_info); - - if (stbr__use_width_upsampling(stbr_info)) - { - float out_pixels_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; - - // Looping through out pixels - for (n = 0; n < total_contributors; n++) - { - float in_center_of_out; // Center of the current out pixel in the in pixel space - int in_first_pixel, in_last_pixel; - - stbr__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, stbr_info->horizontal_shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); - - stbr__calculate_coefficients_upsample(stbr_info, in_first_pixel, in_last_pixel, in_center_of_out, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); - } - } - else - { - float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; - - // Looping through in pixels - for (n = 0; n < total_contributors; n++) - { - float out_center_of_in; // Center of the current out pixel in the in pixel space - int out_first_pixel, out_last_pixel; - int n_adjusted = n - stbr__get_filter_pixel_margin_horizontal(stbr_info); - - stbr__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, stbr_info->horizontal_shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); - - stbr__calculate_coefficients_downsample(stbr_info, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbr__get_contributor(stbr_info, n), stbr__get_coefficient(stbr_info, n, 0)); - } - -#ifdef STBR_DEBUG - stbr__check_downsample_coefficients(stbr_info); -#endif - } -} - -static float* stbr__get_decode_buffer(stbr__info* stbr_info) -{ - // The 0 index of the decode buffer starts after the margin. This makes - // it okay to use negative indexes on the decode buffer. - return &stbr_info->decode_buffer[stbr__get_filter_pixel_margin_horizontal(stbr_info) * stbr_info->channels]; -} - -#define STBR__DECODE(type, colorspace) ((type) * (STBR_MAX_COLORSPACES) + (colorspace)) - -static void stbr__decode_scanline(stbr__info* stbr_info, int n) -{ - int x, c; - int channels = stbr_info->channels; - int alpha_channel = stbr_info->alpha_channel; - int type = stbr_info->type; - int colorspace = stbr_info->colorspace; - int input_w = stbr_info->input_w; - int input_stride = stbr_info->input_stride_bytes / stbr__type_size[stbr_info->type]; - const void* input_data = stbr_info->input_data; - float* decode_buffer = stbr__get_decode_buffer(stbr_info); - stbr_edge edge_horizontal = stbr_info->edge_horizontal; - stbr_edge edge_vertical = stbr_info->edge_vertical; - int in_buffer_row_index = stbr__edge_wrap(edge_vertical, n, stbr_info->input_h) * input_stride; - int max_x = input_w + stbr__get_filter_pixel_margin_horizontal(stbr_info); - int decode = STBR__DECODE(type, colorspace); - - for (x = -stbr__get_filter_pixel_margin_horizontal(stbr_info); x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = in_buffer_row_index + stbr__edge_wrap(edge_horizontal, x, input_w) * channels; - - switch (decode) - { - case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255; - - break; - - case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbr__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; - - if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255; - - break; - - case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535; - break; - - case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbr__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); - - if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535; - - break; - - case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295); - break; - - case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbr__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); - - if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) - decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295); - - break; - - case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; - break; - - case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbr__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); - - if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) - decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; - - break; - - default: - STBR_UNIMPLEMENTED("Unknown type/colorspace/channels combination."); - break; - } - - if (stbr_info->flags&STBR_FLAG_NONPREMUL_ALPHA) - { - float alpha = decode_buffer[decode_pixel_index + alpha_channel]; - for (c = 0; c < channels; c++) - { - if (c == alpha_channel) - continue; - - decode_buffer[decode_pixel_index + c] *= alpha; - } - } - } -} - -static float* stbr__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) -{ - return &ring_buffer[index * ring_buffer_length]; -} - -static float* stbr__add_empty_ring_buffer_entry(stbr__info* stbr_info, int n) -{ - int ring_buffer_index; - float* ring_buffer; - - if (stbr_info->ring_buffer_begin_index < 0) - { - ring_buffer_index = stbr_info->ring_buffer_begin_index = 0; - stbr_info->ring_buffer_first_scanline = n; - } - else - { - ring_buffer_index = (stbr_info->ring_buffer_begin_index + (stbr_info->ring_buffer_last_scanline - stbr_info->ring_buffer_first_scanline) + 1) % stbr__get_filter_pixel_width_vertical(stbr_info); - STBR_DEBUG_ASSERT(ring_buffer_index != stbr_info->ring_buffer_begin_index); - } - - ring_buffer = stbr__get_ring_buffer_entry(stbr_info->ring_buffer, ring_buffer_index, stbr_info->ring_buffer_length_bytes / sizeof(float)); - memset(ring_buffer, 0, stbr_info->ring_buffer_length_bytes); - - stbr_info->ring_buffer_last_scanline = n; - - return ring_buffer; -} - - -static void stbr__resample_horizontal_upsample(stbr__info* stbr_info, int n, float* output_buffer) -{ - int x, k; - int output_w = stbr_info->output_w; - int kernel_pixel_width = stbr__get_filter_pixel_width_horizontal(stbr_info); - int channels = stbr_info->channels; - float* decode_buffer = stbr__get_decode_buffer(stbr_info); - stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; - float* horizontal_coefficients = stbr_info->horizontal_coefficients; - - for (x = 0; x < output_w; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int out_pixel_index = x * channels; - int coefficient_group_index = x * kernel_pixel_width; - int coefficient_counter = 0; - - STBR_DEBUG_ASSERT(n1 >= n0); - STBR_DEBUG_ASSERT(n0 >= -stbr__get_filter_pixel_margin_horizontal(stbr_info)); - STBR_DEBUG_ASSERT(n1 >= -stbr__get_filter_pixel_margin_horizontal(stbr_info)); - STBR_DEBUG_ASSERT(n0 < stbr_info->input_w + stbr__get_filter_pixel_margin_horizontal(stbr_info)); - STBR_DEBUG_ASSERT(n1 < stbr_info->input_w + stbr__get_filter_pixel_margin_horizontal(stbr_info)); - - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_group_index + (coefficient_counter++); - int in_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_index]; - - int c; - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - } -} - -static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, float* output_buffer) -{ - int x, k; - int input_w = stbr_info->input_w; - int output_w = stbr_info->output_w; - int kernel_pixel_width = stbr__get_filter_pixel_width_horizontal(stbr_info); - int channels = stbr_info->channels; - float* decode_buffer = stbr__get_decode_buffer(stbr_info); - stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors; - float* horizontal_coefficients = stbr_info->horizontal_coefficients; - int filter_pixel_margin = stbr__get_filter_pixel_margin_horizontal(stbr_info); - int max_x = input_w + filter_pixel_margin * 2; - - STBR_DEBUG_ASSERT(!stbr__use_width_upsampling(stbr_info)); - - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * channels; - int max_n = stbr__min(n1, output_w-1); - int coefficient_group = x*kernel_pixel_width; - - STBR_DEBUG_ASSERT(n1 >= n0); - - // Using min and max to avoid writing into invalid pixels. - for (k = stbr__max(n0, 0); k <= max_n; k++) - { - int coefficient_index = (k - n0) + coefficient_group; - int out_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_index]; - - int c; - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - } -} - -static void stbr__decode_and_resample_upsample(stbr__info* stbr_info, int n) -{ - // Decode the nth scanline from the source image into the decode buffer. - stbr__decode_scanline(stbr_info, n); - - // Now resample it into the ring buffer. - if (stbr__use_width_upsampling(stbr_info)) - stbr__resample_horizontal_upsample(stbr_info, n, stbr__add_empty_ring_buffer_entry(stbr_info, n)); - else - stbr__resample_horizontal_downsample(stbr_info, n, stbr__add_empty_ring_buffer_entry(stbr_info, n)); - - // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. -} - -static void stbr__decode_and_resample_downsample(stbr__info* stbr_info, int n) -{ - // Decode the nth scanline from the source image into the decode buffer. - stbr__decode_scanline(stbr_info, n); - - memset(stbr_info->horizontal_buffer, 0, stbr_info->output_w * stbr_info->channels * sizeof(float)); - - // Now resample it into the horizontal buffer. - if (stbr__use_width_upsampling(stbr_info)) - stbr__resample_horizontal_upsample(stbr_info, n, stbr_info->horizontal_buffer); - else - stbr__resample_horizontal_downsample(stbr_info, n, stbr_info->horizontal_buffer); - - // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. -} - -// Get the specified scan line from the ring buffer. -static float* stbr__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_size, int ring_buffer_length) -{ - int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_size; - return stbr__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); -} - - -static stbr__inline void stbr__encode_pixel(stbr__info* stbr_info, void* output_buffer, int output_pixel_index, float* encode_buffer, int encode_pixel_index, int channels, int alpha_channel, int decode) -{ - int n; - float divide_alpha = 1; - - if (stbr_info->flags&STBR_FLAG_NONPREMUL_ALPHA) { - float alpha = encode_buffer[encode_pixel_index + alpha_channel]; - float reciprocal_alpha = alpha ? 1.0f / alpha : 0; - for (n = 0; n < channels; n++) - if (n != alpha_channel) - encode_buffer[encode_pixel_index + n] *= reciprocal_alpha; - } - - switch (decode) - { - case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_LINEAR): - for (n = 0; n < channels; n++) - ((unsigned char*)output_buffer)[output_pixel_index + n] = (unsigned char)(stbr__saturate(encode_buffer[encode_pixel_index + n]) * 255); - break; - - case STBR__DECODE(STBR_TYPE_UINT8, STBR_COLORSPACE_SRGB): - for (n = 0; n < channels; n++) - ((unsigned char*)output_buffer)[output_pixel_index + n] = stbr__linear_uchar_to_srgb_uchar[(unsigned char)(stbr__saturate(encode_buffer[encode_pixel_index + n]) * 255)]; - - if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) - ((unsigned char*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbr__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); - - break; - - case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR): - for (n = 0; n < channels; n++) - ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbr__saturate(encode_buffer[encode_pixel_index + n]) * 65535); - break; - - case STBR__DECODE(STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB): - for (n = 0; n < channels; n++) - ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_pixel_index + n])) * 65535); - - if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) - ((unsigned short*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbr__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); - - break; - - case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR): - for (n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbr__saturate(encode_buffer[encode_pixel_index + n])) * 4294967295); - break; - - case STBR__DECODE(STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB): - for (n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbr__linear_to_srgb(stbr__saturate(encode_buffer[encode_pixel_index + n]))) * 4294967295); - - if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) - ((unsigned int*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned int)(((double)stbr__saturate(encode_buffer[encode_pixel_index + alpha_channel])) * 4294967295); - - break; - - case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR): - for (n = 0; n < channels; n++) - ((float*)output_buffer)[output_pixel_index + n] = encode_buffer[encode_pixel_index + n]; - break; - - case STBR__DECODE(STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB): - for (n = 0; n < channels; n++) - ((float*)output_buffer)[output_pixel_index + n] = stbr__linear_to_srgb(encode_buffer[encode_pixel_index + n]); - - if (stbr_info->flags&STBR_FLAG_FORCE_LINEAR_ALPHA) - ((float*)output_buffer)[output_pixel_index + alpha_channel] = encode_buffer[encode_pixel_index + alpha_channel]; - - break; - - default: - STBR_UNIMPLEMENTED("Unknown type/colorspace/channels combination."); - break; - } -} - -static void stbr__resample_vertical_upsample(stbr__info* stbr_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) -{ - int x, k; - int output_w = stbr_info->output_w; - stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; - float* vertical_coefficients = stbr_info->vertical_coefficients; - int channels = stbr_info->channels; - int alpha_channel = stbr_info->alpha_channel; - int type = stbr_info->type; - int colorspace = stbr_info->colorspace; - int kernel_pixel_width = stbr__get_filter_pixel_width_vertical(stbr_info); - void* output_data = stbr_info->output_data; - float* encode_buffer = stbr_info->encode_buffer; - int decode = STBR__DECODE(type, colorspace); - - float* ring_buffer = stbr_info->ring_buffer; - int ring_buffer_begin_index = stbr_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbr_info->ring_buffer_first_scanline; - int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; - int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); - - int n0,n1, output_row_index; - - stbr__calculate_coefficients_upsample(stbr_info, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); - - n0 = vertical_contributors->n0; - n1 = vertical_contributors->n1; - - output_row_index = n * stbr_info->output_stride_bytes / stbr__type_size[type]; - - STBR_DEBUG_ASSERT(stbr__use_height_upsampling(stbr_info)); - STBR_DEBUG_ASSERT(n0 >= in_first_scanline); - STBR_DEBUG_ASSERT(n1 <= in_last_scanline); - - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * channels; - int out_pixel_index = output_row_index + x * channels; - int coefficient_counter = 0; - - STBR_DEBUG_ASSERT(n1 >= n0); - - memset(encode_buffer, 0, sizeof(float) * channels); - - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_index]; - - int c; - for (c = 0; c < channels; c++) - encode_buffer[c] += ring_buffer_entry[in_pixel_index + c] * coefficient; - } - - stbr__encode_pixel(stbr_info, output_data, out_pixel_index, encode_buffer, 0, channels, alpha_channel, decode); - } -} - -static void stbr__resample_vertical_downsample(stbr__info* stbr_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) -{ - int x, k; - int output_w = stbr_info->output_w; - int output_h = stbr_info->output_h; - stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; - float* vertical_coefficients = stbr_info->vertical_coefficients; - int channels = stbr_info->channels; - int kernel_pixel_width = stbr__get_filter_pixel_width_vertical(stbr_info); - void* output_data = stbr_info->output_data; - float* horizontal_buffer = stbr_info->horizontal_buffer; - - float* ring_buffer = stbr_info->ring_buffer; - int ring_buffer_begin_index = stbr_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbr_info->ring_buffer_first_scanline; - int ring_buffer_last_scanline = stbr_info->ring_buffer_last_scanline; - int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); - int n0,n1,max_n; - - stbr__calculate_coefficients_downsample(stbr_info, stbr_info->vertical_scale, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); - - n0 = vertical_contributors->n0; - n1 = vertical_contributors->n1; - max_n = stbr__min(n1, output_h - 1); - - STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); - STBR_DEBUG_ASSERT(n0 >= in_first_scanline); - STBR_DEBUG_ASSERT(n1 <= in_last_scanline); - STBR_DEBUG_ASSERT(n1 >= n0); - - // Using min and max to avoid writing into ring buffers that will be thrown out. - for (k = stbr__max(n0, 0); k <= max_n; k++) - { - int coefficient_index = k - n0; - - float* ring_buffer_entry = stbr__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_index]; - - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * channels; - - int c; - for (c = 0; c < channels; c++) - ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; - } - } -} - -static void stbr__buffer_loop_upsample(stbr__info* stbr_info) -{ - int y; - float scale_ratio = stbr_info->vertical_scale; - float out_scanlines_radius = stbr__filter_info_table[stbr_info->filter].support * scale_ratio; - - STBR_DEBUG_ASSERT(stbr__use_height_upsampling(stbr_info)); - - for (y = 0; y < stbr_info->output_h; y++) - { - float in_center_of_out = 0; // Center of the current out scanline in the in scanline space - int in_first_scanline = 0, in_last_scanline = 0; - - stbr__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbr_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); - - STBR_DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbr__get_filter_pixel_width_vertical(stbr_info)); - - if (stbr_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (in_first_scanline > stbr_info->ring_buffer_first_scanline) - { - if (stbr_info->ring_buffer_first_scanline == stbr_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbr_info->ring_buffer_begin_index = -1; - stbr_info->ring_buffer_first_scanline = 0; - stbr_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbr_info->ring_buffer_first_scanline++; - stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_pixel_width_horizontal(stbr_info); - } - } - } - - // Load in new ones. - if (stbr_info->ring_buffer_begin_index < 0) - stbr__decode_and_resample_upsample(stbr_info, in_first_scanline); - - while (in_last_scanline > stbr_info->ring_buffer_last_scanline) - stbr__decode_and_resample_upsample(stbr_info, stbr_info->ring_buffer_last_scanline + 1); - - // Now all buffers should be ready to write a row of vertical sampling. - stbr__resample_vertical_upsample(stbr_info, y, in_first_scanline, in_last_scanline, in_center_of_out); - } -} - -static void stbr__empty_ring_buffer(stbr__info* stbr_info, int first_necessary_scanline) -{ - int output_stride = stbr_info->output_stride_bytes / stbr__type_size[stbr_info->type]; - int channels = stbr_info->channels; - int alpha_channel = stbr_info->alpha_channel; - int type = stbr_info->type; - int colorspace = stbr_info->colorspace; - int output_w = stbr_info->output_w; - void* output_data = stbr_info->output_data; - int decode = STBR__DECODE(type, colorspace); - - float* ring_buffer = stbr_info->ring_buffer; - int ring_buffer_length = stbr_info->ring_buffer_length_bytes/sizeof(float); - - if (stbr_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (first_necessary_scanline > stbr_info->ring_buffer_first_scanline) - { - if (stbr_info->ring_buffer_first_scanline >= 0 && stbr_info->ring_buffer_first_scanline < stbr_info->output_h) - { - int x; - int output_row = stbr_info->ring_buffer_first_scanline * output_stride; - float* ring_buffer_entry = stbr__get_ring_buffer_entry(ring_buffer, stbr_info->ring_buffer_begin_index, ring_buffer_length); - - for (x = 0; x < output_w; x++) - { - int pixel_index = x * channels; - int ring_pixel_index = pixel_index; - int output_pixel_index = output_row + pixel_index; - - stbr__encode_pixel(stbr_info, output_data, output_pixel_index, ring_buffer_entry, ring_pixel_index, channels, alpha_channel, decode); - } - } - - if (stbr_info->ring_buffer_first_scanline == stbr_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbr_info->ring_buffer_begin_index = -1; - stbr_info->ring_buffer_first_scanline = 0; - stbr_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbr_info->ring_buffer_first_scanline++; - stbr_info->ring_buffer_begin_index = (stbr_info->ring_buffer_begin_index + 1) % stbr__get_filter_pixel_width_vertical(stbr_info); - } - } - } -} - -static void stbr__buffer_loop_downsample(stbr__info* stbr_info) -{ - int y; - float scale_ratio = stbr_info->vertical_scale; - int output_h = stbr_info->output_h; - float in_pixels_radius = stbr__filter_info_table[stbr_info->filter].support / scale_ratio; - int max_y = stbr_info->input_h + stbr__get_filter_pixel_margin_vertical(stbr_info); - - STBR_DEBUG_ASSERT(!stbr__use_height_upsampling(stbr_info)); - - for (y = -stbr__get_filter_pixel_margin_vertical(stbr_info); y < max_y; y++) - { - float out_center_of_in; // Center of the current out scanline in the in scanline space - int out_first_scanline, out_last_scanline; - - stbr__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbr_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); - - STBR_DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbr__get_filter_pixel_width_vertical(stbr_info)); - - if (out_last_scanline < 0 || out_first_scanline >= output_h) - continue; - - stbr__empty_ring_buffer(stbr_info, out_first_scanline); - - stbr__decode_and_resample_downsample(stbr_info, y); - - // Load in new ones. - if (stbr_info->ring_buffer_begin_index < 0) - stbr__add_empty_ring_buffer_entry(stbr_info, out_first_scanline); - - while (out_last_scanline > stbr_info->ring_buffer_last_scanline) - stbr__add_empty_ring_buffer_entry(stbr_info, stbr_info->ring_buffer_last_scanline + 1); - - // Now the horizontal buffer is ready to write to all ring buffer rows. - stbr__resample_vertical_downsample(stbr_info, y, out_first_scanline, out_last_scanline, out_center_of_in); - } - - stbr__empty_ring_buffer(stbr_info, stbr_info->output_h); -} - -static stbr__inline stbr_size_t stbr__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbr_filter filter) -{ - float horizontal_scale = ((float)output_w / input_w) / (s1 - s0); - float vertical_scale = ((float)output_h / input_h) / (t1 - t0); - - int pixel_margin = stbr__get_filter_pixel_margin(filter, input_w, output_w, horizontal_scale); - int filter_height = stbr__get_filter_pixel_width(filter, input_h, output_h, vertical_scale); - - int info_size = sizeof(stbr__info); - int contributors_size = stbr__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(stbr__contributors); - int horizontal_coefficients_size = stbr__get_total_coefficients_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(float); - int vertical_coefficients_size = filter_height * sizeof(float); - int decode_buffer_size = (input_w + pixel_margin*2) * channels * sizeof(float); - int horizontal_buffer_size = output_w * channels * sizeof(float); - int ring_buffer_size = output_w * channels * filter_height * sizeof(float); - int encode_buffer_size = channels * sizeof(float); - - STBR_ASSERT(filter != 0); - STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); // this now happens too late - - if (stbr__use_upsampling(horizontal_scale)) - // The horizontal buffer is for when we're downsampling the height and we - // can't output the result of sampling the decode buffer directly into the - // ring buffers. - horizontal_buffer_size = 0; - else - // The encode buffer is to retain precision in the height upsampling method - // and isn't used when height downsampling. - encode_buffer_size = 0; - - return info_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size; -} - -static int stbr__resize_allocated(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbr_uint32 flags, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace, - void* tempmem, stbr_size_t tempmem_size_in_bytes) -{ - stbr__info* stbr_info = (stbr__info*)tempmem; - - stbr_size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); - - int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w * stbr__type_size[type]; - int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w * stbr__type_size[type]; - -#ifdef STBR_DEBUG_OVERWRITE_TEST -#define OVERWRITE_ARRAY_SIZE 8 - unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; - - stbr_size_t begin_forbidden = width_stride_output * (output_h - 1) + output_w * channels * stbr__type_size[type]; - memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); -#endif - - STBR_ASSERT(filter != 0); - STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); - - if (!filter || filter >= STBR_ARRAY_SIZE(stbr__filter_info_table)) - return 0; - - STBR_ASSERT(s1 > s0); - STBR_ASSERT(t1 > t0); - - if (s1 <= s0 || t1 <= t0) - return 0; - - STBR_ASSERT(s1 <= 1 && s0 >= 0 && t1 <= 1 && t0 >= 0); - - if (s1 > 1 || s0 < 0 || t1 > 1 || t0 < 0) - return 0; - - if (flags&(STBR_FLAG_FORCE_LINEAR_ALPHA | STBR_FLAG_NONPREMUL_ALPHA)) - STBR_ASSERT(alpha_channel >= 0 && alpha_channel < channels); - - if (alpha_channel >= channels) - return 0; - - STBR_ASSERT(tempmem); - - if (!tempmem) - return 0; - - STBR_ASSERT(tempmem_size_in_bytes >= memory_required); - - if (tempmem_size_in_bytes < memory_required) - return 0; - - memset(tempmem, 0, tempmem_size_in_bytes); - - stbr_info->input_data = input_data; - stbr_info->input_w = input_w; - stbr_info->input_h = input_h; - stbr_info->input_stride_bytes = width_stride_input; - - stbr_info->output_data = output_data; - stbr_info->output_w = output_w; - stbr_info->output_h = output_h; - stbr_info->output_stride_bytes = width_stride_output; - - stbr_info->s0 = s0; - stbr_info->t0 = t0; - stbr_info->s1 = s1; - stbr_info->t1 = t1; - - stbr_info->horizontal_scale = ((float)output_w / input_w) / (s1 - s0); - stbr_info->vertical_scale = ((float)output_h / input_h) / (t1 - t0); - - stbr_info->horizontal_shift = s0 * input_w / (s1 - s0); - stbr_info->vertical_shift = t0 * input_h / (t1 - t0); - - stbr_info->channels = channels; - stbr_info->alpha_channel = alpha_channel; - stbr_info->flags = flags; - stbr_info->type = type; - stbr_info->filter = filter; - stbr_info->edge_horizontal = edge_horizontal; - stbr_info->edge_vertical = edge_vertical; - stbr_info->colorspace = colorspace; - - stbr_info->ring_buffer_length_bytes = output_w * channels * sizeof(float); - stbr_info->decode_buffer_pixels = input_w + stbr__get_filter_pixel_margin_horizontal(stbr_info) * 2; - -#define STBR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) - - stbr_info->horizontal_contributors = STBR__NEXT_MEMPTR(stbr_info, sizeof(stbr__info), stbr__contributors); - stbr_info->horizontal_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_contributors, stbr__get_horizontal_contributors(stbr_info) * sizeof(stbr__contributors), float); - stbr_info->vertical_coefficients = STBR__NEXT_MEMPTR(stbr_info->horizontal_coefficients, stbr__get_total_coefficients(stbr_info) * sizeof(float), float); - stbr_info->decode_buffer = STBR__NEXT_MEMPTR(stbr_info->vertical_coefficients, stbr__get_filter_pixel_width_vertical(stbr_info) * sizeof(float), float); - - if (stbr__use_height_upsampling(stbr_info)) - { - stbr_info->horizontal_buffer = NULL; - stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, stbr_info->decode_buffer_pixels * channels * sizeof(float), float); - stbr_info->encode_buffer = STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_pixel_width_horizontal(stbr_info), float); - - STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->encode_buffer, stbr_info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - else - { - stbr_info->horizontal_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, stbr_info->decode_buffer_pixels * channels * sizeof(float), float); - stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->horizontal_buffer, output_w * channels * sizeof(float), float); - stbr_info->encode_buffer = NULL; - - STBR_DEBUG_ASSERT((size_t)STBR__NEXT_MEMPTR(stbr_info->ring_buffer, stbr_info->ring_buffer_length_bytes * stbr__get_filter_pixel_width_vertical(stbr_info), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - -#undef STBR__NEXT_MEMPTR - - // This signals that the ring buffer is empty - stbr_info->ring_buffer_begin_index = -1; - - stbr__calculate_horizontal_filters(stbr_info); - - if (stbr__use_height_upsampling(stbr_info)) - stbr__buffer_loop_upsample(stbr_info); - else - stbr__buffer_loop_downsample(stbr_info); - -#ifdef STBR_DEBUG_OVERWRITE_TEST - STBR_DEBUG_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBR_DEBUG_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); - STBR_DEBUG_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBR_DEBUG_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); -#endif - - return 1; -} - - -STBRDEF int stbr_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbr_uint32 flags, stbr_type type, stbr_filter filter, stbr_edge edge_horizontal, stbr_edge edge_vertical, stbr_colorspace colorspace) -{ - int result; - size_t memory_required = stbr__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); - void* extra_memory = STBR_MALLOC(memory_required); - - if (!extra_memory) - return 0; - - result = stbr__resize_allocated(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, alpha_channel, flags, type, filter, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); - - STBR_FREE(extra_memory); - - return result; -} - -STBRDEF int stbr_resize_uint8_srgb(const stbr_uint8* input_data, int input_w, int input_h, - stbr_uint8* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge) -{ - return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB); -} - -STBRDEF int stbr_resize_uint16_srgb(const stbr_uint16* input_data, int input_w, int input_h, - stbr_uint16* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge) -{ - return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB); -} - -STBRDEF int stbr_resize_uint32_srgb(const stbr_uint32* input_data, int input_w, int input_h, - stbr_uint32* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge) -{ - return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB); -} - -STBRDEF int stbr_resize_float_srgb(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - int channels, stbr_filter filter, stbr_edge edge) -{ - return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB); -} - -STBRDEF int stbr_resize_uint8_alphaweighted(const stbr_uint8* input_data, int input_w, int input_h, - stbr_uint8* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) -{ - return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT8, filter, edge, edge, colorspace); -} - -STBRDEF int stbr_resize_uint16_alphaweighted(const stbr_uint16* input_data, int input_w, int input_h, - stbr_uint16* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) -{ - return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT16, filter, edge, edge, colorspace); -} - -STBRDEF int stbr_resize_uint32_alphaweighted(const stbr_uint32* input_data, int input_w, int input_h, - stbr_uint32* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) -{ - return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT32, filter, edge, edge, colorspace); -} - -STBRDEF int stbr_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace) -{ - return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_FLOAT, filter, edge, edge, colorspace); -} - -STBRDEF int stbr_resize_uint8_subpixel(const stbr_uint8* input_data, int input_w, int input_h, - stbr_uint8* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbr_filter filter, stbr_edge edge) -{ - return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT8, filter, edge, edge, STBR_COLORSPACE_SRGB); -} - -STBRDEF int stbr_resize_uint16_subpixel(const stbr_uint16* input_data, int input_w, int input_h, - stbr_uint16* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbr_filter filter, stbr_edge edge) -{ - return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT16, filter, edge, edge, STBR_COLORSPACE_SRGB); -} - -STBRDEF int stbr_resize_uint32_subpixel(const stbr_uint32* input_data, int input_w, int input_h, - stbr_uint32* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbr_filter filter, stbr_edge edge) -{ - return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_UINT32, filter, edge, edge, STBR_COLORSPACE_SRGB); -} - -STBRDEF int stbr_resize_float_subpixel(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbr_filter filter, stbr_edge edge) -{ - return stbr_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBR_TYPE_FLOAT, filter, edge, edge, STBR_COLORSPACE_SRGB); -} - -#endif // STB_RESAMPLE_IMPLEMENTATION - -/* -revision history: - 0.50 (2014-07-29) - first released version -*/ diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 2a87eca..856351b 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -1,16 +1,16 @@ #if defined(_WIN32) && _MSC_VER > 1200 -#define STBR_ASSERT(x) \ +#define STBIR_ASSERT(x) \ if (!(x)) { \ __debugbreak(); \ } else #else #include -#define STBR_ASSERT(x) assert(x) +#define STBIR_ASSERT(x) assert(x) #endif -#define STB_RESAMPLE_IMPLEMENTATION -#define STB_RESAMPLE_STATIC -#include "stb_resample.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#define STB_IMAGE_RESIZE_STATIC +#include "stb_image_resize.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" @@ -118,8 +118,8 @@ int main(int argc, char** argv) // Cut out the outside 64 pixels all around to test the stride. int border = 64; - STBR_ASSERT(in_w + border <= w); - STBR_ASSERT(in_h + border <= h); + STBIR_ASSERT(in_w + border <= w); + STBIR_ASSERT(in_h + border <= h); #ifdef PERF_TEST struct timeb initial_time_millis, final_time_millis; @@ -129,7 +129,7 @@ int main(int argc, char** argv) { ftime(&initial_time_millis); for (int i = 0; i < 100; i++) - stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, 0, 0, 1, 1, n, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbir_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, 0, 0, 1, 1, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); ftime(&final_time_millis); long lapsed_ms = (long)(final_time_millis.time - initial_time_millis.time) * 1000 + (final_time_millis.millitm - initial_time_millis.millitm); printf("Resample: %dms\n", lapsed_ms); @@ -141,7 +141,7 @@ int main(int argc, char** argv) printf("Average: %dms\n", average); #else - stbr_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbir_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); #endif stbi_image_free(input_data); @@ -153,7 +153,7 @@ int main(int argc, char** argv) return 0; } -void resize_image(const char* filename, float width_percent, float height_percent, stbr_filter filter, stbr_edge edge, stbr_colorspace colorspace, const char* output_filename) +void resize_image(const char* filename, float width_percent, float height_percent, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace, const char* output_filename) { int w, h, n; @@ -169,7 +169,7 @@ void resize_image(const char* filename, float width_percent, float height_percen unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, -1, 0, STBR_TYPE_UINT8, filter, edge, edge, colorspace); + stbir_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, -1, 0, STBIR_TYPE_UINT8, filter, edge, edge, colorspace); stbi_image_free(input_data); @@ -187,7 +187,7 @@ void convert_image(const F* input, T* output, int length) } template -void test_format(const char* file, float width_percent, float height_percent, stbr_type type, stbr_colorspace colorspace) +void test_format(const char* file, float width_percent, float height_percent, stbir_type type, stbir_colorspace colorspace) { int w, h, n; unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); @@ -200,7 +200,7 @@ void test_format(const char* file, float width_percent, float height_percent, st T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, colorspace); + stbir_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -228,7 +228,7 @@ void convert_image_float(const float* input, unsigned char* output, int length) output[i] = (unsigned char)(input[i] * 255); } -void test_float(const char* file, float width_percent, float height_percent, stbr_type type, stbr_colorspace colorspace) +void test_float(const char* file, float width_percent, float height_percent, stbir_type type, stbir_colorspace colorspace) { int w, h, n; unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); @@ -241,7 +241,7 @@ void test_float(const char* file, float width_percent, float height_percent, stb float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); - stbr_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, 0, type, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, colorspace); + stbir_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -273,12 +273,12 @@ void test_channels(const char* file, float width_percent, float height_percent, int output_position = i * channels; for (int c = 0; c < channels; c++) - channels_data[output_position + c] = input_data[input_position + stbr__min(c, n)]; + channels_data[output_position + c] = input_data[input_position + stbir__min(c, n)]; } unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * channels * sizeof(unsigned char)); - stbr_resize_uint8_srgb(channels_data, w, h, output_data, new_w, new_h, channels, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); + stbir_resize_uint8_srgb(channels_data, w, h, output_data, new_w, new_h, channels, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP); free(channels_data); stbi_image_free(input_data); @@ -303,7 +303,7 @@ void test_subpixel(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbir_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); stbi_image_free(input_data); @@ -334,13 +334,13 @@ void test_premul(const char* file) unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBR_FLAG_NONPREMUL_ALPHA, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbir_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); char output[200]; sprintf(output, "test-output/premul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); - stbr_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbir_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); sprintf(output, "test-output/nopremul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); @@ -362,20 +362,20 @@ void test_subpixel_1() unsigned char output_data[16 * 16]; - stbr_resize_arbitrary(image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbir_resize_arbitrary(image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); unsigned char output_left[8 * 16]; unsigned char output_right[8 * 16]; - stbr_resize_arbitrary(image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); - stbr_resize_arbitrary(image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbir_resize_arbitrary(image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); for (int x = 0; x < 8; x++) { for (int y = 0; y < 16; y++) { - STBR_ASSERT(output_data[y * 16 + x] == output_left[y * 8 + x]); - STBR_ASSERT(output_data[y * 16 + x + 8] == output_right[y * 8 + x]); + STBIR_ASSERT(output_data[y * 16 + x] == output_left[y * 8 + x]); + STBIR_ASSERT(output_data[y * 16 + x + 8] == output_right[y * 8 + x]); } } } @@ -407,13 +407,13 @@ void test_subpixel_2() unsigned char output_data_1[16 * 16]; unsigned char output_data_2[16 * 16]; - stbr_resize_arbitrary(image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_WRAP, STBR_EDGE_WRAP, STBR_COLORSPACE_SRGB); - stbr_resize_arbitrary(large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB); + stbir_resize_arbitrary(image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); {for (int x = 0; x < 16; x++) { for (int y = 0; y < 16; y++) - STBR_ASSERT(output_data_1[y * 16 + x] == output_data_2[y * 16 + x]); + STBIR_ASSERT(output_data_1[y * 16 + x] == output_data_2[y * 16 + x]); }} } @@ -430,13 +430,13 @@ void test_subpixel_3() unsigned char output_data_1[32 * 32]; unsigned char output_data_2[32 * 32]; - stbr_resize_uint8_subpixel(image, 8, 8, output_data_1, 32, 32, 0, 0, 1, 1, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); - stbr_resize_uint8_srgb(image, 8, 8, output_data_2, 32, 32, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP); + stbir_resize_uint8_subpixel(image, 8, 8, output_data_1, 32, 32, 0, 0, 1, 1, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP); + stbir_resize_uint8_srgb(image, 8, 8, output_data_2, 32, 32, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP); for (int x = 0; x < 32; x++) { for (int y = 0; y < 32; y++) - STBR_ASSERT(output_data_1[y * 32 + x] == output_data_2[y * 32 + x]); + STBIR_ASSERT(output_data_1[y * 32 + x] == output_data_2[y * 32 + x]); } } @@ -452,8 +452,8 @@ void test_subpixel_4() unsigned char output[8 * 8]; - stbr_resize_arbitrary(image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, 1, -1, 0, STBR_TYPE_UINT8, STBR_FILTER_BILINEAR, STBR_EDGE_CLAMP, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR); - STBR_ASSERT(memcmp(image, output, 8 * 8) == 0); + stbir_resize_arbitrary(image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); + STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); } void test_suite() @@ -491,82 +491,82 @@ void test_suite() test_channels("barbara.png", 2, 2, 4); // Edge behavior tests - resize_image("hgradient.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR, "test-output/hgradient-clamp.png"); - resize_image("hgradient.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_WRAP, STBR_COLORSPACE_LINEAR, "test-output/hgradient-wrap.png"); + resize_image("hgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR, "test-output/hgradient-clamp.png"); + resize_image("hgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/hgradient-wrap.png"); - resize_image("vgradient.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR, "test-output/vgradient-clamp.png"); - resize_image("vgradient.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_WRAP, STBR_COLORSPACE_LINEAR, "test-output/vgradient-wrap.png"); + resize_image("vgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR, "test-output/vgradient-clamp.png"); + resize_image("vgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/vgradient-wrap.png"); - resize_image("1px-border.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, STBR_COLORSPACE_LINEAR, "test-output/1px-border-reflect.png"); - resize_image("1px-border.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_LINEAR, "test-output/1px-border-clamp.png"); + resize_image("1px-border.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_LINEAR, "test-output/1px-border-reflect.png"); + resize_image("1px-border.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR, "test-output/1px-border-clamp.png"); // sRGB tests - resize_image("gamma_colors.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, STBR_COLORSPACE_SRGB, "test-output/gamma_colors.jpg"); - resize_image("gamma_2.2.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, STBR_COLORSPACE_SRGB, "test-output/gamma_2.2.jpg"); - resize_image("gamma_dalai_lama_gray.jpg", .5f, .5f, STBR_FILTER_CATMULLROM, STBR_EDGE_REFLECT, STBR_COLORSPACE_SRGB, "test-output/gamma_dalai_lama_gray.jpg"); + resize_image("gamma_colors.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_colors.jpg"); + resize_image("gamma_2.2.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_2.2.jpg"); + resize_image("gamma_dalai_lama_gray.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_dalai_lama_gray.jpg"); // filter tests - resize_image("barbara.png", 2, 2, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-upsample-nearest.png"); - resize_image("barbara.png", 2, 2, STBR_FILTER_BILINEAR, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); - resize_image("barbara.png", 2, 2, STBR_FILTER_BICUBIC, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); - resize_image("barbara.png", 2, 2, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-upsample-catmullrom.png"); - resize_image("barbara.png", 2, 2, STBR_FILTER_MITCHELL, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-upsample-mitchell.png"); + resize_image("barbara.png", 2, 2, STBIR_FILTER_NEAREST, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-nearest.png"); + resize_image("barbara.png", 2, 2, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); + resize_image("barbara.png", 2, 2, STBIR_FILTER_BICUBIC, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); + resize_image("barbara.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-catmullrom.png"); + resize_image("barbara.png", 2, 2, STBIR_FILTER_MITCHELL, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-mitchell.png"); - resize_image("barbara.png", 0.5f, 0.5f, STBR_FILTER_NEAREST, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-downsample-nearest.png"); - resize_image("barbara.png", 0.5f, 0.5f, STBR_FILTER_BILINEAR, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); - resize_image("barbara.png", 0.5f, 0.5f, STBR_FILTER_BICUBIC, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); - resize_image("barbara.png", 0.5f, 0.5f, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png"); - resize_image("barbara.png", 0.5f, 0.5f, STBR_FILTER_MITCHELL, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); + resize_image("barbara.png", 0.5f, 0.5f, STBIR_FILTER_NEAREST, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-nearest.png"); + resize_image("barbara.png", 0.5f, 0.5f, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); + resize_image("barbara.png", 0.5f, 0.5f, STBIR_FILTER_BICUBIC, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); + resize_image("barbara.png", 0.5f, 0.5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png"); + resize_image("barbara.png", 0.5f, 0.5f, STBIR_FILTER_MITCHELL, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); for (i = 10; i < 100; i++) { char outname[200]; sprintf(outname, "test-output/barbara-width-%d.jpg", i); - resize_image("barbara.png", (float)i / 100, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); + resize_image("barbara.png", (float)i / 100, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); } for (i = 110; i < 500; i += 10) { char outname[200]; sprintf(outname, "test-output/barbara-width-%d.jpg", i); - resize_image("barbara.png", (float)i / 100, 1, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); + resize_image("barbara.png", (float)i / 100, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); } for (i = 10; i < 100; i++) { char outname[200]; sprintf(outname, "test-output/barbara-height-%d.jpg", i); - resize_image("barbara.png", 1, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); + resize_image("barbara.png", 1, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); } for (i = 110; i < 500; i += 10) { char outname[200]; sprintf(outname, "test-output/barbara-height-%d.jpg", i); - resize_image("barbara.png", 1, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); + resize_image("barbara.png", 1, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); } for (i = 50; i < 200; i += 10) { char outname[200]; sprintf(outname, "test-output/barbara-width-height-%d.jpg", i); - resize_image("barbara.png", 100 / (float)i, (float)i / 100, STBR_FILTER_CATMULLROM, STBR_EDGE_CLAMP, STBR_COLORSPACE_SRGB, outname); + resize_image("barbara.png", 100 / (float)i, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); } - test_format("barbara.png", 0.5, 2.0, STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB); - test_format("barbara.png", 0.5, 2.0, STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR); - test_format("barbara.png", 2.0, 0.5, STBR_TYPE_UINT16, STBR_COLORSPACE_SRGB); - test_format("barbara.png", 2.0, 0.5, STBR_TYPE_UINT16, STBR_COLORSPACE_LINEAR); + test_format("barbara.png", 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB); + test_format("barbara.png", 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR); + test_format("barbara.png", 2.0, 0.5, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB); + test_format("barbara.png", 2.0, 0.5, STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR); - test_format("barbara.png", 0.5, 2.0, STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB); - test_format("barbara.png", 0.5, 2.0, STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR); - test_format("barbara.png", 2.0, 0.5, STBR_TYPE_UINT32, STBR_COLORSPACE_SRGB); - test_format("barbara.png", 2.0, 0.5, STBR_TYPE_UINT32, STBR_COLORSPACE_LINEAR); + test_format("barbara.png", 0.5, 2.0, STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB); + test_format("barbara.png", 0.5, 2.0, STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR); + test_format("barbara.png", 2.0, 0.5, STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB); + test_format("barbara.png", 2.0, 0.5, STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR); - test_float("barbara.png", 0.5, 2.0, STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB); - test_float("barbara.png", 0.5, 2.0, STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR); - test_float("barbara.png", 2.0, 0.5, STBR_TYPE_FLOAT, STBR_COLORSPACE_SRGB); - test_float("barbara.png", 2.0, 0.5, STBR_TYPE_FLOAT, STBR_COLORSPACE_LINEAR); + test_float("barbara.png", 0.5, 2.0, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB); + test_float("barbara.png", 0.5, 2.0, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR); + test_float("barbara.png", 2.0, 0.5, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB); + test_float("barbara.png", 2.0, 0.5, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR); } diff --git a/tests/resample_test_c.c b/tests/resample_test_c.c index dcc3572..50520c6 100644 --- a/tests/resample_test_c.c +++ b/tests/resample_test_c.c @@ -1,11 +1,5 @@ -#ifdef _WIN32 -#define STBR_ASSERT(x) \ - if (!(x)) \ - __debugbreak(); -#endif - -#define STB_RESAMPLE_IMPLEMENTATION -#define STB_RESAMPLE_STATIC -#include "stb_resample.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#define STB_IMAGE_RESIZE_STATIC +#include "stb_image_resize.h" // Just to make sure it will build properly with a c compiler diff --git a/tests/test_c_compilation.c b/tests/test_c_compilation.c index 764f911..22375de 100644 --- a/tests/test_c_compilation.c +++ b/tests/test_c_compilation.c @@ -5,7 +5,7 @@ #define STB_DIVIDE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION #define STB_HERRINGBONE_WANG_TILE_IMEPLEMENTATIOn -#define STB_RESAMPLE_IMPLEMENTATION +#define STB_IMAGE_RESIZE_IMPLEMENTATION #include "stb_herringbone_wang_tile.h" #include "stb_image.h" @@ -14,4 +14,4 @@ #include "stb_dxt.h" #include "stb_c_lexer.h" #include "stb_divide.h" -#include "stb_resample.h" \ No newline at end of file +#include "stb_image_resize.h" \ No newline at end of file From b9bb05b81cc826836a7118701d4118d6bf4e5866 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 18 Aug 2014 09:14:11 -0700 Subject: [PATCH 085/168] minor cleanups --- stb_image_resize.h | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 461f74f..9758642 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -249,15 +249,12 @@ STBRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int inpu typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; #ifdef _MSC_VER -#define STBIR_NOTUSED(v) (void)(v) +#define STBIR__NOTUSED(v) (void)(v) #else -#define STBIR_NOTUSED(v) (void)sizeof(v) +#define STBIR__NOTUSED(v) (void)sizeof(v) #endif -#define STBIR_ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) - -#define STBIR__MAX_UNROLLED_CHANNELS 4 - +#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) // must match stbir_type static unsigned char stbir__type_size[] = { @@ -497,7 +494,7 @@ stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) stbir__inline static int stbir__get_filter_pixel_width(stbir_filter filter, int input_w, int output_w, float scale) { STBIR_ASSERT(filter != 0); - STBIR_ASSERT(filter < STBIR_ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); if (stbir__use_upsampling(scale)) return (int)ceil(stbir__filter_info_table[filter].support * 2); @@ -1373,7 +1370,7 @@ static stbir__inline stbir_size_t stbir__calculate_memory(int input_w, int input int encode_buffer_size = channels * sizeof(float); STBIR_ASSERT(filter != 0); - STBIR_ASSERT(filter < STBIR_ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late if (stbir__use_upsampling(horizontal_scale)) // The horizontal buffer is for when we're downsampling the height and we @@ -1416,9 +1413,9 @@ static int stbir__resize_allocated(const void* input_data, int input_w, int inpu #endif STBIR_ASSERT(filter != 0); - STBIR_ASSERT(filter < STBIR_ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - if (!filter || filter >= STBIR_ARRAY_SIZE(stbir__filter_info_table)) + if (!filter || filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) return 0; STBIR_ASSERT(s1 > s0); From 5eb0236d9de248cbbf2f7dafacbb5b87ee4bd807 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 18 Aug 2014 09:33:01 -0700 Subject: [PATCH 086/168] reverse default behavior of linear/gamma for alpha --- stb_image_resize.h | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 9758642..884372e 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -109,7 +109,7 @@ typedef enum typedef enum { STBIR_FLAG_NONPREMUL_ALPHA = (1 << 0), // The specified alpha channel will be multiplied into all other channels before resampling, then divided back out after. - STBIR_FLAG_FORCE_LINEAR_ALPHA = (1 << 1), // The specified alpha channel should be handled as a linear value even when doing sRGB operations. + STBIR_FLAG_GAMMA_CORRECT_ALPHA = (1 << 1), // The specified alpha channel should be handled as a linear value even when doing sRGB operations. } stbir_flags; typedef unsigned char stbir_uint8; @@ -810,7 +810,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; - if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255; break; @@ -824,7 +824,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); - if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535; break; @@ -838,7 +838,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); - if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295); break; @@ -852,7 +852,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); - if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; break; @@ -1047,7 +1047,7 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out for (n = 0; n < channels; n++) ((unsigned char*)output_buffer)[output_pixel_index + n] = stbir__linear_uchar_to_srgb_uchar[(unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 255)]; - if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + if (!(stbir_info->flags&STBIR_FLAG_FORCE_GAMMA_CORRECT_ALPHA)) ((unsigned char*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); break; @@ -1061,7 +1061,7 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out for (n = 0; n < channels; n++) ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n])) * 65535); - if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) ((unsigned short*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); break; @@ -1075,7 +1075,7 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out for (n = 0; n < channels; n++) ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n]))) * 4294967295); - if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) ((unsigned int*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned int)(((double)stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel])) * 4294967295); break; @@ -1089,7 +1089,7 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out for (n = 0; n < channels; n++) ((float*)output_buffer)[output_pixel_index + n] = stbir__linear_to_srgb(encode_buffer[encode_pixel_index + n]); - if (stbir_info->flags&STBIR_FLAG_FORCE_LINEAR_ALPHA) + if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) ((float*)output_buffer)[output_pixel_index + alpha_channel] = encode_buffer[encode_pixel_index + alpha_channel]; break; @@ -1429,7 +1429,10 @@ static int stbir__resize_allocated(const void* input_data, int input_w, int inpu if (s1 > 1 || s0 < 0 || t1 > 1 || t0 < 0) return 0; - if (flags&(STBIR_FLAG_FORCE_LINEAR_ALPHA | STBIR_FLAG_NONPREMUL_ALPHA)) + if (alpha_channel < 0) + flags = STBIR_FLAG_GAMMA_CORRECT_ALPHA; // this shouldn't be necessary in the long run, but safety for now + + if (!(flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA) || (flags&STBIR_FLAG_NONPREMUL_ALPHA)) STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < channels); if (alpha_channel >= channels) From 32b626859db82681d23106bab602d516d84b4d71 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 18 Aug 2014 10:02:00 -0700 Subject: [PATCH 087/168] remove most per-pixel switches, beginnings of removing encode_pixel switch --- stb_image_resize.h | 145 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 95 insertions(+), 50 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 884372e..990f11d 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -563,7 +563,7 @@ stbir__inline static float* stbir__get_coefficient(stbir__info* stbir_info, int return &stbir_info->horizontal_coefficients[stbir__get_filter_pixel_width(stbir_info->filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale)*n + c]; } -stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) { switch (edge) { @@ -617,6 +617,14 @@ stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) } } +stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +{ + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow(edge, n, max); +} + // What input pixels contribute to this output pixel? static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) { @@ -778,7 +786,7 @@ static float* stbir__get_decode_buffer(stbir__info* stbir_info) static void stbir__decode_scanline(stbir__info* stbir_info, int n) { - int x, c; + int c; int channels = stbir_info->channels; int alpha_channel = stbir_info->alpha_channel; int type = stbir_info->type; @@ -793,77 +801,114 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) int max_x = input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info); int decode = STBIR__DECODE(type, colorspace); - for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + int x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); - switch (decode) + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + int decode_pixel_index = x * channels; + int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255; + } + break; - break; - - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255; + } + break; - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535; - break; + } + break; - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535; + } + break; - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295); - break; + } + break; - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295); + } + break; - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; - break; + } + break; - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; - - break; - - default: - STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); - break; } - if (stbir_info->flags&STBIR_FLAG_NONPREMUL_ALPHA) + break; + + default: + STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; + } + + if (stbir_info->flags & STBIR_FLAG_NONPREMUL_ALPHA) + { + for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++) { + int decode_pixel_index = x * channels; + int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; float alpha = decode_buffer[decode_pixel_index + alpha_channel]; for (c = 0; c < channels; c++) { @@ -1047,7 +1092,7 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out for (n = 0; n < channels; n++) ((unsigned char*)output_buffer)[output_pixel_index + n] = stbir__linear_uchar_to_srgb_uchar[(unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 255)]; - if (!(stbir_info->flags&STBIR_FLAG_FORCE_GAMMA_CORRECT_ALPHA)) + if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) ((unsigned char*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); break; @@ -1100,6 +1145,16 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out } } +// @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, int output_offset, float *encode_buffer, int channels, int alpha_channel, int decode) +{ + int x; + for (x=0; x < num_pixels; ++x) + { + stbir__encode_pixel(stbir_info, output_buffer, output_offset+x*channels, encode_buffer, x*channels, channels, alpha_channel, decode); + } +} + static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) { int x, k; @@ -1134,6 +1189,8 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in STBIR__DEBUG_ASSERT(n0 >= in_first_scanline); STBIR__DEBUG_ASSERT(n1 <= in_last_scanline); + memset(encode_buffer, 0, output_w * sizeof(float) * channels); + for (x = 0; x < output_w; x++) { int in_pixel_index = x * channels; @@ -1142,8 +1199,6 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in STBIR__DEBUG_ASSERT(n1 >= n0); - memset(encode_buffer, 0, sizeof(float) * channels); - for (k = n0; k <= n1; k++) { int coefficient_index = coefficient_counter++; @@ -1152,11 +1207,10 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int c; for (c = 0; c < channels; c++) - encode_buffer[c] += ring_buffer_entry[in_pixel_index + c] * coefficient; + encode_buffer[x*channels + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; } - - stbir__encode_pixel(stbir_info, output_data, out_pixel_index, encode_buffer, 0, channels, alpha_channel, decode); } + stbir__encode_scanline(stbir_info, output_w, output_data, output_row_index, encode_buffer, channels, alpha_channel, decode); } static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) @@ -1280,18 +1334,9 @@ static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessar { if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) { - int x; int output_row = stbir_info->ring_buffer_first_scanline * output_stride; float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); - - for (x = 0; x < output_w; x++) - { - int pixel_index = x * channels; - int ring_pixel_index = pixel_index; - int output_pixel_index = output_row + pixel_index; - - stbir__encode_pixel(stbir_info, output_data, output_pixel_index, ring_buffer_entry, ring_pixel_index, channels, alpha_channel, decode); - } + stbir__encode_scanline(stbir_info, output_w, output_data, output_row, ring_buffer_entry, channels, alpha_channel, decode); } if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) @@ -1367,7 +1412,7 @@ static stbir__inline stbir_size_t stbir__calculate_memory(int input_w, int input int decode_buffer_size = (input_w + pixel_margin*2) * channels * sizeof(float); int horizontal_buffer_size = output_w * channels * sizeof(float); int ring_buffer_size = output_w * channels * filter_height * sizeof(float); - int encode_buffer_size = channels * sizeof(float); + int encode_buffer_size = output_w * channels * sizeof(float); STBIR_ASSERT(filter != 0); STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late From 5dfa79fb31bd35509679333047f88c069742d6a5 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 18 Aug 2014 10:18:59 -0700 Subject: [PATCH 088/168] stride doesn't have to be multiples of pixels --- stb_image_resize.h | 40 +++++++++++++++++++--------------------- tests/resize.dsp | 14 +++++++------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 990f11d..d247ade 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -792,12 +792,12 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) int type = stbir_info->type; int colorspace = stbir_info->colorspace; int input_w = stbir_info->input_w; - int input_stride = stbir_info->input_stride_bytes / stbir__type_size[stbir_info->type]; - const void* input_data = stbir_info->input_data; + int input_stride_bytes = stbir_info->input_stride_bytes; float* decode_buffer = stbir__get_decode_buffer(stbir_info); stbir_edge edge_horizontal = stbir_info->edge_horizontal; stbir_edge edge_vertical = stbir_info->edge_vertical; - int in_buffer_row_index = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride; + int in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; + const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; int max_x = input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info); int decode = STBIR__DECODE(type, colorspace); @@ -809,7 +809,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (; x < max_x; x++) { int decode_pixel_index = x * channels; - int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255; } @@ -819,7 +819,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (; x < max_x; x++) { int decode_pixel_index = x * channels; - int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; @@ -832,7 +832,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (; x < max_x; x++) { int decode_pixel_index = x * channels; - int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535; } @@ -842,7 +842,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (; x < max_x; x++) { int decode_pixel_index = x * channels; - int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); @@ -855,7 +855,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (; x < max_x; x++) { int decode_pixel_index = x * channels; - int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295); } @@ -865,7 +865,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (; x < max_x; x++) { int decode_pixel_index = x * channels; - int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); @@ -878,7 +878,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (; x < max_x; x++) { int decode_pixel_index = x * channels; - int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; } @@ -888,7 +888,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (; x < max_x; x++) { int decode_pixel_index = x * channels; - int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); @@ -908,7 +908,6 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++) { int decode_pixel_index = x * channels; - int input_pixel_index = in_buffer_row_index + stbir__edge_wrap(edge_horizontal, x, input_w) * channels; float alpha = decode_buffer[decode_pixel_index + alpha_channel]; for (c = 0; c < channels; c++) { @@ -1146,12 +1145,12 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out } // @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, int output_offset, float *encode_buffer, int channels, int alpha_channel, int decode) +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; for (x=0; x < num_pixels; ++x) { - stbir__encode_pixel(stbir_info, output_buffer, output_offset+x*channels, encode_buffer, x*channels, channels, alpha_channel, decode); + stbir__encode_pixel(stbir_info, output_buffer, x*channels, encode_buffer, x*channels, channels, alpha_channel, decode); } } @@ -1176,14 +1175,14 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - int n0,n1, output_row_index; + int n0,n1, output_row_start; stbir__calculate_coefficients_upsample(stbir_info, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); n0 = vertical_contributors->n0; n1 = vertical_contributors->n1; - output_row_index = n * stbir_info->output_stride_bytes / stbir__type_size[type]; + output_row_start = n * stbir_info->output_stride_bytes; STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); STBIR__DEBUG_ASSERT(n0 >= in_first_scanline); @@ -1194,7 +1193,6 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in for (x = 0; x < output_w; x++) { int in_pixel_index = x * channels; - int out_pixel_index = output_row_index + x * channels; int coefficient_counter = 0; STBIR__DEBUG_ASSERT(n1 >= n0); @@ -1210,7 +1208,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in encode_buffer[x*channels + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; } } - stbir__encode_scanline(stbir_info, output_w, output_data, output_row_index, encode_buffer, channels, alpha_channel, decode); + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); } static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) @@ -1315,7 +1313,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info) static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) { - int output_stride = stbir_info->output_stride_bytes / stbir__type_size[stbir_info->type]; + int output_stride_bytes = stbir_info->output_stride_bytes; int channels = stbir_info->channels; int alpha_channel = stbir_info->alpha_channel; int type = stbir_info->type; @@ -1334,9 +1332,9 @@ static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessar { if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) { - int output_row = stbir_info->ring_buffer_first_scanline * output_stride; + int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); - stbir__encode_scanline(stbir_info, output_w, output_data, output_row, ring_buffer_entry, channels, alpha_channel, decode); + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); } if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) diff --git a/tests/resize.dsp b/tests/resize.dsp index 232b1c4..cf40689 100644 --- a/tests/resize.dsp +++ b/tests/resize.dsp @@ -48,8 +48,8 @@ BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "resize - Win32 Debug" @@ -63,16 +63,16 @@ LINK32=link.exe # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF @@ -82,7 +82,7 @@ LINK32=link.exe # Name "resize - Win32 Debug" # Begin Source File -SOURCE=..\resample_test.cpp +SOURCE=.\resample_test.cpp # End Source File # End Target # End Project From c1b876768e27990357da2bb0965eb2c4c903984f Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 31 Aug 2014 06:31:50 -0700 Subject: [PATCH 089/168] in progress new API --- stb_image_resize.h | 418 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 257 insertions(+), 161 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index d247ade..91e6b2a 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -7,8 +7,9 @@ #define STBIR_ASSERT(x) to avoid using assert.h. - #define STBIR_MALLOC and STBIR_FREE to avoid using stdlib.h malloc. This will apply - to all functions except stbir_resize_arbitrary(), which doesn't allocate memory. + #define STBIR_MALLOC(context,size) and STBIR_FREE(context,ptr) 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. QUICK NOTES: Written with emphasis on usage and speed. Only the resize operation is @@ -36,82 +37,6 @@ #ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H #define STBIR_INCLUDE_STB_IMAGE_RESIZE_H -// Basic usage: -// result = stbir_resize_uint8_srgb(input_data, input_w, input_h, output_data, output_w, output_h, channels, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP); -// * input_data is your supplied pixels. -// * output_data will be the resized pixels. It should be of size output_w * output_h * channels -// * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBIR_ASSERT() to see it. -// * If you're unsure of which filter to use, Catmull-Rom is a good upsampling filter and Mitchell is a good downsampling filter. -// -// -// Data types provided: uint8, uint16, uint32, float. -// -// -// Other function groups are provided, one for each data type, for more advanced functionality: -// -// stbir_resize_type_alphaweighted(input_data, input_w, input_h, output_data, output_w, output_h, channels, alpha_channel, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB) -// * alpha_channel - if nonnegative, this channel will be multiplied into all other channels before resampling, then divided back out after. -// -// stbir_resize_type_subpixel(input_data, input_w, input_h, output_data, output_w, output_h, s0, t0, s1, t1, channels, filter, edge) -// * 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. -// -// -// All functionality is offered in this function: -// -// result = stbir_resize_arbitrary(input_data, input_w, input_h, input_stride_in_bytes, -// output_data, output_w, output_h, output_stride_in_bytes, -// s0, t0, s1, t1, -// channels, alpha_channel, flags, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); -// -// * input_stride_in_bytes and output_stride_in_bytes can be 0. If so they will be automatically calculated as width * channels. -// * 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. -// * flags are from the stbir_flags enum and should be bitwise OR'd together. -// * First edge parameter is for horizontal edge behavior, second is for vertical. -// * Returned result is 1 for success or 0 in case of an error. In the case of an error an assert with be triggered, #define STBIR_ASSERT() to see it. -// * Memory required grows approximately linearly with input and output size, but with discontinuities at input_w == output_w and input_h == output_height. -// * To use temporary memory, define an STBIR_MALLOC that returns the temp memory and make STBIR_FREE do nothing--each function only ever allocates one block - - -typedef enum -{ - STBIR_FILTER_NEAREST = 1, - STBIR_FILTER_BILINEAR = 2, - STBIR_FILTER_BICUBIC = 3, // A cubic b spline - STBIR_FILTER_CATMULLROM = 4, - STBIR_FILTER_MITCHELL = 5, -} stbir_filter; - -typedef enum -{ - STBIR_EDGE_CLAMP = 1, - STBIR_EDGE_REFLECT = 2, - STBIR_EDGE_WRAP = 3, -} stbir_edge; - -typedef enum -{ - STBIR_COLORSPACE_LINEAR, - STBIR_COLORSPACE_SRGB, - - STBIR_MAX_COLORSPACES, -} stbir_colorspace; - -typedef enum -{ - STBIR_TYPE_UINT8 , - STBIR_TYPE_UINT16, - STBIR_TYPE_UINT32, - STBIR_TYPE_FLOAT , - - STBIR_MAX_TYPES -} stbir_type; - -typedef enum -{ - STBIR_FLAG_NONPREMUL_ALPHA = (1 << 0), // The specified alpha channel will be multiplied into all other channels before resampling, then divided back out after. - STBIR_FLAG_GAMMA_CORRECT_ALPHA = (1 << 1), // The specified alpha channel should be handled as a linear value even when doing sRGB operations. -} stbir_flags; - typedef unsigned char stbir_uint8; #ifdef _MSC_VER @@ -123,88 +48,176 @@ typedef uint16_t stbir_uint16; typedef uint32_t stbir_uint32; #endif -typedef unsigned int stbir_size_t; // to avoid including a header for size_t - #ifdef STB_IMAGE_RESIZE_STATIC -#define STBRDEF static +#define STBIRDEF static #else #ifdef __cplusplus -#define STBRDEF extern "C" +#define STBIRDEF extern "C" #else -#define STBRDEF extern +#define STBIRDEF extern #endif #endif + ////////////////////////////////////////////////////////////////////////////// // -// PRIMARY API - sRGB type-safe image resizing. +// Easy-to-use API: // +// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4) +// * input_w is input image width (x-axis), input_h is input image height (y-axis) +// * stride is the offset between successive rows of image data in memory, in bytes. you can +// specify 0 to mean packed continuously in memory +// * alpha channel is treated identically to other channels. +// * colorspace is linear or sRGB as specified by function name +// * returned result is 1 for success or 0 in case of an error. +// In the case of an error an assert with be triggered, #define STBIR_ASSERT() to see it. +// * Memory required grows approximately linearly with input and output size, but with +// discontinuities at input_w == output_w and input_h == output_h. +// * These functions use a "default" resampling filter defined at compile time. To change the filter, +// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE +// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. -STBRDEF int stbir_resize_uint8_srgb(const stbir_uint8* input_data, int input_w, int input_h, - stbir_uint8* output_data, int output_w, int output_h, - int channels, stbir_filter filter, stbir_edge edge); +STBIRDEF int stbir_resize_uint8( unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); -STBRDEF int stbir_resize_uint16_srgb(const stbir_uint16* input_data, int input_w, int input_h, - stbir_uint16* output_data, int output_w, int output_h, - int channels, stbir_filter filter, stbir_edge edge); +STBIRDEF int stbir_resize_uint8_srgb(unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); -STBRDEF int stbir_resize_uint32_srgb(const stbir_uint32* input_data, int input_w, int input_h, - stbir_uint32* output_data, int output_w, int output_h, - int channels, stbir_filter filter, stbir_edge edge); +STBIRDEF int stbir_resize_float( float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); -STBRDEF int stbir_resize_float_srgb(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - int channels, stbir_filter filter, stbir_edge edge); +typedef enum +{ + STBIR_EDGE_CLAMP = 1, + STBIR_EDGE_REFLECT = 2, + STBIR_EDGE_WRAP = 3, +} stbir_edge; + +// This function adds the ability to specify how requests to sample off the edge of the image are handled. +STBIRDEF int stbir_resize_uint8_srgb_edgemode(unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, + stbir_edge edge_wrap_mode); + +////////////////////////////////////////////////////////////////////////////// +// +// Medium-complexity API +// +// This extends the easy-to-use API as follows: +// +// * 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) +// * Filter can be selected explicitly +// * uint16 image type +// * sRGB colorspace available for all types +// * context parameter for passing to STBIR_MALLOC + +typedef enum +{ + STBIR_FILTER_NEAREST = 1, + STBIR_FILTER_BILINEAR = 2, + STBIR_FILTER_BICUBIC = 3, // A cubic b spline + STBIR_FILTER_CATMULLROM = 4, + STBIR_FILTER_MITCHELL = 5, +} stbir_filter; + +#define STBIR_FLAG_NONPREMUL_ALPHA (1 << 0) // The specified alpha channel will be multiplied into all other channels before resampling, then divided back out after. +#define STBIR_FLAG_GAMMA_CORRECT_ALPHA (1 << 1) // The specified alpha channel should be handled as gamma-corrected value even when doing sRGB operations. + +typedef enum +{ + STBIR_COLORSPACE_LINEAR, + STBIR_COLORSPACE_SRGB, + + STBIR_MAX_COLORSPACES, +} stbir_colorspace; + +// The following functions are all identical except for the type of the image data + +STBIRDEF int stbir_resize_uint8_generic( unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, + void *context); + +STBIRDEF int stbir_resize_uint16_generic(stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, + void *context); + +STBIRDEF int stbir_resize_float_generic( float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, + void *context); + +#define STBIR_ALPHA_CHANNEL_NONE -1 -STBRDEF int stbir_resize_uint8_alphaweighted(const stbir_uint8* input_data, int input_w, int input_h, - stbir_uint8* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace); +////////////////////////////////////////////////////////////////////////////// +// +// Full-complexity API +// +// This extends the medium API as follows: +// +// * uint32 image type +// * not typesafe +// * separate filter types for each axis +// * separate edge modes for each axis +// * can specify scale explicitly for subpixel correctness +// * can specify image source tile using texture coordinates -STBRDEF int stbir_resize_uint16_alphaweighted(const stbir_uint16* input_data, int input_w, int input_h, - stbir_uint16* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace); +typedef enum +{ + STBIR_TYPE_UINT8 , + STBIR_TYPE_UINT16, + STBIR_TYPE_UINT32, + STBIR_TYPE_FLOAT , -STBRDEF int stbir_resize_uint32_alphaweighted(const stbir_uint32* input_data, int input_w, int input_h, - stbir_uint32* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace); + STBIR_MAX_TYPES +} stbir_datatype; -STBRDEF int stbir_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace); +STBIRDEF int stbir_resize( unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, + stbir_edge edge_mode_vertical , stbir_filter filter_vertical, + stbir_colorspace space); +STBIRDEF int stbir_resize_subpixel(unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, + stbir_edge edge_mode_vertical , stbir_filter filter_vertical, + stbir_colorspace space, + float x_scale, float x_offset, + float y_scale, float y_offset); -STBRDEF int stbir_resize_uint8_subpixel(const stbir_uint8* input_data, int input_w, int input_h, - stbir_uint8* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbir_filter filter, stbir_edge edge); +STBIRDEF int stbir_resize_region( unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, + stbir_edge edge_mode_vertical , stbir_filter filter_vertical, + stbir_colorspace space, + 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. -STBRDEF int stbir_resize_uint16_subpixel(const stbir_uint16* input_data, int input_w, int input_h, - stbir_uint16* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbir_filter filter, stbir_edge edge); - -STBRDEF int stbir_resize_uint32_subpixel(const stbir_uint32* input_data, int input_w, int input_h, - stbir_uint32* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbir_filter filter, stbir_edge edge); - -STBRDEF int stbir_resize_float_subpixel(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbir_filter filter, stbir_edge edge); - - -STBRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbir_uint32 flags, stbir_type type, stbir_filter filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace); // // //// end header file ///////////////////////////////////////////////////// #endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H + + + + #ifdef STB_IMAGE_RESIZE_IMPLEMENTATION #ifndef STBIR_ASSERT @@ -229,11 +242,10 @@ STBRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int inpu #ifndef STBIR_MALLOC #include -#define STBIR_MALLOC(x) malloc(x) -#define STBIR_FREE(x) free(x) +#define STBIR_MALLOC(c,x) malloc(c,x) +#define STBIR_FREE(c,x) free(c,x) #endif - #ifndef _MSC_VER #ifdef __cplusplus #define stbir__inline inline @@ -256,7 +268,15 @@ typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1] #define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) -// must match stbir_type +#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE +#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +#endif + +#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +#endif + +// must match stbir_datatype static unsigned char stbir__type_size[] = { 1, // STBIR_TYPE_UINT8 2, // STBIR_TYPE_UINT16 @@ -303,7 +323,7 @@ typedef struct int channels; int alpha_channel; stbir_uint32 flags; - stbir_type type; + stbir_datatype type; stbir_filter filter; stbir_edge edge_horizontal; stbir_edge edge_vertical; @@ -1395,7 +1415,7 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info) stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); } -static stbir__inline stbir_size_t stbir__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbir_filter filter) +static stbir__inline stbir_uint32 stbir__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbir_filter filter) { float horizontal_scale = ((float)output_w / input_w) / (s1 - s0); float vertical_scale = ((float)output_h / input_h) / (t1 - t0); @@ -1430,13 +1450,13 @@ static stbir__inline stbir_size_t stbir__calculate_memory(int input_w, int input static int stbir__resize_allocated(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbir_uint32 flags, stbir_type type, stbir_filter filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, - void* tempmem, stbir_size_t tempmem_size_in_bytes) + float s0, float t0, float s1, float t1, float *transform, + int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, stbir_filter x_filter, stbir_filter y_filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, + void* tempmem, size_t tempmem_size_in_bytes) { stbir__info* stbir_info = (stbir__info*)tempmem; - stbir_size_t memory_required = stbir__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + size_t memory_required = stbir__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w * stbir__type_size[type]; int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w * stbir__type_size[type]; @@ -1508,12 +1528,21 @@ static int stbir__resize_allocated(const void* input_data, int input_w, int inpu stbir_info->s1 = s1; stbir_info->t1 = t1; - stbir_info->horizontal_scale = ((float)output_w / input_w) / (s1 - s0); - stbir_info->vertical_scale = ((float)output_h / input_h) / (t1 - t0); - - stbir_info->horizontal_shift = s0 * input_w / (s1 - s0); - stbir_info->vertical_shift = t0 * input_h / (t1 - t0); + if (transform) + { + stbir_info->horizontal_scale = transform[0]; + stbir_info->vertical_scale = transform[1]; + stbir_info->horizontal_shift = transform[2]; + stbir_info->vertical_shift = transform[3]; + } + else + { + stbir_info->horizontal_scale = ((float)output_w / input_w) / (s1 - s0); + stbir_info->vertical_scale = ((float)output_h / input_h) / (t1 - t0); + stbir_info->horizontal_shift = s0 * input_w / (s1 - s0); + stbir_info->vertical_shift = t0 * input_h / (t1 - t0); + } stbir_info->channels = channels; stbir_info->alpha_channel = alpha_channel; stbir_info->flags = flags; @@ -1573,10 +1602,10 @@ static int stbir__resize_allocated(const void* input_data, int input_w, int inpu } -STBRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, +STBIRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbir_uint32 flags, stbir_type type, stbir_filter filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) + int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, stbir_filter filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) { int result; size_t memory_required = stbir__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); @@ -1592,63 +1621,63 @@ STBRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int inpu return result; } -STBRDEF int stbir_resize_uint8_srgb(const stbir_uint8* input_data, int input_w, int input_h, +STBIRDEF int stbir_resize_uint8_srgb(const stbir_uint8* input_data, int input_w, int input_h, stbir_uint8* output_data, int output_w, int output_h, int channels, stbir_filter filter, stbir_edge edge) { return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBIR_TYPE_UINT8, filter, edge, edge, STBIR_COLORSPACE_SRGB); } -STBRDEF int stbir_resize_uint16_srgb(const stbir_uint16* input_data, int input_w, int input_h, +STBIRDEF int stbir_resize_uint16_srgb(const stbir_uint16* input_data, int input_w, int input_h, stbir_uint16* output_data, int output_w, int output_h, int channels, stbir_filter filter, stbir_edge edge) { return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBIR_TYPE_UINT16, filter, edge, edge, STBIR_COLORSPACE_SRGB); } -STBRDEF int stbir_resize_uint32_srgb(const stbir_uint32* input_data, int input_w, int input_h, +STBIRDEF int stbir_resize_uint32_srgb(const stbir_uint32* input_data, int input_w, int input_h, stbir_uint32* output_data, int output_w, int output_h, int channels, stbir_filter filter, stbir_edge edge) { return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBIR_TYPE_UINT32, filter, edge, edge, STBIR_COLORSPACE_SRGB); } -STBRDEF int stbir_resize_float_srgb(const float* input_data, int input_w, int input_h, +STBIRDEF int stbir_resize_float_srgb(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, int channels, stbir_filter filter, stbir_edge edge) { return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBIR_TYPE_FLOAT, filter, edge, edge, STBIR_COLORSPACE_SRGB); } -STBRDEF int stbir_resize_uint8_alphaweighted(const stbir_uint8* input_data, int input_w, int input_h, +STBIRDEF int stbir_resize_uint8_alphaweighted(const stbir_uint8* input_data, int input_w, int input_h, stbir_uint8* output_data, int output_w, int output_h, int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace) { return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT8, filter, edge, edge, colorspace); } -STBRDEF int stbir_resize_uint16_alphaweighted(const stbir_uint16* input_data, int input_w, int input_h, +STBIRDEF int stbir_resize_uint16_alphaweighted(const stbir_uint16* input_data, int input_w, int input_h, stbir_uint16* output_data, int output_w, int output_h, int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace) { return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT16, filter, edge, edge, colorspace); } -STBRDEF int stbir_resize_uint32_alphaweighted(const stbir_uint32* input_data, int input_w, int input_h, +STBIRDEF int stbir_resize_uint32_alphaweighted(const stbir_uint32* input_data, int input_w, int input_h, stbir_uint32* output_data, int output_w, int output_h, int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace) { return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT32, filter, edge, edge, colorspace); } -STBRDEF int stbir_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, +STBIRDEF int stbir_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace) { return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_FLOAT, filter, edge, edge, colorspace); } -STBRDEF int stbir_resize_uint8_subpixel(const stbir_uint8* input_data, int input_w, int input_h, +STBIRDEF int stbir_resize_uint8_subpixel(const stbir_uint8* input_data, int input_w, int input_h, stbir_uint8* output_data, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbir_filter filter, stbir_edge edge) @@ -1656,7 +1685,7 @@ STBRDEF int stbir_resize_uint8_subpixel(const stbir_uint8* input_data, int input return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_UINT8, filter, edge, edge, STBIR_COLORSPACE_SRGB); } -STBRDEF int stbir_resize_uint16_subpixel(const stbir_uint16* input_data, int input_w, int input_h, +STBIRDEF int stbir_resize_uint16_subpixel(const stbir_uint16* input_data, int input_w, int input_h, stbir_uint16* output_data, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbir_filter filter, stbir_edge edge) @@ -1664,7 +1693,7 @@ STBRDEF int stbir_resize_uint16_subpixel(const stbir_uint16* input_data, int inp return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_UINT16, filter, edge, edge, STBIR_COLORSPACE_SRGB); } -STBRDEF int stbir_resize_uint32_subpixel(const stbir_uint32* input_data, int input_w, int input_h, +STBIRDEF int stbir_resize_uint32_subpixel(const stbir_uint32* input_data, int input_w, int input_h, stbir_uint32* output_data, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbir_filter filter, stbir_edge edge) @@ -1672,7 +1701,7 @@ STBRDEF int stbir_resize_uint32_subpixel(const stbir_uint32* input_data, int inp return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_UINT32, filter, edge, edge, STBIR_COLORSPACE_SRGB); } -STBRDEF int stbir_resize_float_subpixel(const float* input_data, int input_w, int input_h, +STBIRDEF int stbir_resize_float_subpixel(const float* input_data, int input_w, int input_h, float* output_data, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbir_filter filter, stbir_edge edge) @@ -1680,6 +1709,73 @@ STBRDEF int stbir_resize_float_subpixel(const float* input_data, int input_w, in return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_FLOAT, filter, edge, edge, STBIR_COLORSPACE_SRGB); } +STBIRDEF int stbir_resize_uint8( unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + +STBIRDEF int stbir_resize_uint8_srgb(unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + +STBIRDEF int stbir_resize_float( float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + +typedef enum +{ + STBIR_EDGE_CLAMP = 1, + STBIR_EDGE_REFLECT = 2, + STBIR_EDGE_WRAP = 3, +} stbir_edge; + +// This function adds the ability to specify how requests to sample off the edge of the image are handled. +STBIRDEF int stbir_resize_uint8_srgb_edgemode(unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, + stbir_edge edge_wrap_mode); + +STBIRDEF int stbir_resize_uint8_generic( unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, + void *context); + +STBIRDEF int stbir_resize_uint16_generic(stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, + void *context); + +STBIRDEF int stbir_resize_float_generic( float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, + void *context); + +STBIRDEF int stbir_resize( unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, + stbir_edge edge_mode_vertical , stbir_filter filter_vertical, + stbir_colorspace space); + +STBIRDEF int stbir_resize_subpixel(unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, + stbir_edge edge_mode_vertical , stbir_filter filter_vertical, + stbir_colorspace space, + float x_scale, float x_offset, + float y_scale, float y_offset); + +STBIRDEF int stbir_resize_region( unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, + stbir_edge edge_mode_vertical , stbir_filter filter_vertical, + stbir_colorspace space, + float s0, float t0, float s1, float t1); + #endif // STB_IMAGE_RESIZE_IMPLEMENTATION /* From 1bd9770e75e666155d9759e27db7a3ad25bfb8c2 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 31 Aug 2014 06:47:45 -0700 Subject: [PATCH 090/168] separate filter for horizontal and vertical --- stb_image_resize.h | 82 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index d247ade..ae4f17d 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -304,7 +304,8 @@ typedef struct int alpha_channel; stbir_uint32 flags; stbir_type type; - stbir_filter filter; + stbir_filter horizontal_filter; + stbir_filter vertical_filter; stbir_edge edge_horizontal; stbir_edge edge_vertical; stbir_colorspace colorspace; @@ -504,12 +505,12 @@ stbir__inline static int stbir__get_filter_pixel_width(stbir_filter filter, int stbir__inline static int stbir__get_filter_pixel_width_horizontal(stbir__info* stbir_info) { - return stbir__get_filter_pixel_width(stbir_info->filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); + return stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); } stbir__inline static int stbir__get_filter_pixel_width_vertical(stbir__info* stbir_info) { - return stbir__get_filter_pixel_width(stbir_info->filter, stbir_info->input_h, stbir_info->output_h, stbir_info->vertical_scale); + return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->input_h, stbir_info->output_h, stbir_info->vertical_scale); } // This is how much to expand buffers to account for filters seeking outside @@ -521,12 +522,12 @@ stbir__inline static int stbir__get_filter_pixel_margin(stbir_filter filter, int stbir__inline static int stbir__get_filter_pixel_margin_horizontal(stbir__info* stbir_info) { - return stbir__get_filter_pixel_width(stbir_info->filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale) / 2; + return stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale) / 2; } stbir__inline static int stbir__get_filter_pixel_margin_vertical(stbir__info* stbir_info) { - return stbir__get_filter_pixel_width(stbir_info->filter, stbir_info->input_h, stbir_info->output_h, stbir_info->vertical_scale) / 2; + return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->input_h, stbir_info->output_h, stbir_info->vertical_scale) / 2; } stbir__inline static int stbir__get_horizontal_contributors_noinfo(stbir_filter filter, int input_w, int output_w, float horizontal_scale) @@ -539,17 +540,18 @@ stbir__inline static int stbir__get_horizontal_contributors_noinfo(stbir_filter stbir__inline static int stbir__get_horizontal_contributors(stbir__info* stbir_info) { - return stbir__get_horizontal_contributors_noinfo(stbir_info->filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); + return stbir__get_horizontal_contributors_noinfo(stbir_info->horizontal_filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); } -stbir__inline static int stbir__get_total_coefficients_noinfo(stbir_filter filter, int input_w, int output_w, float horizontal_scale) +stbir__inline static int stbir__get_total_coefficients_noinfo(stbir_filter h_filter, int input_w, int output_w, float horizontal_scale) { - return stbir__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * stbir__get_filter_pixel_width(filter, input_w, output_w, horizontal_scale); + return stbir__get_horizontal_contributors_noinfo(h_filter, input_w, output_w, horizontal_scale) + * stbir__get_filter_pixel_width (h_filter, input_w, output_w, horizontal_scale); } stbir__inline static int stbir__get_total_coefficients(stbir__info* stbir_info) { - return stbir__get_total_coefficients_noinfo(stbir_info->filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); + return stbir__get_total_coefficients_noinfo(stbir_info->horizontal_filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); } stbir__inline static stbir__contributors* stbir__get_contributor(stbir__info* stbir_info, int n) @@ -560,7 +562,7 @@ stbir__inline static stbir__contributors* stbir__get_contributor(stbir__info* st stbir__inline static float* stbir__get_coefficient(stbir__info* stbir_info, int n, int c) { - return &stbir_info->horizontal_coefficients[stbir__get_filter_pixel_width(stbir_info->filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale)*n + c]; + return &stbir_info->horizontal_coefficients[stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale)*n + c]; } static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) @@ -655,12 +657,11 @@ static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radi *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); } -static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) +static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbir_filter filter, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) { int i; float total_filter = 0; float filter_scale; - stbir_filter filter = stbir_info->filter; STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= stbir__get_filter_pixel_width_horizontal(stbir_info)); @@ -685,10 +686,9 @@ static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, int coefficient_group[i] *= filter_scale; } -static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) +static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) { int i; - stbir_filter filter = stbir_info->filter; STBIR__DEBUG_ASSERT(out_last_pixel - out_first_pixel <= stbir__get_filter_pixel_width_horizontal(stbir_info)); @@ -740,7 +740,7 @@ static void stbir__calculate_horizontal_filters(stbir__info* stbir_info) if (stbir__use_width_upsampling(stbir_info)) { - float out_pixels_radius = stbir__filter_info_table[stbir_info->filter].support * scale_ratio; + float out_pixels_radius = stbir__filter_info_table[stbir_info->horizontal_filter].support * scale_ratio; // Looping through out pixels for (n = 0; n < total_contributors; n++) @@ -750,12 +750,12 @@ static void stbir__calculate_horizontal_filters(stbir__info* stbir_info) stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, stbir_info->horizontal_shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); - stbir__calculate_coefficients_upsample(stbir_info, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(stbir_info, n), stbir__get_coefficient(stbir_info, n, 0)); + stbir__calculate_coefficients_upsample(stbir_info, stbir_info->horizontal_filter, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(stbir_info, n), stbir__get_coefficient(stbir_info, n, 0)); } } else { - float in_pixels_radius = stbir__filter_info_table[stbir_info->filter].support / scale_ratio; + float in_pixels_radius = stbir__filter_info_table[stbir_info->horizontal_filter].support / scale_ratio; // Looping through in pixels for (n = 0; n < total_contributors; n++) @@ -766,7 +766,7 @@ static void stbir__calculate_horizontal_filters(stbir__info* stbir_info) stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, stbir_info->horizontal_shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); - stbir__calculate_coefficients_downsample(stbir_info, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(stbir_info, n), stbir__get_coefficient(stbir_info, n, 0)); + stbir__calculate_coefficients_downsample(stbir_info, stbir_info->horizontal_filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(stbir_info, n), stbir__get_coefficient(stbir_info, n, 0)); } #ifdef STBIR_DEBUG @@ -1177,7 +1177,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int n0,n1, output_row_start; - stbir__calculate_coefficients_upsample(stbir_info, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); + stbir__calculate_coefficients_upsample(stbir_info, stbir_info->vertical_filter, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); n0 = vertical_contributors->n0; n1 = vertical_contributors->n1; @@ -1230,7 +1230,7 @@ static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); int n0,n1,max_n; - stbir__calculate_coefficients_downsample(stbir_info, stbir_info->vertical_scale, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); + stbir__calculate_coefficients_downsample(stbir_info, stbir_info->vertical_filter, stbir_info->vertical_scale, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); n0 = vertical_contributors->n0; n1 = vertical_contributors->n1; @@ -1264,7 +1264,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info) { int y; float scale_ratio = stbir_info->vertical_scale; - float out_scanlines_radius = stbir__filter_info_table[stbir_info->filter].support * scale_ratio; + float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support * scale_ratio; STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); @@ -1360,7 +1360,7 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info) int y; float scale_ratio = stbir_info->vertical_scale; int output_h = stbir_info->output_h; - float in_pixels_radius = stbir__filter_info_table[stbir_info->filter].support / scale_ratio; + float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support / scale_ratio; int max_y = stbir_info->input_h + stbir__get_filter_pixel_margin_vertical(stbir_info); STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); @@ -1395,25 +1395,27 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info) stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); } -static stbir__inline stbir_size_t stbir__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbir_filter filter) +static stbir__inline stbir_size_t stbir__calculate_memory(int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels, stbir_filter h_filter, stbir_filter v_filter) { float horizontal_scale = ((float)output_w / input_w) / (s1 - s0); float vertical_scale = ((float)output_h / input_h) / (t1 - t0); - int pixel_margin = stbir__get_filter_pixel_margin(filter, input_w, output_w, horizontal_scale); - int filter_height = stbir__get_filter_pixel_width(filter, input_h, output_h, vertical_scale); + int pixel_margin = stbir__get_filter_pixel_margin(h_filter, input_w, output_w, horizontal_scale); + int filter_height = stbir__get_filter_pixel_width(v_filter, input_h, output_h, vertical_scale); int info_size = sizeof(stbir__info); - int contributors_size = stbir__get_horizontal_contributors_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(stbir__contributors); - int horizontal_coefficients_size = stbir__get_total_coefficients_noinfo(filter, input_w, output_w, horizontal_scale) * sizeof(float); + int contributors_size = stbir__get_horizontal_contributors_noinfo(h_filter, input_w, output_w, horizontal_scale) * sizeof(stbir__contributors); + int horizontal_coefficients_size = stbir__get_total_coefficients_noinfo(h_filter, input_w, output_w, horizontal_scale) * sizeof(float); int vertical_coefficients_size = filter_height * sizeof(float); int decode_buffer_size = (input_w + pixel_margin*2) * channels * sizeof(float); int horizontal_buffer_size = output_w * channels * sizeof(float); int ring_buffer_size = output_w * channels * filter_height * sizeof(float); int encode_buffer_size = output_w * channels * sizeof(float); - STBIR_ASSERT(filter != 0); - STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(h_filter != 0); + STBIR_ASSERT(h_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(v_filter != 0); + STBIR_ASSERT(v_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late if (stbir__use_upsampling(horizontal_scale)) // The horizontal buffer is for when we're downsampling the height and we @@ -1431,12 +1433,13 @@ static stbir__inline stbir_size_t stbir__calculate_memory(int input_w, int input static int stbir__resize_allocated(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbir_uint32 flags, stbir_type type, stbir_filter filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, + int channels, int alpha_channel, stbir_uint32 flags, stbir_type type, stbir_filter h_filter, stbir_filter v_filter, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, void* tempmem, stbir_size_t tempmem_size_in_bytes) { stbir__info* stbir_info = (stbir__info*)tempmem; - stbir_size_t memory_required = stbir__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, filter); + stbir_size_t memory_required = stbir__calculate_memory(input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels, h_filter, v_filter); int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w * stbir__type_size[type]; int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w * stbir__type_size[type]; @@ -1455,10 +1458,14 @@ static int stbir__resize_allocated(const void* input_data, int input_w, int inpu memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); #endif - STBIR_ASSERT(filter != 0); - STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(h_filter != 0); + STBIR_ASSERT(h_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(v_filter != 0); + STBIR_ASSERT(v_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - if (!filter || filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + if (!h_filter || h_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + if (!v_filter || v_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) return 0; STBIR_ASSERT(s1 > s0); @@ -1518,7 +1525,8 @@ static int stbir__resize_allocated(const void* input_data, int input_w, int inpu stbir_info->alpha_channel = alpha_channel; stbir_info->flags = flags; stbir_info->type = type; - stbir_info->filter = filter; + stbir_info->horizontal_filter = h_filter; + stbir_info->vertical_filter = v_filter; stbir_info->edge_horizontal = edge_horizontal; stbir_info->edge_vertical = edge_vertical; stbir_info->colorspace = colorspace; @@ -1579,13 +1587,13 @@ STBRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int inpu int channels, int alpha_channel, stbir_uint32 flags, stbir_type type, stbir_filter filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) { int result; - size_t memory_required = stbir__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter); + size_t memory_required = stbir__calculate_memory(input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels, filter, filter); void* extra_memory = STBIR_MALLOC(memory_required); if (!extra_memory) return 0; - result = stbir__resize_allocated(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, alpha_channel, flags, type, filter, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); + result = stbir__resize_allocated(input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, channels, alpha_channel, flags, type, filter, filter, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); STBIR_FREE(extra_memory); From 2549d8156e8e38977dbbe4f8ac06db5327d72ad9 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 31 Aug 2014 07:23:34 -0700 Subject: [PATCH 091/168] in-progress refactoring --- tests/resample_test.cpp | 4 ++-- tests/stb.dsp | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 856351b..b906d15 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -187,7 +187,7 @@ void convert_image(const F* input, T* output, int length) } template -void test_format(const char* file, float width_percent, float height_percent, stbir_type type, stbir_colorspace colorspace) +void test_format(const char* file, float width_percent, float height_percent, stbir_datatype type, stbir_colorspace colorspace) { int w, h, n; unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); @@ -228,7 +228,7 @@ void convert_image_float(const float* input, unsigned char* output, int length) output[i] = (unsigned char)(input[i] * 255); } -void test_float(const char* file, float width_percent, float height_percent, stbir_type type, stbir_colorspace colorspace) +void test_float(const char* file, float width_percent, float height_percent, stbir_datatype type, stbir_colorspace colorspace) { int w, h, n; unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); diff --git a/tests/stb.dsp b/tests/stb.dsp index 40e3229..9fd5df3 100644 --- a/tests/stb.dsp +++ b/tests/stb.dsp @@ -106,10 +106,18 @@ SOURCE=..\stb_dxt.h # End Source File # Begin Source File +SOURCE=..\stb_herringbone_wang_tile.h +# End Source File +# Begin Source File + SOURCE=..\stb_image.h # End Source File # Begin Source File +SOURCE=..\stb_image_resize.h +# End Source File +# Begin Source File + SOURCE=..\stb_image_write.h # End Source File # Begin Source File From bbd4e2ee9ac48d51fec23141b268d076059011b7 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 31 Aug 2014 07:32:10 -0700 Subject: [PATCH 092/168] new API "finished" but untested --- stb_image_resize.h | 102 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 86 insertions(+), 16 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 6678f6f..9ed04be 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -119,6 +119,7 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels typedef enum { + STBIR_FILTER_DEFAULT = 0, STBIR_FILTER_NEAREST = 1, STBIR_FILTER_BILINEAR = 2, STBIR_FILTER_BICUBIC = 3, // A cubic b spline @@ -189,14 +190,14 @@ STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, stbir_edge edge_mode_vertical , stbir_filter filter_vertical, - stbir_colorspace space); + stbir_colorspace space, void *alloc_context); STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, stbir_edge edge_mode_vertical , stbir_filter filter_vertical, - stbir_colorspace space, + stbir_colorspace space, void *alloc_context, float x_scale, float x_offset, float y_scale, float y_offset); @@ -205,7 +206,7 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, stbir_edge edge_mode_vertical , stbir_filter filter_vertical, - stbir_colorspace space, + stbir_colorspace space, void *alloc_context, 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. @@ -1765,39 +1766,82 @@ STBIRDEF int stbir_resize_float_subpixel(const float* input_data, int input_w, i STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); + int num_channels) +{ + return stbir_resize_arbitrary2(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); + int num_channels) +{ + return stbir_resize_arbitrary2(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); +} STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); + int num_channels) +{ + return stbir_resize_arbitrary2(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} -// This function adds the ability to specify how requests to sample off the edge of the image are handled. STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, - stbir_edge edge_wrap_mode); + stbir_edge edge_wrap_mode) +{ + return stbir_resize_arbitrary2(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); +} STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, - void *alloc_context); + void *alloc_context) +{ + return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, - void *alloc_context); + void *alloc_context) +{ + return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, - void *alloc_context); + void *alloc_context) +{ + return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, @@ -1805,24 +1849,50 @@ STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, stbir_edge edge_mode_vertical , stbir_filter filter_vertical, - stbir_colorspace space); + stbir_colorspace space, void *alloc_context) +{ + return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, stbir_edge edge_mode_vertical , stbir_filter filter_vertical, - stbir_colorspace space, + stbir_colorspace space, void *alloc_context, float x_scale, float x_offset, - float y_scale, float y_offset); + float y_scale, float y_offset) +{ + float transform[4]; + transform[0] = x_scale; + transform[1] = y_scale; + transform[2] = x_offset; + transform[3] = y_offset; + return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, stbir_edge edge_mode_vertical , stbir_filter filter_vertical, - stbir_colorspace space, - float s0, float t0, float s1, float t1); + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1) +{ + return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} #endif // STB_IMAGE_RESIZE_IMPLEMENTATION From 664d8961f54d490f877d996495150bd2fba4fc06 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 31 Aug 2014 08:34:05 -0700 Subject: [PATCH 093/168] new API partially in-use --- stb_image_resize.h | 51 ++++++++++++++++--------------------------------- tests/resample_test.cpp | 4 ++-- 2 files changed, 18 insertions(+), 37 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 9ed04be..803ed45 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -119,7 +119,7 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels typedef enum { - STBIR_FILTER_DEFAULT = 0, + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses STBIR_FILTER_NEAREST = 1, STBIR_FILTER_BILINEAR = 2, STBIR_FILTER_BICUBIC = 3, // A cubic b spline @@ -194,6 +194,7 @@ STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, stbir_edge edge_mode_vertical , stbir_filter filter_vertical, @@ -203,6 +204,7 @@ STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, stbir_edge edge_mode_vertical , stbir_filter filter_vertical, @@ -1564,8 +1566,8 @@ static int stbir__resize_allocated(stbir__info *stbir_info, stbir_info->alpha_channel = alpha_channel; stbir_info->flags = flags; stbir_info->type = type; - stbir_info->horizontal_filter = h_filter; - stbir_info->vertical_filter = v_filter; + //stbir_info->horizontal_filter = h_filter; + //stbir_info->vertical_filter = v_filter; stbir_info->edge_horizontal = edge_horizontal; stbir_info->edge_vertical = edge_vertical; stbir_info->colorspace = colorspace; @@ -1575,7 +1577,7 @@ static int stbir__resize_allocated(stbir__info *stbir_info, #define STBIR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) - stbir_info->horizontal_contributors = STBIR__NEXT_MEMPTR(stbir_info, sizeof(stbir__info), stbir__contributors); + stbir_info->horizontal_contributors = (stbir__contributors *) tempmem; stbir_info->horizontal_coefficients = STBIR__NEXT_MEMPTR(stbir_info->horizontal_contributors, stbir__get_horizontal_contributors(stbir_info) * sizeof(stbir__contributors), float); stbir_info->vertical_coefficients = STBIR__NEXT_MEMPTR(stbir_info->horizontal_coefficients, stbir__get_total_coefficients(stbir_info) * sizeof(float), float); stbir_info->decode_buffer = STBIR__NEXT_MEMPTR(stbir_info->vertical_coefficients, stbir__get_filter_pixel_width_vertical(stbir_info) * sizeof(float), float); @@ -1620,32 +1622,6 @@ static int stbir__resize_allocated(stbir__info *stbir_info, } -STBIRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_filter filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) -{ - stbir__info info; - int result; - size_t memory_required; - void* extra_memory; - stbir__calculate_transform(&info, input_w, input_h, output_w, output_h, s0,t0,s1,t1,NULL); - stbir__choose_filter(&info, filter, filter); - memory_required = stbir__calculate_memory(&info, input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels); - extra_memory = STBIR_MALLOC(NULL, memory_required); - - if (!extra_memory) - return 0; - - result = stbir__resize_allocated(&info, input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, NULL, - channels, alpha_channel, flags, type, filter, filter, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); - - STBIR_FREE(NULL, extra_memory); - - return result; -} - STBIRDEF int stbir_resize_arbitrary2( void *alloc_context, const void* input_data, int input_w, int input_h, int input_stride_in_bytes, @@ -1675,14 +1651,19 @@ STBIRDEF int stbir_resize_arbitrary2( return result; } - -STBIRDEF int stbir_resize_uint8_srgb(const stbir_uint8* input_data, int input_w, int input_h, - stbir_uint8* output_data, int output_w, int output_h, - int channels, stbir_filter filter, stbir_edge edge) +STBIRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, + int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_filter filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) { - return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBIR_TYPE_UINT8, filter, edge, edge, STBIR_COLORSPACE_SRGB); + return stbir_resize_arbitrary2(NULL, input_data, input_w, input_h, input_stride_in_bytes, + output_data, output_w, output_h, output_stride_in_bytes, + s0,t0,s1,t1,NULL, channels, alpha_channel, flags, type, + filter,filter, edge_horizontal,edge_vertical, colorspace); } + STBIRDEF int stbir_resize_uint16_srgb(const stbir_uint16* input_data, int input_w, int input_h, stbir_uint16* output_data, int output_w, int output_h, int channels, stbir_filter filter, stbir_edge edge) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index b906d15..b06b966 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -278,7 +278,7 @@ void test_channels(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * channels * sizeof(unsigned char)); - stbir_resize_uint8_srgb(channels_data, w, h, output_data, new_w, new_h, channels, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP); + stbir_resize_uint8_srgb(channels_data, w, h, 0, output_data, new_w, new_h, 0, channels); free(channels_data); stbi_image_free(input_data); @@ -431,7 +431,7 @@ void test_subpixel_3() unsigned char output_data_2[32 * 32]; stbir_resize_uint8_subpixel(image, 8, 8, output_data_1, 32, 32, 0, 0, 1, 1, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP); - stbir_resize_uint8_srgb(image, 8, 8, output_data_2, 32, 32, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP); + stbir_resize_uint8(image, 8, 8, 0, output_data_2, 32, 32, 0, 1); for (int x = 0; x < 32; x++) { From 732fec68ee8e4e85251a37f2881d9bae0c003ac1 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 31 Aug 2014 08:55:41 -0700 Subject: [PATCH 094/168] tweak new API, get it partly working --- stb_image_resize.h | 38 ++++++++++++++++++++------------------ tests/resample_test.cpp | 49 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 53 insertions(+), 34 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 803ed45..b497f35 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -143,19 +143,19 @@ typedef enum STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context); STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context); STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context); #define STBIR_ALPHA_CHANNEL_NONE -1 @@ -188,16 +188,16 @@ STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, - stbir_edge edge_mode_vertical , stbir_filter filter_vertical, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context); STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, - stbir_edge edge_mode_vertical , stbir_filter filter_vertical, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, float x_scale, float x_offset, float y_scale, float y_offset); @@ -206,8 +206,8 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, - stbir_edge edge_mode_vertical , stbir_filter filter_vertical, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, 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. @@ -1651,6 +1651,7 @@ STBIRDEF int stbir_resize_arbitrary2( return result; } +#if 0 STBIRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, @@ -1744,6 +1745,7 @@ STBIRDEF int stbir_resize_float_subpixel(const float* input_data, int input_w, i { return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_FLOAT, filter, edge, edge, STBIR_COLORSPACE_SRGB); } +#endif STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, @@ -1789,7 +1791,7 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, @@ -1801,7 +1803,7 @@ STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, @@ -1814,7 +1816,7 @@ STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_colorspace space, stbir_filter filter, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, @@ -1828,8 +1830,8 @@ STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, - stbir_edge edge_mode_vertical , stbir_filter filter_vertical, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context) { return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, @@ -1843,8 +1845,8 @@ STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, - stbir_edge edge_mode_vertical , stbir_filter filter_vertical, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, float x_scale, float x_offset, float y_scale, float y_offset) @@ -1864,8 +1866,8 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_filter filter_horizontal, - stbir_edge edge_mode_vertical , stbir_filter filter_vertical, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, float s0, float t0, float s1, float t1) { diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index b06b966..50e1cde 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -129,7 +129,7 @@ int main(int argc, char** argv) { ftime(&initial_time_millis); for (int i = 0; i < 100; i++) - stbir_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, 0, 0, 1, 1, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary2(NULL, input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); ftime(&final_time_millis); long lapsed_ms = (long)(final_time_millis.time - initial_time_millis.time) * 1000 + (final_time_millis.millitm - initial_time_millis.millitm); printf("Resample: %dms\n", lapsed_ms); @@ -141,7 +141,7 @@ int main(int argc, char** argv) printf("Average: %dms\n", average); #else - stbir_resize_arbitrary(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary2(NULL, input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); #endif stbi_image_free(input_data); @@ -160,7 +160,7 @@ void resize_image(const char* filename, float width_percent, float height_percen unsigned char* input_data = stbi_load(filename, &w, &h, &n, 0); if (!input_data) { - printf("Input image could not be loaded"); + printf("Input image could not be loaded\n"); return; } @@ -169,7 +169,7 @@ void resize_image(const char* filename, float width_percent, float height_percen unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); - stbir_resize_arbitrary(input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, n, -1, 0, STBIR_TYPE_UINT8, filter, edge, edge, colorspace); + stbir_resize_arbitrary2(NULL, input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, filter, filter, edge, edge, colorspace); stbi_image_free(input_data); @@ -192,6 +192,10 @@ void test_format(const char* file, float width_percent, float height_percent, st int w, h, n; unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + if (input_data == NULL) + return; + + int new_w = (int)(w * width_percent); int new_h = (int)(h * height_percent); @@ -200,7 +204,7 @@ void test_format(const char* file, float width_percent, float height_percent, st T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); - stbir_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); + stbir_resize_arbitrary2(NULL, T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -233,6 +237,9 @@ void test_float(const char* file, float width_percent, float height_percent, stb int w, h, n; unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + if (input_data == NULL) + return; + int new_w = (int)(w * width_percent); int new_h = (int)(h * height_percent); @@ -241,7 +248,7 @@ void test_float(const char* file, float width_percent, float height_percent, stb float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); - stbir_resize_arbitrary(T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); + stbir_resize_arbitrary2(NULL, T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -262,6 +269,9 @@ void test_channels(const char* file, float width_percent, float height_percent, int w, h, n; unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + if (input_data == NULL) + return; + int new_w = (int)(w * width_percent); int new_h = (int)(h * height_percent); @@ -295,6 +305,9 @@ void test_subpixel(const char* file, float width_percent, float height_percent, int w, h, n; unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + if (input_data == NULL) + return; + s1 = ((float)w - 1 + s1)/w; t1 = ((float)h - 1 + t1)/h; @@ -303,7 +316,7 @@ void test_subpixel(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbir_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary2(NULL, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); stbi_image_free(input_data); @@ -320,6 +333,10 @@ void test_premul(const char* file) unsigned char* input_data = stbi_load(file, &w, &h, &n, 4); n = 4; + if (input_data == NULL) + return; + + // Set alpha for the top half. for (int x = 0; x < w; x++) { @@ -334,13 +351,13 @@ void test_premul(const char* file) unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbir_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, 3, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary2(NULL, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, 3, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); char output[200]; sprintf(output, "test-output/premul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); - stbir_resize_arbitrary(input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary2(NULL, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); sprintf(output, "test-output/nopremul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); @@ -362,13 +379,13 @@ void test_subpixel_1() unsigned char output_data[16 * 16]; - stbir_resize_arbitrary(image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary2(NULL, image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); unsigned char output_left[8 * 16]; unsigned char output_right[8 * 16]; - stbir_resize_arbitrary(image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); - stbir_resize_arbitrary(image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary2(NULL, image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary2(NULL, image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); for (int x = 0; x < 8; x++) { @@ -407,8 +424,8 @@ void test_subpixel_2() unsigned char output_data_1[16 * 16]; unsigned char output_data_2[16 * 16]; - stbir_resize_arbitrary(image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_COLORSPACE_SRGB); - stbir_resize_arbitrary(large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary2(NULL, image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary2(NULL, large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); {for (int x = 0; x < 16; x++) { @@ -430,7 +447,7 @@ void test_subpixel_3() unsigned char output_data_1[32 * 32]; unsigned char output_data_2[32 * 32]; - stbir_resize_uint8_subpixel(image, 8, 8, output_data_1, 32, 32, 0, 0, 1, 1, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP); + stbir_resize_region(image, 8, 8, 0, output_data_1, 32, 32, 0, STBIR_TYPE_UINT8, 1,0,-1, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_LINEAR, NULL, 0, 0, 1, 1); stbir_resize_uint8(image, 8, 8, 0, output_data_2, 32, 32, 0, 1); for (int x = 0; x < 32; x++) @@ -452,7 +469,7 @@ void test_subpixel_4() unsigned char output[8 * 8]; - stbir_resize_arbitrary(image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); + stbir_resize_arbitrary2(NULL, image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_BILINEAR, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); } From 9a1d34843e524b80fad39d50267b07b064415e23 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 31 Aug 2014 09:10:49 -0700 Subject: [PATCH 095/168] STBIR_EDGE_ZERO --- stb_image_resize.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index b497f35..0c6a5d7 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -94,6 +94,7 @@ typedef enum STBIR_EDGE_CLAMP = 1, STBIR_EDGE_REFLECT = 2, STBIR_EDGE_WRAP = 3, + STBIR_EDGE_ZERO = 4, } stbir_edge; // This function adds the ability to specify how requests to sample off the edge of the image are handled. @@ -593,6 +594,9 @@ static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) { switch (edge) { + case STBIR_EDGE_ZERO: + return 0; + case STBIR_EDGE_CLAMP: if (n < 0) return 0; @@ -600,7 +604,7 @@ static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) if (n >= max) return max - 1; - return n; + return n; // NOTREACHED case STBIR_EDGE_REFLECT: { @@ -621,7 +625,7 @@ static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) return max2 - n - 1; } - return n; + return n; // NOTREACHED } case STBIR_EDGE_WRAP: @@ -636,6 +640,7 @@ static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) return (m); } + return n; // NOTREACHED default: STBIR__UNIMPLEMENTED("Unimplemented edge type"); From 84520de6c47b13b9faa8e4f6b566375d36f22954 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 31 Aug 2014 09:32:17 -0700 Subject: [PATCH 096/168] finish STBI_EDGE_ZERO, untested --- stb_image_resize.h | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 0c6a5d7..b1b0670 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -595,7 +595,7 @@ static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) switch (edge) { case STBIR_EDGE_ZERO: - return 0; + return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later case STBIR_EDGE_CLAMP: if (n < 0) @@ -832,6 +832,16 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) int x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); + // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, + // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO + if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) + { + for (; x < max_x; x++) + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + return; + } + switch (decode) { case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): @@ -947,6 +957,20 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) } } } + + if (edge_horizontal == STBIR_EDGE_ZERO) + { + for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < 0; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + for (x = input_w; x < max_x; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + } } static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) From 07c35180f79380c8341d91755242456c7f146e7b Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 31 Aug 2014 09:45:29 -0700 Subject: [PATCH 097/168] tweak new API --- stb_image_resize.h | 58 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index b1b0670..80dff68 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -81,14 +81,26 @@ STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int inp unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels); -STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); - STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels); + +// +// The following functions interpret image data as gamma-corrected sRGB. +// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, +// or otherwise provide the index of the alpha channel. By default, +// alpha channel is linear even if colors are sRGB. + +#define STBIR_ALPHA_CHANNEL_NONE -1 +#define STBIR_FLAG_PREMULTIPLIED_ALPHA (1 << 0) // If this flag is not set, the specified alpha channel will be multiplied into all other channels before resampling, then divided back out after. +#define STBIR_FLAG_GAMMA_CORRECT_ALPHA (1 << 1) // The specified alpha channel should be handled as gamma-corrected value even when doing sRGB operations. + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags); + + typedef enum { STBIR_EDGE_CLAMP = 1, @@ -100,7 +112,7 @@ typedef enum // This function adds the ability to specify how requests to sample off the edge of the image are handled. STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, + int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode); ////////////////////////////////////////////////////////////////////////////// @@ -109,7 +121,7 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels // // This extends the easy-to-use API as follows: // -// * Alpha-channel can be processed separately +// * 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) @@ -128,9 +140,6 @@ typedef enum STBIR_FILTER_MITCHELL = 5, } stbir_filter; -#define STBIR_FLAG_NONPREMUL_ALPHA (1 << 0) // The specified alpha channel will be multiplied into all other channels before resampling, then divided back out after. -#define STBIR_FLAG_GAMMA_CORRECT_ALPHA (1 << 1) // The specified alpha channel should be handled as gamma-corrected value even when doing sRGB operations. - typedef enum { STBIR_COLORSPACE_LINEAR, @@ -159,7 +168,6 @@ STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context); -#define STBIR_ALPHA_CHANNEL_NONE -1 ////////////////////////////////////////////////////////////////////////////// @@ -942,7 +950,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) break; } - if (stbir_info->flags & STBIR_FLAG_NONPREMUL_ALPHA) + if (!(stbir_info->flags & STBIR_FLAG_PREMULTIPLIED_ALPHA)) { for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++) { @@ -1125,7 +1133,7 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out int n; float divide_alpha = 1; - if (stbir_info->flags&STBIR_FLAG_NONPREMUL_ALPHA) { + if (!(stbir_info->flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) { float alpha = encode_buffer[encode_pixel_index + alpha_channel]; float reciprocal_alpha = alpha ? 1.0f / alpha : 0; for (n = 0; n < channels; n++) @@ -1558,7 +1566,7 @@ static int stbir__resize_allocated(stbir__info *stbir_info, if (alpha_channel < 0) flags = STBIR_FLAG_GAMMA_CORRECT_ALPHA; // this shouldn't be necessary in the long run, but safety for now - if (!(flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA) || (flags&STBIR_FLAG_NONPREMUL_ALPHA)) + if (!(flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA) || !(flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < channels); if (alpha_channel >= channels) @@ -1786,16 +1794,6 @@ STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int inp STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); } -STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels) -{ - return stbir_resize_arbitrary2(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); -} - STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels) @@ -1806,14 +1804,24 @@ STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , i STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); } +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags) +{ + return stbir_resize_arbitrary2(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); +} + STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, + int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode) { return stbir_resize_arbitrary2(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); } From 24c540e1b0bddcfe0753640df365b8a1a564491a Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 31 Aug 2014 10:00:54 -0700 Subject: [PATCH 098/168] rename alpha gamma flag --- stb_image_resize.h | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 80dff68..6ec2919 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -70,7 +70,7 @@ typedef uint32_t stbir_uint32; // * alpha channel is treated identically to other channels. // * colorspace is linear or sRGB as specified by function name // * returned result is 1 for success or 0 in case of an error. -// In the case of an error an assert with be triggered, #define STBIR_ASSERT() to see it. +// #define STBIR_ASSERT() to trigger an assert on parameter validation errors. // * Memory required grows approximately linearly with input and output size, but with // discontinuities at input_w == output_w and input_h == output_h. // * These functions use a "default" resampling filter defined at compile time. To change the filter, @@ -86,15 +86,16 @@ STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , i int num_channels); -// // The following functions interpret image data as gamma-corrected sRGB. // Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, -// or otherwise provide the index of the alpha channel. By default, -// alpha channel is linear even if colors are sRGB. +// or otherwise provide the index of the alpha channel. Flags value +// of 0 will probably do the right thing if you're not sure what +// the flags mean. -#define STBIR_ALPHA_CHANNEL_NONE -1 -#define STBIR_FLAG_PREMULTIPLIED_ALPHA (1 << 0) // If this flag is not set, the specified alpha channel will be multiplied into all other channels before resampling, then divided back out after. -#define STBIR_FLAG_GAMMA_CORRECT_ALPHA (1 << 1) // The specified alpha channel should be handled as gamma-corrected value even when doing sRGB operations. +#define STBIR_ALPHA_CHANNEL_NONE -1 + +#define STBIR_FLAG_PREMULTIPLIED_ALPHA (1 << 0) // If this flag is not set, the specified alpha channel will be multiplied into all other channels before resampling, then divided back out after. +#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) // The specified alpha channel should be handled as gamma-corrected value even when doing sRGB operations. STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, @@ -870,7 +871,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; - if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255; } break; @@ -893,7 +894,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); - if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535; } break; @@ -916,7 +917,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); - if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295); } break; @@ -939,7 +940,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); - if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; } @@ -1152,7 +1153,7 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out for (n = 0; n < channels; n++) ((unsigned char*)output_buffer)[output_pixel_index + n] = stbir__linear_uchar_to_srgb_uchar[(unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 255)]; - if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) ((unsigned char*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); break; @@ -1166,7 +1167,7 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out for (n = 0; n < channels; n++) ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n])) * 65535); - if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) ((unsigned short*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); break; @@ -1180,7 +1181,7 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out for (n = 0; n < channels; n++) ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n]))) * 4294967295); - if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) ((unsigned int*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned int)(((double)stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel])) * 4294967295); break; @@ -1194,7 +1195,7 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out for (n = 0; n < channels; n++) ((float*)output_buffer)[output_pixel_index + n] = stbir__linear_to_srgb(encode_buffer[encode_pixel_index + n]); - if (!(stbir_info->flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA)) + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) ((float*)output_buffer)[output_pixel_index + alpha_channel] = encode_buffer[encode_pixel_index + alpha_channel]; break; @@ -1564,9 +1565,9 @@ static int stbir__resize_allocated(stbir__info *stbir_info, return 0; if (alpha_channel < 0) - flags = STBIR_FLAG_GAMMA_CORRECT_ALPHA; // this shouldn't be necessary in the long run, but safety for now + flags = STBIR_FLAG_ALPHA_USES_COLORSPACE; // this shouldn't be necessary in the long run, but safety for now - if (!(flags&STBIR_FLAG_GAMMA_CORRECT_ALPHA) || !(flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) + if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < channels); if (alpha_channel >= channels) From aee30095c717eae57b9d559538062d8451a4dd0b Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 1 Sep 2014 16:52:04 -0700 Subject: [PATCH 099/168] refactor internal interfaces to avoid passing things multiple times; finish prepping 'stbir__info' even before calculate_memory; get rid of 'noinfo' functions since now calculate_memory doesn't need 'em; add new binary-searched sRGB function (untested) --- stb_image_resize.h | 423 ++++++++++++++++++++---------------------------- tests/resample_test.cpp | 39 +++-- 2 files changed, 200 insertions(+), 262 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 6ec2919..e46cebc 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -19,10 +19,10 @@ supported filters see the stbir_filter enum. To add a new filter, write a filter function and add it to stbir__filter_info_table. - Latest revisions: - 0.50 (2014-07-29) first released version + STBIR_MAX_CHANNELS: defaults to 16, if you need more, bump it up - See end of file for full revision history. + Revisions: + 0.50 (2014-??-??) first released version TODO: Installable filters @@ -209,8 +209,8 @@ STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, - float x_scale, float x_offset, - float y_scale, float y_offset); + float x_scale, float y_scale, + float x_offset, float y_offset); STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, @@ -290,6 +290,10 @@ 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 + // must match stbir_datatype static unsigned char stbir__type_size[] = { 1, // STBIR_TYPE_UINT8 @@ -412,21 +416,44 @@ static float stbir__srgb_uchar_to_linear_float[256] = { 0.982251f, 0.991102f, 1.0f }; -static unsigned char stbir__linear_uchar_to_srgb_uchar[256] = { - 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, - 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, - 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, - 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, - 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, - 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, - 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, - 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, - 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, - 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, - 251, 251, 252, 252, 253, 253, 254, 254, 255 +// 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, }; -float stbir__srgb_to_linear(float f) +static float stbir__srgb_to_linear(float f) { if (f <= 0.04045f) return f / 12.92f; @@ -434,7 +461,7 @@ float stbir__srgb_to_linear(float f) return (float)pow((f + 0.055f) / 1.055f, 2.4f); } -float stbir__linear_to_srgb(float f) +static float stbir__linear_to_srgb(float f) { if (f <= 0.0031308f) return f * 12.92f; @@ -442,7 +469,21 @@ float stbir__linear_to_srgb(float f) return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; } +static unsigned char stbir__linear_to_srgb_uchar(float f) +{ + int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp + int v = 0; + if (x >= stbir__srgb_offset_to_linear_scaled[ v+128 ]) v += 128; + if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 64 ]) v += 64; + if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 32 ]) v += 32; + if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 16 ]) v += 16; + if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 8 ]) v += 8; + if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 4 ]) v += 4; + if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 2 ]) v += 2; + if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 1 ]) v += 1; + return (unsigned char) v; +} static float stbir__filter_nearest(float x) { @@ -502,7 +543,7 @@ static float stbir__filter_mitchell(float x) static stbir__filter_info stbir__filter_info_table[] = { { NULL, 0.0f }, - { stbir__filter_nearest, 0.5f }, + { stbir__filter_nearest, 0.5f }, // 0.000001? { stbir__filter_bilinear, 1.0f }, { stbir__filter_bicubic, 2.0f }, { stbir__filter_catmullrom, 2.0f }, @@ -564,28 +605,18 @@ stbir__inline static int stbir__get_filter_pixel_margin_vertical(stbir__info* st return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->input_h, stbir_info->output_h, stbir_info->vertical_scale) / 2; } -stbir__inline static int stbir__get_horizontal_contributors_noinfo(stbir_filter filter, int input_w, int output_w, float horizontal_scale) +stbir__inline static int stbir__get_horizontal_contributors(stbir__info* info) { - if (stbir__use_upsampling(horizontal_scale)) - return output_w; + if (stbir__use_upsampling(info->horizontal_scale)) + return info->output_w; else - return (input_w + stbir__get_filter_pixel_margin(filter, input_w, output_w, horizontal_scale) * 2); + return (info->input_w + stbir__get_filter_pixel_margin(info->horizontal_filter, info->input_w, info->output_w, info->horizontal_scale) * 2); } -stbir__inline static int stbir__get_horizontal_contributors(stbir__info* stbir_info) +stbir__inline static int stbir__get_total_coefficients(stbir__info* info) { - return stbir__get_horizontal_contributors_noinfo(stbir_info->horizontal_filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); -} - -stbir__inline static int stbir__get_total_coefficients_noinfo(stbir_filter h_filter, int input_w, int output_w, float horizontal_scale) -{ - return stbir__get_horizontal_contributors_noinfo(h_filter, input_w, output_w, horizontal_scale) - * stbir__get_filter_pixel_width (h_filter, input_w, output_w, horizontal_scale); -} - -stbir__inline static int stbir__get_total_coefficients(stbir__info* stbir_info) -{ - return stbir__get_total_coefficients_noinfo(stbir_info->horizontal_filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); + return stbir__get_horizontal_contributors(info) + * stbir__get_filter_pixel_width (info->horizontal_filter, info->input_w, info->output_w, info->horizontal_scale); } stbir__inline static stbir__contributors* stbir__get_contributor(stbir__info* stbir_info, int n) @@ -1151,7 +1182,7 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): for (n = 0; n < channels; n++) - ((unsigned char*)output_buffer)[output_pixel_index + n] = stbir__linear_uchar_to_srgb_uchar[(unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 255)]; + ((unsigned char*)output_buffer)[output_pixel_index + n] = stbir__linear_to_srgb_uchar(encode_buffer[encode_pixel_index + n]); if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) ((unsigned char*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); @@ -1457,22 +1488,36 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info) stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); } -static void stbir__calculate_transform(stbir__info *stbir_info, int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, float *transform) +static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) { + info->input_w = input_w; + info->input_h = input_h; + info->output_w = output_w; + info->output_h = output_h; + info->channels = channels; +} + +static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) +{ + info->s0 = s0; + info->t0 = t0; + info->s1 = s1; + info->t1 = t1; + if (transform) { - stbir_info->horizontal_scale = transform[0]; - stbir_info->vertical_scale = transform[1]; - stbir_info->horizontal_shift = transform[2]; - stbir_info->vertical_shift = transform[3]; + info->horizontal_scale = transform[0]; + info->vertical_scale = transform[1]; + info->horizontal_shift = transform[2]; + info->vertical_shift = transform[3]; } else { - stbir_info->horizontal_scale = ((float)output_w / input_w) / (s1 - s0); - stbir_info->vertical_scale = ((float)output_h / input_h) / (t1 - t0); + info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); + info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); - stbir_info->horizontal_shift = s0 * input_w / (s1 - s0); - stbir_info->vertical_shift = t0 * input_h / (t1 - t0); + info->horizontal_shift = s0 * info->input_w / (s1 - s0); + info->vertical_shift = t0 * info->input_h / (t1 - t0); } } @@ -1486,26 +1531,25 @@ static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir info->vertical_filter = v_filter; } -static stbir_uint32 stbir__calculate_memory(stbir__info *stbir_info, int input_w, int input_h, int output_w, int output_h, float s0, float t0, float s1, float t1, int channels) +static stbir_uint32 stbir__calculate_memory(stbir__info *info) { - int pixel_margin = stbir__get_filter_pixel_margin(stbir_info->horizontal_filter, input_w, output_w, stbir_info->horizontal_scale); - int filter_height = stbir__get_filter_pixel_width(stbir_info->vertical_filter, input_h, output_h, stbir_info->vertical_scale); + int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->input_w, info->output_w, info->horizontal_scale); + int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->input_h, info->output_h, info->vertical_scale); - int info_size = sizeof(stbir__info); - int contributors_size = stbir__get_horizontal_contributors_noinfo(stbir_info->horizontal_filter, input_w, output_w, stbir_info->horizontal_scale) * sizeof(stbir__contributors); - int horizontal_coefficients_size = stbir__get_total_coefficients_noinfo(stbir_info->horizontal_filter, input_w, output_w, stbir_info->horizontal_scale) * sizeof(float); + int contributors_size = stbir__get_horizontal_contributors(info) * sizeof(stbir__contributors); + int horizontal_coefficients_size = stbir__get_total_coefficients(info) * sizeof(float); int vertical_coefficients_size = filter_height * sizeof(float); - int decode_buffer_size = (input_w + pixel_margin*2) * channels * sizeof(float); - int horizontal_buffer_size = output_w * channels * sizeof(float); - int ring_buffer_size = output_w * channels * filter_height * sizeof(float); - int encode_buffer_size = output_w * channels * sizeof(float); + int decode_buffer_size = (info->input_w + pixel_margin*2) * info->channels * sizeof(float); + int horizontal_buffer_size = info->output_w * info->channels * sizeof(float); + int ring_buffer_size = info->output_w * info->channels * filter_height * sizeof(float); + int encode_buffer_size = info->output_w * info->channels * sizeof(float); - STBIR_ASSERT(stbir_info->horizontal_filter != 0); - STBIR_ASSERT(stbir_info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - STBIR_ASSERT(stbir_info->vertical_filter != 0); - STBIR_ASSERT(stbir_info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(info->horizontal_filter != 0); + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(info->vertical_filter != 0); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - if (stbir__use_upsampling(stbir_info->horizontal_scale)) + if (stbir__use_upsampling(info->horizontal_scale)) // The horizontal buffer is for when we're downsampling the height and we // can't output the result of sampling the decode buffer directly into the // ring buffers. @@ -1515,21 +1559,20 @@ static stbir_uint32 stbir__calculate_memory(stbir__info *stbir_info, int input_w // and isn't used when height downsampling. encode_buffer_size = 0; - return info_size + contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size; + return contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size; } -static int stbir__resize_allocated(stbir__info *stbir_info, - const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, float *transform, - int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, stbir_filter h_filter, stbir_filter v_filter, +static int stbir__resize_allocated(stbir__info *info, + const void* input_data, int input_stride_in_bytes, + void* output_data, int output_stride_in_bytes, + int alpha_channel, stbir_uint32 flags, stbir_datatype type, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, void* tempmem, size_t tempmem_size_in_bytes) { - size_t memory_required = stbir__calculate_memory(stbir_info, input_w, input_h, output_w, output_h, s0, t0, s1, t1, channels); + size_t memory_required = stbir__calculate_memory(info); - int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : channels * input_w * stbir__type_size[type]; - int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : channels * output_w * stbir__type_size[type]; + int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; + int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; #ifdef STBIR_DEBUG_OVERWRITE_TEST #define OVERWRITE_ARRAY_SIZE 8 @@ -1545,32 +1588,27 @@ static int stbir__resize_allocated(stbir__info *stbir_info, memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); #endif - STBIR_ASSERT(h_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - STBIR_ASSERT(v_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); + STBIR_ASSERT(info->channels >= 0); - if (h_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; - if (v_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + if (info->channels > STBIR_MAX_CHANNELS || info->channels < 0) return 0; - STBIR_ASSERT(s1 > s0); - STBIR_ASSERT(t1 > t0); + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - if (s1 <= s0 || t1 <= t0) + if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) return 0; - - STBIR_ASSERT(s1 <= 1 && s0 >= 0 && t1 <= 1 && t0 >= 0); - - if (s1 > 1 || s0 < 0 || t1 > 1 || t0 < 0) + if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) return 0; if (alpha_channel < 0) - flags = STBIR_FLAG_ALPHA_USES_COLORSPACE; // this shouldn't be necessary in the long run, but safety for now + flags = STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_PREMULTIPLIED_ALPHA; if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) - STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < channels); + STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); - if (alpha_channel >= channels) + if (alpha_channel >= info->channels) return 0; STBIR_ASSERT(tempmem); @@ -1585,69 +1623,57 @@ static int stbir__resize_allocated(stbir__info *stbir_info, memset(tempmem, 0, tempmem_size_in_bytes); - stbir_info->input_data = input_data; - stbir_info->input_w = input_w; - stbir_info->input_h = input_h; - stbir_info->input_stride_bytes = width_stride_input; + info->input_data = input_data; + info->input_stride_bytes = width_stride_input; - stbir_info->output_data = output_data; - stbir_info->output_w = output_w; - stbir_info->output_h = output_h; - stbir_info->output_stride_bytes = width_stride_output; + info->output_data = output_data; + info->output_stride_bytes = width_stride_output; - stbir_info->s0 = s0; - stbir_info->t0 = t0; - stbir_info->s1 = s1; - stbir_info->t1 = t1; + info->alpha_channel = alpha_channel; + info->flags = flags; + info->type = type; + info->edge_horizontal = edge_horizontal; + info->edge_vertical = edge_vertical; + info->colorspace = colorspace; - stbir_info->channels = channels; - stbir_info->alpha_channel = alpha_channel; - stbir_info->flags = flags; - stbir_info->type = type; - //stbir_info->horizontal_filter = h_filter; - //stbir_info->vertical_filter = v_filter; - stbir_info->edge_horizontal = edge_horizontal; - stbir_info->edge_vertical = edge_vertical; - stbir_info->colorspace = colorspace; - - stbir_info->ring_buffer_length_bytes = output_w * channels * sizeof(float); - stbir_info->decode_buffer_pixels = input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info) * 2; + info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); + info->decode_buffer_pixels = info->input_w + stbir__get_filter_pixel_margin_horizontal(info) * 2; #define STBIR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) - stbir_info->horizontal_contributors = (stbir__contributors *) tempmem; - stbir_info->horizontal_coefficients = STBIR__NEXT_MEMPTR(stbir_info->horizontal_contributors, stbir__get_horizontal_contributors(stbir_info) * sizeof(stbir__contributors), float); - stbir_info->vertical_coefficients = STBIR__NEXT_MEMPTR(stbir_info->horizontal_coefficients, stbir__get_total_coefficients(stbir_info) * sizeof(float), float); - stbir_info->decode_buffer = STBIR__NEXT_MEMPTR(stbir_info->vertical_coefficients, stbir__get_filter_pixel_width_vertical(stbir_info) * sizeof(float), float); + info->horizontal_contributors = (stbir__contributors *) tempmem; + info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, stbir__get_horizontal_contributors(info) * sizeof(stbir__contributors), float); + info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__get_total_coefficients(info) * sizeof(float), float); + info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, stbir__get_filter_pixel_width_vertical(info) * sizeof(float), float); - if (stbir__use_height_upsampling(stbir_info)) + if (stbir__use_height_upsampling(info)) { - stbir_info->horizontal_buffer = NULL; - stbir_info->ring_buffer = STBIR__NEXT_MEMPTR(stbir_info->decode_buffer, stbir_info->decode_buffer_pixels * channels * sizeof(float), float); - stbir_info->encode_buffer = STBIR__NEXT_MEMPTR(stbir_info->ring_buffer, stbir_info->ring_buffer_length_bytes * stbir__get_filter_pixel_width_horizontal(stbir_info), float); + info->horizontal_buffer = NULL; + info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, info->decode_buffer_pixels * info->channels * sizeof(float), float); + info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, info->ring_buffer_length_bytes * stbir__get_filter_pixel_width_horizontal(info), float); - STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(stbir_info->encode_buffer, stbir_info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } else { - stbir_info->horizontal_buffer = STBIR__NEXT_MEMPTR(stbir_info->decode_buffer, stbir_info->decode_buffer_pixels * channels * sizeof(float), float); - stbir_info->ring_buffer = STBIR__NEXT_MEMPTR(stbir_info->horizontal_buffer, output_w * channels * sizeof(float), float); - stbir_info->encode_buffer = NULL; + info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, info->decode_buffer_pixels * info->channels * sizeof(float), float); + info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, info->output_w * info->channels * sizeof(float), float); + info->encode_buffer = NULL; - STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(stbir_info->ring_buffer, stbir_info->ring_buffer_length_bytes * stbir__get_filter_pixel_width_vertical(stbir_info), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, info->ring_buffer_length_bytes * stbir__get_filter_pixel_width_vertical(info), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } #undef STBIR__NEXT_MEMPTR // This signals that the ring buffer is empty - stbir_info->ring_buffer_begin_index = -1; + info->ring_buffer_begin_index = -1; - stbir__calculate_horizontal_filters(stbir_info); + stbir__calculate_horizontal_filters(info); - if (stbir__use_height_upsampling(stbir_info)) - stbir__buffer_loop_upsample(stbir_info); + if (stbir__use_height_upsampling(info)) + stbir__buffer_loop_upsample(info); else - stbir__buffer_loop_downsample(stbir_info); + stbir__buffer_loop_downsample(info); #ifdef STBIR_DEBUG_OVERWRITE_TEST STBIR__DEBUG_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); @@ -1660,7 +1686,7 @@ static int stbir__resize_allocated(stbir__info *stbir_info, } -STBIRDEF int stbir_resize_arbitrary2( +static int stbir_resize_arbitrary( void *alloc_context, const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, @@ -1673,123 +1699,32 @@ STBIRDEF int stbir_resize_arbitrary2( int result; size_t memory_required; void* extra_memory; - stbir__calculate_transform(&info, input_w, input_h, output_w, output_h, s0,t0,s1,t1,transform); + + stbir__setup(&info, input_w, input_h, output_w, output_h, channels); + stbir__calculate_transform(&info, s0,t0,s1,t1,transform); stbir__choose_filter(&info, h_filter, v_filter); - memory_required = stbir__calculate_memory(&info, input_w, input_h, output_w, output_h, 0, 0, 1, 1, channels); + memory_required = stbir__calculate_memory(&info); extra_memory = STBIR_MALLOC(NULL, memory_required); if (!extra_memory) return 0; - result = stbir__resize_allocated(&info, input_data, input_w, input_h, input_stride_in_bytes, output_data, output_w, output_h, output_stride_in_bytes, s0, t0, s1, t1, NULL, - channels, alpha_channel, flags, type, h_filter, v_filter, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); + result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, + output_data, output_stride_in_bytes, + alpha_channel, flags, type, + edge_horizontal, edge_vertical, + colorspace, extra_memory, memory_required); STBIR_FREE(context, extra_memory); return result; } -#if 0 -STBIRDEF int stbir_resize_arbitrary(const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, - int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_filter filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) -{ - return stbir_resize_arbitrary2(NULL, input_data, input_w, input_h, input_stride_in_bytes, - output_data, output_w, output_h, output_stride_in_bytes, - s0,t0,s1,t1,NULL, channels, alpha_channel, flags, type, - filter,filter, edge_horizontal,edge_vertical, colorspace); -} - - -STBIRDEF int stbir_resize_uint16_srgb(const stbir_uint16* input_data, int input_w, int input_h, - stbir_uint16* output_data, int output_w, int output_h, - int channels, stbir_filter filter, stbir_edge edge) -{ - return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBIR_TYPE_UINT16, filter, edge, edge, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint32_srgb(const stbir_uint32* input_data, int input_w, int input_h, - stbir_uint32* output_data, int output_w, int output_h, - int channels, stbir_filter filter, stbir_edge edge) -{ - return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBIR_TYPE_UINT32, filter, edge, edge, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_float_srgb(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - int channels, stbir_filter filter, stbir_edge edge) -{ - return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, 0, 0, STBIR_TYPE_FLOAT, filter, edge, edge, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint8_alphaweighted(const stbir_uint8* input_data, int input_w, int input_h, - stbir_uint8* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace) -{ - return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT8, filter, edge, edge, colorspace); -} - -STBIRDEF int stbir_resize_uint16_alphaweighted(const stbir_uint16* input_data, int input_w, int input_h, - stbir_uint16* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace) -{ - return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT16, filter, edge, edge, colorspace); -} - -STBIRDEF int stbir_resize_uint32_alphaweighted(const stbir_uint32* input_data, int input_w, int input_h, - stbir_uint32* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace) -{ - return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT32, filter, edge, edge, colorspace); -} - -STBIRDEF int stbir_resize_float_alphaweighted(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - int channels, int alpha_channel, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace) -{ - return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, 0, 0, 1, 1, channels, alpha_channel, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_FLOAT, filter, edge, edge, colorspace); -} - -STBIRDEF int stbir_resize_uint8_subpixel(const stbir_uint8* input_data, int input_w, int input_h, - stbir_uint8* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbir_filter filter, stbir_edge edge) -{ - return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_UINT8, filter, edge, edge, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint16_subpixel(const stbir_uint16* input_data, int input_w, int input_h, - stbir_uint16* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbir_filter filter, stbir_edge edge) -{ - return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_UINT16, filter, edge, edge, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint32_subpixel(const stbir_uint32* input_data, int input_w, int input_h, - stbir_uint32* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbir_filter filter, stbir_edge edge) -{ - return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_UINT32, filter, edge, edge, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_float_subpixel(const float* input_data, int input_w, int input_h, - float* output_data, int output_w, int output_h, - float s0, float t0, float s1, float t1, - int channels, stbir_filter filter, stbir_edge edge) -{ - return stbir_resize_arbitrary(input_data, input_w, input_h, 0, output_data, output_w, output_h, 0, s0, t0, s1, t1, channels, 0, 0, STBIR_TYPE_FLOAT, filter, edge, edge, STBIR_COLORSPACE_SRGB); -} -#endif - STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels) { - return stbir_resize_arbitrary2(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir_resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); @@ -1799,7 +1734,7 @@ STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , i float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels) { - return stbir_resize_arbitrary2(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir_resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); @@ -1809,7 +1744,7 @@ STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int inp unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags) { - return stbir_resize_arbitrary2(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir_resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); @@ -1820,7 +1755,7 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode) { - return stbir_resize_arbitrary2(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir_resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); @@ -1832,7 +1767,7 @@ STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { - return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, edge_wrap_mode, edge_wrap_mode, space); @@ -1844,7 +1779,7 @@ STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { - return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, edge_wrap_mode, edge_wrap_mode, space); @@ -1857,7 +1792,7 @@ STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { - return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, edge_wrap_mode, edge_wrap_mode, space); @@ -1872,7 +1807,7 @@ STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context) { - return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, edge_mode_horizontal, edge_mode_vertical, space); @@ -1886,15 +1821,15 @@ STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, - float x_scale, float x_offset, - float y_scale, float y_offset) + float x_scale, float y_scale, + float x_offset, float y_offset) { float transform[4]; transform[0] = x_scale; transform[1] = y_scale; transform[2] = x_offset; transform[3] = y_offset; - return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, edge_mode_horizontal, edge_mode_vertical, space); @@ -1909,16 +1844,10 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int stbir_colorspace space, void *alloc_context, float s0, float t0, float s1, float t1) { - return stbir_resize_arbitrary2(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, edge_mode_horizontal, edge_mode_vertical, space); } #endif // STB_IMAGE_RESIZE_IMPLEMENTATION - -/* -revision history: - 0.50 (2014-07-29) - first released version -*/ diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 50e1cde..e3a1d6d 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -129,7 +129,7 @@ int main(int argc, char** argv) { ftime(&initial_time_millis); for (int i = 0; i < 100; i++) - stbir_resize_arbitrary2(NULL, input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(NULL, input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); ftime(&final_time_millis); long lapsed_ms = (long)(final_time_millis.time - initial_time_millis.time) * 1000 + (final_time_millis.millitm - initial_time_millis.millitm); printf("Resample: %dms\n", lapsed_ms); @@ -141,7 +141,7 @@ int main(int argc, char** argv) printf("Average: %dms\n", average); #else - stbir_resize_arbitrary2(NULL, input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(NULL, input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); #endif stbi_image_free(input_data); @@ -169,7 +169,7 @@ void resize_image(const char* filename, float width_percent, float height_percen unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); - stbir_resize_arbitrary2(NULL, input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, filter, filter, edge, edge, colorspace); + stbir_resize_arbitrary(NULL, input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, filter, filter, edge, edge, colorspace); stbi_image_free(input_data); @@ -204,7 +204,7 @@ void test_format(const char* file, float width_percent, float height_percent, st T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); - stbir_resize_arbitrary2(NULL, T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); + stbir_resize_arbitrary(NULL, T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -248,7 +248,7 @@ void test_float(const char* file, float width_percent, float height_percent, stb float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); - stbir_resize_arbitrary2(NULL, T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); + stbir_resize_arbitrary(NULL, T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -288,7 +288,7 @@ void test_channels(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * channels * sizeof(unsigned char)); - stbir_resize_uint8_srgb(channels_data, w, h, 0, output_data, new_w, new_h, 0, channels); + stbir_resize_uint8_srgb(channels_data, w, h, 0, output_data, new_w, new_h, 0, channels, -1,0); free(channels_data); stbi_image_free(input_data); @@ -316,7 +316,7 @@ void test_subpixel(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbir_resize_arbitrary2(NULL, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(NULL, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); stbi_image_free(input_data); @@ -351,13 +351,13 @@ void test_premul(const char* file) unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbir_resize_arbitrary2(NULL, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, 3, STBIR_FLAG_NONPREMUL_ALPHA, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(NULL, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, 3, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); char output[200]; sprintf(output, "test-output/premul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); - stbir_resize_arbitrary2(NULL, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(NULL, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); sprintf(output, "test-output/nopremul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); @@ -379,13 +379,13 @@ void test_subpixel_1() unsigned char output_data[16 * 16]; - stbir_resize_arbitrary2(NULL, image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(NULL, image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); unsigned char output_left[8 * 16]; unsigned char output_right[8 * 16]; - stbir_resize_arbitrary2(NULL, image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); - stbir_resize_arbitrary2(NULL, image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(NULL, image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(NULL, image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); for (int x = 0; x < 8; x++) { @@ -424,8 +424,8 @@ void test_subpixel_2() unsigned char output_data_1[16 * 16]; unsigned char output_data_2[16 * 16]; - stbir_resize_arbitrary2(NULL, image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_COLORSPACE_SRGB); - stbir_resize_arbitrary2(NULL, large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(NULL, image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(NULL, large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); {for (int x = 0; x < 16; x++) { @@ -469,7 +469,7 @@ void test_subpixel_4() unsigned char output[8 * 8]; - stbir_resize_arbitrary2(NULL, image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_BILINEAR, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); + stbir_resize_arbitrary(NULL, image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_BILINEAR, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); } @@ -477,6 +477,15 @@ void test_suite() { int 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_subpixel_1(); test_subpixel_2(); test_subpixel_3(); From 952c26e626144aef43ae052503fcd1c5d979230c Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 1 Sep 2014 19:29:28 -0700 Subject: [PATCH 100/168] inline stbir__encode_pixel into stbir__encode_scanline --- stb_image_resize.h | 130 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 42 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index e46cebc..c68a778 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1160,75 +1160,131 @@ static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buff } -static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* output_buffer, int output_pixel_index, float* encode_buffer, int encode_pixel_index, int channels, int alpha_channel, int decode) +// @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; int n; - float divide_alpha = 1; - if (!(stbir_info->flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) { - float alpha = encode_buffer[encode_pixel_index + alpha_channel]; - float reciprocal_alpha = alpha ? 1.0f / alpha : 0; - for (n = 0; n < channels; n++) - if (n != alpha_channel) - encode_buffer[encode_pixel_index + n] *= reciprocal_alpha; +// stbir__encode_pixel(stbir_info, output_buffer, x*channels, encode_buffer, x*channels, channels, alpha_channel, decode); + + if (!(stbir_info->flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) + { + for (x=0; x < num_pixels; ++x) + { + int output_pixel_index = x*channels; + int encode_pixel_index = x*channels; + float alpha = encode_buffer[encode_pixel_index + alpha_channel]; + float reciprocal_alpha = alpha ? 1.0f / alpha : 0; + // @TODO: if final alpha=0, we actually want to have ignored alpha... set alpha to sRGB_to_linear(1/255)/(2^24) so floats will discard it? + for (n = 0; n < channels; n++) + if (n != alpha_channel) + encode_buffer[encode_pixel_index + n] *= reciprocal_alpha; + } } switch (decode) { case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (n = 0; n < channels; n++) - ((unsigned char*)output_buffer)[output_pixel_index + n] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 255); + for (x=0; x < num_pixels; ++x) + { + int output_pixel_index = x*channels; + int encode_pixel_index = x*channels; + + for (n = 0; n < channels; n++) + ((unsigned char*)output_buffer)[output_pixel_index + n] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 255); + } break; case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (n = 0; n < channels; n++) - ((unsigned char*)output_buffer)[output_pixel_index + n] = stbir__linear_to_srgb_uchar(encode_buffer[encode_pixel_index + n]); + for (x=0; x < num_pixels; ++x) + { + int output_pixel_index = x*channels; + int encode_pixel_index = x*channels; - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned char*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); + for (n = 0; n < channels; n++) + ((unsigned char*)output_buffer)[output_pixel_index + n] = stbir__linear_to_srgb_uchar(encode_buffer[encode_pixel_index + n]); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned char*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); + } break; case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (n = 0; n < channels; n++) - ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 65535); + for (x=0; x < num_pixels; ++x) + { + int output_pixel_index = x*channels; + int encode_pixel_index = x*channels; + + for (n = 0; n < channels; n++) + ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 65535); + } break; case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (n = 0; n < channels; n++) - ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n])) * 65535); + for (x=0; x < num_pixels; ++x) + { + int output_pixel_index = x*channels; + int encode_pixel_index = x*channels; - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned short*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); + for (n = 0; n < channels; n++) + ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n])) * 65535); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned short*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); + } break; case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbir__saturate(encode_buffer[encode_pixel_index + n])) * 4294967295); + for (x=0; x < num_pixels; ++x) + { + int output_pixel_index = x*channels; + int encode_pixel_index = x*channels; + + for (n = 0; n < channels; n++) + ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbir__saturate(encode_buffer[encode_pixel_index + n])) * 4294967295); + } break; case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n]))) * 4294967295); + for (x=0; x < num_pixels; ++x) + { + int output_pixel_index = x*channels; + int encode_pixel_index = x*channels; - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned int*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned int)(((double)stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel])) * 4294967295); + for (n = 0; n < channels; n++) + ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n]))) * 4294967295); + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned int*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned int)(((double)stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel])) * 4294967295); + } break; case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (n = 0; n < channels; n++) - ((float*)output_buffer)[output_pixel_index + n] = encode_buffer[encode_pixel_index + n]; + for (x=0; x < num_pixels; ++x) + { + int output_pixel_index = x*channels; + int encode_pixel_index = x*channels; + + for (n = 0; n < channels; n++) + ((float*)output_buffer)[output_pixel_index + n] = encode_buffer[encode_pixel_index + n]; + } break; case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (n = 0; n < channels; n++) - ((float*)output_buffer)[output_pixel_index + n] = stbir__linear_to_srgb(encode_buffer[encode_pixel_index + n]); + for (x=0; x < num_pixels; ++x) + { + int output_pixel_index = x*channels; + int encode_pixel_index = x*channels; - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((float*)output_buffer)[output_pixel_index + alpha_channel] = encode_buffer[encode_pixel_index + alpha_channel]; + for (n = 0; n < channels; n++) + ((float*)output_buffer)[output_pixel_index + n] = stbir__linear_to_srgb(encode_buffer[encode_pixel_index + n]); + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((float*)output_buffer)[output_pixel_index + alpha_channel] = encode_buffer[encode_pixel_index + alpha_channel]; + } break; default: @@ -1237,16 +1293,6 @@ static stbir__inline void stbir__encode_pixel(stbir__info* stbir_info, void* out } } -// @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; - for (x=0; x < num_pixels; ++x) - { - stbir__encode_pixel(stbir_info, output_buffer, x*channels, encode_buffer, x*channels, channels, alpha_channel, decode); - } -} - static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) { int x, k; From 75bdd2da83f30bf95f71d8ba6f1d65a4bd5ca980 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 6 Sep 2014 08:48:46 -0700 Subject: [PATCH 101/168] Fix malloc context and whitespace. --- stb_image_resize.h | 18 +++++++------- tests/resample_test.cpp | 66 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index c68a778..8cab4c9 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -199,7 +199,7 @@ STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context); STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, @@ -207,7 +207,7 @@ STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, float x_scale, float y_scale, float x_offset, float y_offset); @@ -217,7 +217,7 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, 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. @@ -1750,7 +1750,7 @@ static int stbir_resize_arbitrary( stbir__calculate_transform(&info, s0,t0,s1,t1,transform); stbir__choose_filter(&info, h_filter, v_filter); memory_required = stbir__calculate_memory(&info); - extra_memory = STBIR_MALLOC(NULL, memory_required); + extra_memory = STBIR_MALLOC(alloc_context, memory_required); if (!extra_memory) return 0; @@ -1759,9 +1759,9 @@ static int stbir_resize_arbitrary( output_data, output_stride_in_bytes, alpha_channel, flags, type, edge_horizontal, edge_vertical, - colorspace, extra_memory, memory_required); + colorspace, extra_memory, memory_required); - STBIR_FREE(context, extra_memory); + STBIR_FREE(alloc_context, extra_memory); return result; } @@ -1850,7 +1850,7 @@ STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context) { return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, @@ -1865,7 +1865,7 @@ STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, float x_scale, float y_scale, float x_offset, float y_offset) @@ -1886,7 +1886,7 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, float s0, float t0, float s1, float t1) { diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index e3a1d6d..ae420f9 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -1,3 +1,5 @@ +#include + #if defined(_WIN32) && _MSC_VER > 1200 #define STBIR_ASSERT(x) \ if (!(x)) { \ @@ -8,6 +10,42 @@ #define STBIR_ASSERT(x) assert(x) #endif +#define STBIR_MALLOC stbir_malloc +#define STBIR_FREE stbir_free + +class stbir_context { +public: + stbir_context() + { + size = 1000000; + memory = malloc(size); + } + + ~stbir_context() + { + free(memory); + } + + size_t size; + void* memory; +} g_context; + +void* stbir_malloc(void* context, size_t size) +{ + if (!context) + return 0; + + stbir_context* real_context = (stbir_context*)context; + if (size > real_context->size) + return 0; + + return real_context->memory; +} + +void stbir_free(void* context, void* memory) +{ +} + #define STB_IMAGE_RESIZE_IMPLEMENTATION #define STB_IMAGE_RESIZE_STATIC #include "stb_image_resize.h" @@ -129,7 +167,7 @@ int main(int argc, char** argv) { ftime(&initial_time_millis); for (int i = 0; i < 100; i++) - stbir_resize_arbitrary(NULL, input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(&g_context, input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); ftime(&final_time_millis); long lapsed_ms = (long)(final_time_millis.time - initial_time_millis.time) * 1000 + (final_time_millis.millitm - initial_time_millis.millitm); printf("Resample: %dms\n", lapsed_ms); @@ -141,7 +179,7 @@ int main(int argc, char** argv) printf("Average: %dms\n", average); #else - stbir_resize_arbitrary(NULL, input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(&g_context, input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); #endif stbi_image_free(input_data); @@ -169,7 +207,7 @@ void resize_image(const char* filename, float width_percent, float height_percen unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); - stbir_resize_arbitrary(NULL, input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, filter, filter, edge, edge, colorspace); + stbir_resize_arbitrary(&g_context, input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, filter, filter, edge, edge, colorspace); stbi_image_free(input_data); @@ -204,7 +242,7 @@ void test_format(const char* file, float width_percent, float height_percent, st T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); - stbir_resize_arbitrary(NULL, T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); + stbir_resize_arbitrary(&g_context, T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -248,7 +286,7 @@ void test_float(const char* file, float width_percent, float height_percent, stb float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); - stbir_resize_arbitrary(NULL, T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); + stbir_resize_arbitrary(&g_context, T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); free(T_data); stbi_image_free(input_data); @@ -316,7 +354,7 @@ void test_subpixel(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbir_resize_arbitrary(NULL, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(&g_context, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); stbi_image_free(input_data); @@ -351,13 +389,13 @@ void test_premul(const char* file) unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbir_resize_arbitrary(NULL, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, 3, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(&g_context, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, 3, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); char output[200]; sprintf(output, "test-output/premul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); - stbir_resize_arbitrary(NULL, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(&g_context, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); sprintf(output, "test-output/nopremul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); @@ -379,13 +417,13 @@ void test_subpixel_1() unsigned char output_data[16 * 16]; - stbir_resize_arbitrary(NULL, image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(&g_context, image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); unsigned char output_left[8 * 16]; unsigned char output_right[8 * 16]; - stbir_resize_arbitrary(NULL, image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); - stbir_resize_arbitrary(NULL, image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(&g_context, image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(&g_context, image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); for (int x = 0; x < 8; x++) { @@ -424,8 +462,8 @@ void test_subpixel_2() unsigned char output_data_1[16 * 16]; unsigned char output_data_2[16 * 16]; - stbir_resize_arbitrary(NULL, image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_COLORSPACE_SRGB); - stbir_resize_arbitrary(NULL, large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(&g_context, image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_COLORSPACE_SRGB); + stbir_resize_arbitrary(&g_context, large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); {for (int x = 0; x < 16; x++) { @@ -469,7 +507,7 @@ void test_subpixel_4() unsigned char output[8 * 8]; - stbir_resize_arbitrary(NULL, image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_BILINEAR, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); + stbir_resize_arbitrary(&g_context, image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_BILINEAR, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); } From fb059fcece709d67c1fe910b04da5fda92a64dca Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 6 Sep 2014 10:57:21 -0700 Subject: [PATCH 102/168] Progress report. --- stb_image_resize.h | 17 +++++++++++++++++ tests/resample_test.cpp | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/stb_image_resize.h b/stb_image_resize.h index 8cab4c9..42077bb 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -223,6 +223,19 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int // (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 + // // //// end header file ///////////////////////////////////////////////////// @@ -1447,6 +1460,8 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info) // Now all buffers should be ready to write a row of vertical sampling. stbir__resample_vertical_upsample(stbir_info, y, in_first_scanline, in_last_scanline, in_center_of_out); + + STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); } } @@ -1529,6 +1544,8 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info) // Now the horizontal buffer is ready to write to all ring buffer rows. stbir__resample_vertical_downsample(stbir_info, y, out_first_scanline, out_last_scanline, out_center_of_in); + + STBIR_PROGRESS_REPORT((float)(y + stbir__get_filter_pixel_margin_vertical(stbir_info)) / (max_y + stbir__get_filter_pixel_margin_vertical(stbir_info))); } stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index ae420f9..738177c 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -46,6 +46,13 @@ void stbir_free(void* context, void* memory) { } +void stbir_progress(float p) +{ + STBIR_ASSERT(p >= 0 && p <= 1); +} + +#define STBIR_PROGRESS_REPORT stbir_progress + #define STB_IMAGE_RESIZE_IMPLEMENTATION #define STB_IMAGE_RESIZE_STATIC #include "stb_image_resize.h" From 41555b5d53a72fa076aefe525f87de72fe9f981a Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 6 Sep 2014 14:58:32 -0700 Subject: [PATCH 103/168] update test cases to work on things other than barbara.png --- stb_image_resize.h | 9 +-- tests/resample_test.cpp | 168 +++++++++++++++++++++++++----------------------- tests/resize.dsp | 4 ++ 3 files changed, 98 insertions(+), 83 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index c68a778..aa7fa5f 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1,3 +1,5 @@ +#define STBIR_DEBUG_OVERWRITE_TEST + /* stb_image_resize - v0.50 - public domain image resampling no warranty implied; use at your own risk @@ -627,7 +629,8 @@ stbir__inline static stbir__contributors* stbir__get_contributor(stbir__info* st stbir__inline static float* stbir__get_coefficient(stbir__info* stbir_info, int n, int c) { - return &stbir_info->horizontal_coefficients[stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale)*n + c]; + int width = stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); + return &stbir_info->horizontal_coefficients[width*n + c]; } static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) @@ -1166,8 +1169,6 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void int x; int n; -// stbir__encode_pixel(stbir_info, output_buffer, x*channels, encode_buffer, x*channels, channels, alpha_channel, decode); - if (!(stbir_info->flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) { for (x=0; x < num_pixels; ++x) @@ -1627,7 +1628,7 @@ static int stbir__resize_allocated(stbir__info *info, unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; - stbir_size_t begin_forbidden = width_stride_output * (output_h - 1) + output_w * channels * stbir__type_size[type]; + size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index e3a1d6d..fc30925 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -22,6 +22,8 @@ #include #endif +#include + #define MT_SIZE 624 static size_t g_aiMT[MT_SIZE]; static size_t g_iMTI = 0; @@ -74,7 +76,7 @@ inline float mtfrand() } -void test_suite(); +void test_suite(int argc, char **argv); int main(int argc, char** argv) { @@ -85,7 +87,7 @@ int main(int argc, char** argv) int out_w, out_h, out_stride; #if 1 - test_suite(); + test_suite(argc, argv); return 0; #endif @@ -473,9 +475,17 @@ void test_subpixel_4() STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); } -void test_suite() +void test_suite(int argc, char **argv) { int i; + char *barbara; + + _mkdir("test-output"); + + if (argc > 1) + barbara = argv[1]; + else + barbara = "barbara.png"; #if 0 // linear_to_srgb_uchar table for (i=0; i < 256; ++i) { @@ -491,30 +501,93 @@ void test_suite() test_subpixel_3(); test_subpixel_4(); - test_premul("barbara.png"); + test_premul(barbara); for (i = 0; i < 10; i++) - test_subpixel("barbara.png", 0.5f, 0.5f, (float)i / 10, 1); + test_subpixel(barbara, 0.5f, 0.5f, (float)i / 10, 1); for (i = 0; i < 10; i++) - test_subpixel("barbara.png", 0.5f, 0.5f, 1, (float)i / 10); + test_subpixel(barbara, 0.5f, 0.5f, 1, (float)i / 10); for (i = 0; i < 10; i++) - test_subpixel("barbara.png", 2, 2, (float)i / 10, 1); + test_subpixel(barbara, 2, 2, (float)i / 10, 1); for (i = 0; i < 10; i++) - test_subpixel("barbara.png", 2, 2, 1, (float)i / 10); + test_subpixel(barbara, 2, 2, 1, (float)i / 10); // Channels test - test_channels("barbara.png", 0.5f, 0.5f, 1); - test_channels("barbara.png", 0.5f, 0.5f, 2); - test_channels("barbara.png", 0.5f, 0.5f, 3); - test_channels("barbara.png", 0.5f, 0.5f, 4); + test_channels(barbara, 0.5f, 0.5f, 1); + test_channels(barbara, 0.5f, 0.5f, 2); + test_channels(barbara, 0.5f, 0.5f, 3); + test_channels(barbara, 0.5f, 0.5f, 4); - test_channels("barbara.png", 2, 2, 1); - test_channels("barbara.png", 2, 2, 2); - test_channels("barbara.png", 2, 2, 3); - test_channels("barbara.png", 2, 2, 4); + test_channels(barbara, 2, 2, 1); + test_channels(barbara, 2, 2, 2); + test_channels(barbara, 2, 2, 3); + test_channels(barbara, 2, 2, 4); + + // filter tests + resize_image(barbara, 2, 2, STBIR_FILTER_NEAREST, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-nearest.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_BICUBIC, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-catmullrom.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_MITCHELL, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-mitchell.png"); + + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_NEAREST, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-nearest.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BICUBIC, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_MITCHELL, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); + + for (i = 10; i < 100; i++) + { + char outname[200]; + sprintf(outname, "test-output/barbara-width-%d.jpg", i); + resize_image(barbara, (float)i / 100, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); + } + + for (i = 110; i < 500; i += 10) + { + char outname[200]; + sprintf(outname, "test-output/barbara-width-%d.jpg", i); + resize_image(barbara, (float)i / 100, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); + } + + for (i = 10; i < 100; i++) + { + char outname[200]; + sprintf(outname, "test-output/barbara-height-%d.jpg", i); + resize_image(barbara, 1, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); + } + + for (i = 110; i < 500; i += 10) + { + char outname[200]; + sprintf(outname, "test-output/barbara-height-%d.jpg", i); + resize_image(barbara, 1, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); + } + + for (i = 50; i < 200; i += 10) + { + char outname[200]; + sprintf(outname, "test-output/barbara-width-height-%d.jpg", i); + resize_image(barbara, 100 / (float)i, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); + } + + test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB); + test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR); + test_format(barbara, 2.0, 0.5, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB); + test_format(barbara, 2.0, 0.5, STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR); + + test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB); + test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR); + test_format(barbara, 2.0, 0.5, STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB); + test_format(barbara, 2.0, 0.5, STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR); + + test_float(barbara, 0.5, 2.0, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB); + test_float(barbara, 0.5, 2.0, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR); + test_float(barbara, 2.0, 0.5, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB); + test_float(barbara, 2.0, 0.5, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR); // Edge behavior tests resize_image("hgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR, "test-output/hgradient-clamp.png"); @@ -530,69 +603,6 @@ void test_suite() resize_image("gamma_colors.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_colors.jpg"); resize_image("gamma_2.2.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_2.2.jpg"); resize_image("gamma_dalai_lama_gray.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_dalai_lama_gray.jpg"); - - // filter tests - resize_image("barbara.png", 2, 2, STBIR_FILTER_NEAREST, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-nearest.png"); - resize_image("barbara.png", 2, 2, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); - resize_image("barbara.png", 2, 2, STBIR_FILTER_BICUBIC, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); - resize_image("barbara.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-catmullrom.png"); - resize_image("barbara.png", 2, 2, STBIR_FILTER_MITCHELL, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-mitchell.png"); - - resize_image("barbara.png", 0.5f, 0.5f, STBIR_FILTER_NEAREST, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-nearest.png"); - resize_image("barbara.png", 0.5f, 0.5f, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); - resize_image("barbara.png", 0.5f, 0.5f, STBIR_FILTER_BICUBIC, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); - resize_image("barbara.png", 0.5f, 0.5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png"); - resize_image("barbara.png", 0.5f, 0.5f, STBIR_FILTER_MITCHELL, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); - - for (i = 10; i < 100; i++) - { - char outname[200]; - sprintf(outname, "test-output/barbara-width-%d.jpg", i); - resize_image("barbara.png", (float)i / 100, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); - } - - for (i = 110; i < 500; i += 10) - { - char outname[200]; - sprintf(outname, "test-output/barbara-width-%d.jpg", i); - resize_image("barbara.png", (float)i / 100, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); - } - - for (i = 10; i < 100; i++) - { - char outname[200]; - sprintf(outname, "test-output/barbara-height-%d.jpg", i); - resize_image("barbara.png", 1, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); - } - - for (i = 110; i < 500; i += 10) - { - char outname[200]; - sprintf(outname, "test-output/barbara-height-%d.jpg", i); - resize_image("barbara.png", 1, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); - } - - for (i = 50; i < 200; i += 10) - { - char outname[200]; - sprintf(outname, "test-output/barbara-width-height-%d.jpg", i); - resize_image("barbara.png", 100 / (float)i, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); - } - - test_format("barbara.png", 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB); - test_format("barbara.png", 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR); - test_format("barbara.png", 2.0, 0.5, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB); - test_format("barbara.png", 2.0, 0.5, STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR); - - test_format("barbara.png", 0.5, 2.0, STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB); - test_format("barbara.png", 0.5, 2.0, STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR); - test_format("barbara.png", 2.0, 0.5, STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB); - test_format("barbara.png", 2.0, 0.5, STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR); - - test_float("barbara.png", 0.5, 2.0, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB); - test_float("barbara.png", 0.5, 2.0, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR); - test_float("barbara.png", 2.0, 0.5, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB); - test_float("barbara.png", 2.0, 0.5, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR); } diff --git a/tests/resize.dsp b/tests/resize.dsp index cf40689..df52874 100644 --- a/tests/resize.dsp +++ b/tests/resize.dsp @@ -84,5 +84,9 @@ LINK32=link.exe SOURCE=.\resample_test.cpp # End Source File +# Begin Source File + +SOURCE=..\stb_image_resize.h +# End Source File # End Target # End Project From 38ce5494bc895ee75b07da918cd73358a4db96d3 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 6 Sep 2014 20:17:19 -0700 Subject: [PATCH 104/168] Clarify some comments. Make stbir__resize_arbitrary an internal function. Update test cases to use actual API functions. --- stb_image_resize.h | 33 +++++++++++++++++++-------------- tests/resample_test.cpp | 44 ++++++++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 42077bb..fe4a16c 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -94,8 +94,13 @@ STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , i #define STBIR_ALPHA_CHANNEL_NONE -1 -#define STBIR_FLAG_PREMULTIPLIED_ALPHA (1 << 0) // If this flag is not set, the specified alpha channel will be multiplied into all other channels before resampling, then divided back out after. -#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) // The specified alpha channel should be handled as gamma-corrected value even when doing sRGB operations. +// 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) +// The specified alpha channel should be handled as gamma-corrected value even +// when doing sRGB operations. +#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, @@ -1666,7 +1671,7 @@ 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_PREMULTIPLIED_ALPHA; if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); @@ -1749,7 +1754,7 @@ static int stbir__resize_allocated(stbir__info *info, } -static int stbir_resize_arbitrary( +static int stbir__resize_arbitrary( void *alloc_context, const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, @@ -1787,7 +1792,7 @@ STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int inp unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels) { - return stbir_resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); @@ -1797,7 +1802,7 @@ STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , i float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels) { - return stbir_resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); @@ -1807,7 +1812,7 @@ STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int inp unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags) { - return stbir_resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); @@ -1818,7 +1823,7 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode) { - return stbir_resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); @@ -1830,7 +1835,7 @@ STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { - return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, edge_wrap_mode, edge_wrap_mode, space); @@ -1842,7 +1847,7 @@ STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { - return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, edge_wrap_mode, edge_wrap_mode, space); @@ -1855,7 +1860,7 @@ STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { - return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, edge_wrap_mode, edge_wrap_mode, space); @@ -1870,7 +1875,7 @@ STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context) { - return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, edge_mode_horizontal, edge_mode_vertical, space); @@ -1892,7 +1897,7 @@ STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int transform[1] = y_scale; transform[2] = x_offset; transform[3] = y_offset; - return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, edge_mode_horizontal, edge_mode_vertical, space); @@ -1907,7 +1912,7 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int stbir_colorspace space, void *alloc_context, float s0, float t0, float s1, float t1) { - return stbir_resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, edge_mode_horizontal, edge_mode_vertical, space); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 738177c..cbd5956 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -174,7 +174,7 @@ int main(int argc, char** argv) { ftime(&initial_time_millis); for (int i = 0; i < 100; i++) - stbir_resize_arbitrary(&g_context, input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, STBIR_TYPE_UINT8, n, n - 1, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); ftime(&final_time_millis); long lapsed_ms = (long)(final_time_millis.time - initial_time_millis.time) * 1000 + (final_time_millis.millitm - initial_time_millis.millitm); printf("Resample: %dms\n", lapsed_ms); @@ -185,13 +185,21 @@ int main(int argc, char** argv) average /= 10; printf("Average: %dms\n", average); -#else - stbir_resize_arbitrary(&g_context, input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, s0, t0, s1, t1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); -#endif stbi_image_free(input_data); stbi_write_png("output.png", out_w, out_h, n, output_data, out_stride); +#else + stbir_resize_region(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, STBIR_TYPE_UINT8, n, n-1, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, s0, t0, s1, t1); + + stbi_write_png("output-region.png", out_w, out_h, n, output_data, out_stride); + + stbir_resize_subpixel(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, STBIR_TYPE_UINT8, n, n-1, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, in_w*s0, in_h*t0, 0.5f, 0.5f); + + stbi_write_png("output-subpixel.png", out_w, out_h, n, output_data, out_stride); + + stbi_image_free(input_data); +#endif free(output_data); @@ -214,7 +222,7 @@ void resize_image(const char* filename, float width_percent, float height_percen unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); - stbir_resize_arbitrary(&g_context, input_data, w, h, 0, output_data, out_w, out_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, filter, filter, edge, edge, colorspace); + stbir_resize(input_data, w, h, 0, output_data, out_w, out_h, 0, STBIR_TYPE_UINT8, n, 0, 0, edge, edge, filter, filter, colorspace, &g_context); stbi_image_free(input_data); @@ -249,7 +257,7 @@ void test_format(const char* file, float width_percent, float height_percent, st T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); - stbir_resize_arbitrary(&g_context, T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); + stbir_resize(T_data, w, h, 0, output_data, new_w, new_h, 0, type, n, 0, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, colorspace, &g_context); free(T_data); stbi_image_free(input_data); @@ -293,7 +301,7 @@ void test_float(const char* file, float width_percent, float height_percent, stb float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); - stbir_resize_arbitrary(&g_context, T_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, type, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, colorspace); + stbir_resize_float_generic(T_data, w, h, 0, output_data, new_w, new_h, 0, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, colorspace, &g_context); free(T_data); stbi_image_free(input_data); @@ -333,7 +341,7 @@ void test_channels(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * channels * sizeof(unsigned char)); - stbir_resize_uint8_srgb(channels_data, w, h, 0, output_data, new_w, new_h, 0, channels, -1,0); + stbir_resize_uint8_srgb(channels_data, w, h, 0, output_data, new_w, new_h, 0, channels, STBIR_ALPHA_CHANNEL_NONE, 0); free(channels_data); stbi_image_free(input_data); @@ -361,7 +369,7 @@ void test_subpixel(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbir_resize_arbitrary(&g_context, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, s1, t1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_region(input_data, w, h, 0, output_data, new_w, new_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0, 0, s1, t1); stbi_image_free(input_data); @@ -396,13 +404,13 @@ void test_premul(const char* file) unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbir_resize_arbitrary(&g_context, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, 3, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_uint8_generic(input_data, w, h, 0, output_data, new_w, new_h, 0, n, n - 1, 0, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); char output[200]; sprintf(output, "test-output/premul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); - stbir_resize_arbitrary(&g_context, input_data, w, h, 0, output_data, new_w, new_h, 0, 0, 0, 1, 1, NULL, n, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_uint8_generic(input_data, w, h, 0, output_data, new_w, new_h, 0, n, n - 1, STBIR_FLAG_PREMULTIPLIED_ALPHA, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); sprintf(output, "test-output/nopremul-%s", file); stbi_write_png(output, new_w, new_h, n, output_data, 0); @@ -424,13 +432,13 @@ void test_subpixel_1() unsigned char output_data[16 * 16]; - stbir_resize_arbitrary(&g_context, image, 8, 8, 0, output_data, 16, 16, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_region(image, 8, 8, 0, output_data, 16, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0, 0, 1, 1); unsigned char output_left[8 * 16]; unsigned char output_right[8 * 16]; - stbir_resize_arbitrary(&g_context, image, 8, 8, 0, output_left, 8, 16, 0, 0, 0, 0.5f, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); - stbir_resize_arbitrary(&g_context, image, 8, 8, 0, output_right, 8, 16, 0, 0.5f, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize_region(image, 8, 8, 0, output_left, 8, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0, 0, 0.5f, 1); + stbir_resize_region(image, 8, 8, 0, output_right, 8, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0.5f, 0, 1, 1); for (int x = 0; x < 8; x++) { @@ -469,8 +477,8 @@ void test_subpixel_2() unsigned char output_data_1[16 * 16]; unsigned char output_data_2[16 * 16]; - stbir_resize_arbitrary(&g_context, image, 8, 8, 0, output_data_1, 16, 16, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_COLORSPACE_SRGB); - stbir_resize_arbitrary(&g_context, large_image, 32, 32, 0, output_data_2, 16, 16, 0, 0.25f, 0.25f, 0.5f, 0.5f, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + stbir_resize(image, 8, 8, 0, output_data_1, 16, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); + stbir_resize_region(large_image, 32, 32, 0, output_data_2, 16, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0.25f, 0.25f, 0.5f, 0.5f); {for (int x = 0; x < 16; x++) { @@ -492,7 +500,7 @@ void test_subpixel_3() unsigned char output_data_1[32 * 32]; unsigned char output_data_2[32 * 32]; - stbir_resize_region(image, 8, 8, 0, output_data_1, 32, 32, 0, STBIR_TYPE_UINT8, 1,0,-1, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_LINEAR, NULL, 0, 0, 1, 1); + stbir_resize_region(image, 8, 8, 0, output_data_1, 32, 32, 0, STBIR_TYPE_UINT8, 1, 0, STBIR_ALPHA_CHANNEL_NONE, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_LINEAR, NULL, 0, 0, 1, 1); stbir_resize_uint8(image, 8, 8, 0, output_data_2, 32, 32, 0, 1); for (int x = 0; x < 32; x++) @@ -514,7 +522,7 @@ void test_subpixel_4() unsigned char output[8 * 8]; - stbir_resize_arbitrary(&g_context, image, 8, 8, 0, output, 8, 8, 0, 0, 0, 1, 1, NULL, 1, -1, 0, STBIR_TYPE_UINT8, STBIR_FILTER_BILINEAR, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); + stbir_resize_region(image, 8, 8, 0, output, 8, 8, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BILINEAR, STBIR_FILTER_BILINEAR, STBIR_COLORSPACE_LINEAR, &g_context, 0, 0, 1, 1); STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); } From 586e84087c54a3eb48ef0fc593bc71019329bc06 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 6 Sep 2014 21:50:28 -0700 Subject: [PATCH 105/168] Correctly specify alpha channels. --- tests/resample_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index cbd5956..9fd5ea3 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -222,7 +222,7 @@ void resize_image(const char* filename, float width_percent, float height_percen unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); - stbir_resize(input_data, w, h, 0, output_data, out_w, out_h, 0, STBIR_TYPE_UINT8, n, 0, 0, edge, edge, filter, filter, colorspace, &g_context); + stbir_resize(input_data, w, h, 0, output_data, out_w, out_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, edge, edge, filter, filter, colorspace, &g_context); stbi_image_free(input_data); @@ -257,7 +257,7 @@ void test_format(const char* file, float width_percent, float height_percent, st T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); - stbir_resize(T_data, w, h, 0, output_data, new_w, new_h, 0, type, n, 0, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, colorspace, &g_context); + stbir_resize(T_data, w, h, 0, output_data, new_w, new_h, 0, type, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, colorspace, &g_context); free(T_data); stbi_image_free(input_data); From cd1fbacbb60fa97215e165fc1ac14f1b99b1b1a6 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 7 Sep 2014 03:19:18 -0700 Subject: [PATCH 106/168] rename NEAREST to BOX write test for BOX --- stb_image_resize.h | 8 ++--- tests/resample_test.cpp | 87 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 79 insertions(+), 16 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index aa7fa5f..307d829 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -136,7 +136,7 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels typedef enum { STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses - STBIR_FILTER_NEAREST = 1, + STBIR_FILTER_BOX = 1, STBIR_FILTER_BILINEAR = 2, STBIR_FILTER_BICUBIC = 3, // A cubic b spline STBIR_FILTER_CATMULLROM = 4, @@ -487,9 +487,9 @@ static unsigned char stbir__linear_to_srgb_uchar(float f) return (unsigned char) v; } -static float stbir__filter_nearest(float x) +static float stbir__filter_box(float x) { - if (x <= -0.5f) + if (x < -0.5f) return 0; else if (x > 0.5f) return 0; @@ -545,7 +545,7 @@ static float stbir__filter_mitchell(float x) static stbir__filter_info stbir__filter_info_table[] = { { NULL, 0.0f }, - { stbir__filter_nearest, 0.5f }, // 0.000001? + { stbir__filter_box , 0.5f }, { stbir__filter_bilinear, 1.0f }, { stbir__filter_bicubic, 2.0f }, { stbir__filter_catmullrom, 2.0f }, diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index fc30925..0d65f21 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -377,7 +377,7 @@ void test_subpixel_1() mtsrand(0); for (int i = 0; i < sizeof(image); i++) - image[i] = mtrand() % 255; + image[i] = mtrand() & 255; unsigned char output_data[16 * 16]; @@ -407,7 +407,7 @@ void test_subpixel_2() mtsrand(0); for (int i = 0; i < sizeof(image); i++) - image[i] = mtrand() % 255; + image[i] = mtrand() & 255; unsigned char large_image[32 * 32]; @@ -444,7 +444,7 @@ void test_subpixel_3() mtsrand(0); for (int i = 0; i < sizeof(image); i++) - image[i] = mtrand() % 255; + image[i] = mtrand() & 255; unsigned char output_data_1[32 * 32]; unsigned char output_data_2[32 * 32]; @@ -467,7 +467,7 @@ void test_subpixel_4() mtsrand(0); for (int i = 0; i < sizeof(image); i++) - image[i] = mtrand() % 255; + image[i] = mtrand() & 255; unsigned char output[8 * 8]; @@ -475,6 +475,67 @@ void test_subpixel_4() STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); } +static unsigned char image88 [8][8]; +static unsigned char output88[8][8]; +static unsigned char output44[4][4]; +static unsigned char output22[2][2]; +static unsigned char output11[1][1]; + +void resample_88(stbir_filter filter) +{ + stbir_resize_uint8_generic(image88[0],8,8,0, output88[0],8,8,0, 1,-1,0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); + stbir_resize_uint8_generic(image88[0],8,8,0, output44[0],4,4,0, 1,-1,0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); + stbir_resize_uint8_generic(image88[0],8,8,0, output22[0],2,2,0, 1,-1,0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); + stbir_resize_uint8_generic(image88[0],8,8,0, output11[0],1,1,0, 1,-1,0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); +} + +void verify_box(void) +{ + int i,j,t; + + resample_88(STBIR_FILTER_BOX); + + for (i=0; i < sizeof(image88); ++i) + STBIR_ASSERT(image88[0][i] == output88[0][i]); + + t = 0; + for (j=0; j < 4; ++j) + for (i=0; i < 4; ++i) { + int n = image88[j*2+0][i*2+0] + + image88[j*2+0][i*2+1] + + image88[j*2+1][i*2+0] + + image88[j*2+1][i*2+1]; + STBIR_ASSERT(output44[j][i] == ((n+2)>>2)); + t += n; + } + STBIR_ASSERT(output11[j][i] == ((t+32)>>6)); +} + +void test_filters(void) +{ + int i,j; + + for (i=0; i < sizeof(image88); ++i) + image88[0][i] = mtrand() & 255; + verify_box(); + + for (i=0; i < sizeof(image88); ++i) + image88[0][i] = 0; + image88[4][4] = 255; + verify_box(); + + for (j=0; j < 8; ++j) + for (i=0; i < 8; ++i) + image88[j][i] = (j^i)&1 ? 255 : 0; + verify_box(); + + for (j=0; j < 8; ++j) + for (i=0; i < 8; ++i) + image88[j][i] = i&2 ? 255 : 0; + verify_box(); +} + + void test_suite(int argc, char **argv) { int i; @@ -496,6 +557,8 @@ void test_suite(int argc, char **argv) } #endif + test_filters(); + test_subpixel_1(); test_subpixel_2(); test_subpixel_3(); @@ -527,17 +590,17 @@ void test_suite(int argc, char **argv) test_channels(barbara, 2, 2, 4); // filter tests - resize_image(barbara, 2, 2, STBIR_FILTER_NEAREST, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-nearest.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_BICUBIC, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_BOX , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-nearest.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_BILINEAR , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_BICUBIC , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); resize_image(barbara, 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-catmullrom.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_MITCHELL, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-mitchell.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_MITCHELL , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-mitchell.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_NEAREST, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-nearest.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BILINEAR, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BICUBIC, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BOX , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-nearest.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BILINEAR , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BICUBIC , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_MITCHELL, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_MITCHELL , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); for (i = 10; i < 100; i++) { From 7da729bfce662e9c8a69beb2a5024a33a0af694f Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 7 Sep 2014 04:07:07 -0700 Subject: [PATCH 107/168] restore correct definition of box --- stb_image_resize.h | 4 +--- tests/resample_test.cpp | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index a1949b5..0322743 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1,5 +1,3 @@ -#define STBIR_DEBUG_OVERWRITE_TEST - /* stb_image_resize - v0.50 - public domain image resampling no warranty implied; use at your own risk @@ -507,7 +505,7 @@ static unsigned char stbir__linear_to_srgb_uchar(float f) static float stbir__filter_box(float x) { - if (x < -0.5f) + if (x <= -0.5f) return 0; else if (x > 0.5f) return 0; diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 77d8437..5cc1518 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -720,5 +720,3 @@ void test_suite(int argc, char **argv) resize_image("gamma_2.2.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_2.2.jpg"); resize_image("gamma_dalai_lama_gray.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_dalai_lama_gray.jpg"); } - - From 7f8ac35e4224a7209eae1cd466236093bcf3c019 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 7 Sep 2014 05:29:43 -0700 Subject: [PATCH 108/168] check whether filter kernels are normalized --- tests/resample_test.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 5cc1518..efcb380 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -601,6 +601,41 @@ void test_suite(int argc, char **argv) else barbara = "barbara.png"; + #if 1 + { + float x,y; + for (x = -1; x < 1; x += 0.05f) { + float sums[4] = {0}; + float o; + for (o=-5; o <= 5; ++o) { + sums[0] += stbir__filter_mitchell(x+o); + sums[1] += stbir__filter_catmullrom(x+o); + sums[2] += stbir__filter_bicubic(x+o); + sums[3] += stbir__filter_bilinear(x+o); + } + for (i=0; i < 4; ++i) + STBIR_ASSERT(sums[i] >= 1.0 - 0.01 && sums[i] <= 1.0 + 0.01); + } + + #if 1 + for (y = 0.11f; y < 1; y += 0.01f) { + for (x = -1; x < 1; x += 0.05f) { + float sums[4] = {0}; + float o; + for (o=-5; o <= 5; o += y) { + sums[0] += y * stbir__filter_mitchell(x+o); + sums[1] += y * stbir__filter_catmullrom(x+o); + sums[2] += y * stbir__filter_bicubic(x+o); + sums[3] += y * stbir__filter_bilinear(x+o); + } + for (i=0; i < 3; ++i) + STBIR_ASSERT(sums[i] >= 1.0 - 0.02 && sums[i] <= 1.0 + 0.02); + } + } + #endif + } + #endif + #if 0 // linear_to_srgb_uchar table for (i=0; i < 256; ++i) { float f = stbir__srgb_to_linear((i+0.5f)/256.0f); From 36db03f3902943fdb27fa54f930baf6ad642426d Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 7 Sep 2014 05:31:40 -0700 Subject: [PATCH 109/168] tighten bounds on filter normalization test --- 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 efcb380..5980c3f 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -614,7 +614,7 @@ void test_suite(int argc, char **argv) sums[3] += stbir__filter_bilinear(x+o); } for (i=0; i < 4; ++i) - STBIR_ASSERT(sums[i] >= 1.0 - 0.01 && sums[i] <= 1.0 + 0.01); + STBIR_ASSERT(sums[i] >= 1.0 - 0.001 && sums[i] <= 1.0 + 0.001); } #if 1 From 8cc6a3abfcff77abd14fb64bad095b0d1a6682af Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sun, 7 Sep 2014 10:02:51 -0700 Subject: [PATCH 110/168] If there's no context we're using the simple API functions - fall back to malloc and free. --- tests/resample_test.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 5980c3f..5ec6f2c 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -33,7 +33,7 @@ public: void* stbir_malloc(void* context, size_t size) { if (!context) - return 0; + return malloc(size); stbir_context* real_context = (stbir_context*)context; if (size > real_context->size) @@ -44,6 +44,8 @@ void* stbir_malloc(void* context, size_t size) void stbir_free(void* context, void* memory) { + if (!context) + return free(memory); } void stbir_progress(float p) @@ -645,6 +647,19 @@ void test_suite(int argc, char **argv) } #endif + /*resize_image("white-stripes.png", 0.5f, 0.5f, STBIR_FILTER_BOX, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-50-nearest.png"); + resize_image("white-stripes.png", 0.5f, 0.5f, STBIR_FILTER_BILINEAR, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-50-bilinear.png"); + resize_image("white-stripes.png", 0.5f, 0.5f, STBIR_FILTER_BICUBIC, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-50-bicubic.png"); + resize_image("white-stripes.png", 0.5f, 0.5f, STBIR_FILTER_MITCHELL, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-50-mitchell.png"); + resize_image("white-stripes.png", 0.5f, 0.5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-50-catmullrom.png"); + + resize_image("white-stripes.png", 0.25f, 0.25f, STBIR_FILTER_BOX, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-25-nearest.png"); + resize_image("white-stripes.png", 0.25f, 0.25f, STBIR_FILTER_BILINEAR, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-25-bilinear.png"); + resize_image("white-stripes.png", 0.25f, 0.25f, STBIR_FILTER_BICUBIC, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-25-bicubic.png"); + resize_image("white-stripes.png", 0.25f, 0.25f, STBIR_FILTER_MITCHELL, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-25-mitchell.png"); + resize_image("white-stripes.png", 0.25f, 0.25f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-25-catmullrom.png"); + + return;*/ test_filters(); test_subpixel_1(); From e6c47ec657e81e72c387e10225f7e5770885c3e6 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sun, 7 Sep 2014 11:56:11 -0700 Subject: [PATCH 111/168] Fix kernel lookup for downsampling. --- stb_image_resize.h | 7 ++++--- tests/resample_test.cpp | 13 ------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 0322743..e642e41 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -787,8 +787,9 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st for (i = 0; i <= out_last_pixel - out_first_pixel; i++) { - float in_pixel_center = (float)(i + out_first_pixel) + 0.5f; - coefficient_group[i] = stbir__filter_info_table[filter].kernel((out_center_of_in - in_pixel_center)/scale_ratio); + float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(x) * scale_ratio; } } @@ -1719,7 +1720,7 @@ static int stbir__resize_allocated(stbir__info *info, info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, info->decode_buffer_pixels * info->channels * sizeof(float), float); info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, info->ring_buffer_length_bytes * stbir__get_filter_pixel_width_horizontal(info), float); - STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, info->output_w * info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } else { diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 5ec6f2c..3aae86a 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -647,19 +647,6 @@ void test_suite(int argc, char **argv) } #endif - /*resize_image("white-stripes.png", 0.5f, 0.5f, STBIR_FILTER_BOX, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-50-nearest.png"); - resize_image("white-stripes.png", 0.5f, 0.5f, STBIR_FILTER_BILINEAR, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-50-bilinear.png"); - resize_image("white-stripes.png", 0.5f, 0.5f, STBIR_FILTER_BICUBIC, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-50-bicubic.png"); - resize_image("white-stripes.png", 0.5f, 0.5f, STBIR_FILTER_MITCHELL, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-50-mitchell.png"); - resize_image("white-stripes.png", 0.5f, 0.5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-50-catmullrom.png"); - - resize_image("white-stripes.png", 0.25f, 0.25f, STBIR_FILTER_BOX, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-25-nearest.png"); - resize_image("white-stripes.png", 0.25f, 0.25f, STBIR_FILTER_BILINEAR, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-25-bilinear.png"); - resize_image("white-stripes.png", 0.25f, 0.25f, STBIR_FILTER_BICUBIC, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-25-bicubic.png"); - resize_image("white-stripes.png", 0.25f, 0.25f, STBIR_FILTER_MITCHELL, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-25-mitchell.png"); - resize_image("white-stripes.png", 0.25f, 0.25f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/white-stripes-down-25-catmullrom.png"); - - return;*/ test_filters(); test_subpixel_1(); From 118f28557e7bf55de8dd58d026971260c2c43fc9 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sun, 7 Sep 2014 16:47:02 -0700 Subject: [PATCH 112/168] Tests to ensure images of a solid color stay the same solid color after resampling. --- tests/resample_test.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 3aae86a..9fb6593 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -566,6 +566,22 @@ void verify_box(void) STBIR_ASSERT(output11[j][i] == ((t+32)>>6)); } +void verify_filter_normalized(stbir_filter filter, unsigned char* output, int output_size) +{ + int value = 64; + + int i, j; + for (j = 0; j < 8; ++j) + for (i = 0; i < 8; ++i) + image88[j][i] = value; + + stbir_resize_uint8_generic(image88[0], 8, 8, 0, output, output_size, output_size, 0, 1, -1, 0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); + + for (j = 0; j < output_size; ++j) + for (i = 0; i < output_size; ++i) + STBIR_ASSERT(value == output[j*output_size + i]); +} + void test_filters(void) { int i,j; @@ -588,6 +604,30 @@ void test_filters(void) for (i=0; i < 8; ++i) image88[j][i] = i&2 ? 255 : 0; verify_box(); + + verify_filter_normalized(STBIR_FILTER_BOX, &output88[0][0], 8); + verify_filter_normalized(STBIR_FILTER_BILINEAR, &output88[0][0], 8); + verify_filter_normalized(STBIR_FILTER_BICUBIC, &output88[0][0], 8); + verify_filter_normalized(STBIR_FILTER_CATMULLROM, &output88[0][0], 8); + verify_filter_normalized(STBIR_FILTER_MITCHELL, &output88[0][0], 8); + + verify_filter_normalized(STBIR_FILTER_BOX, &output44[0][0], 4); + verify_filter_normalized(STBIR_FILTER_BILINEAR, &output44[0][0], 4); + verify_filter_normalized(STBIR_FILTER_BICUBIC, &output44[0][0], 4); + verify_filter_normalized(STBIR_FILTER_CATMULLROM, &output44[0][0], 4); + verify_filter_normalized(STBIR_FILTER_MITCHELL, &output44[0][0], 4); + + verify_filter_normalized(STBIR_FILTER_BOX, &output22[0][0], 2); + verify_filter_normalized(STBIR_FILTER_BILINEAR, &output22[0][0], 2); + verify_filter_normalized(STBIR_FILTER_BICUBIC, &output22[0][0], 2); + verify_filter_normalized(STBIR_FILTER_CATMULLROM, &output22[0][0], 2); + verify_filter_normalized(STBIR_FILTER_MITCHELL, &output22[0][0], 2); + + verify_filter_normalized(STBIR_FILTER_BOX, &output11[0][0], 1); + verify_filter_normalized(STBIR_FILTER_BILINEAR, &output11[0][0], 1); + verify_filter_normalized(STBIR_FILTER_BICUBIC, &output11[0][0], 1); + verify_filter_normalized(STBIR_FILTER_CATMULLROM, &output11[0][0], 1); + verify_filter_normalized(STBIR_FILTER_MITCHELL, &output11[0][0], 1); } From 145690788c1c2b34af9a595d39203330a06abe8f Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 10:58:01 -0700 Subject: [PATCH 113/168] Use input_h + pixel_margin * 2 to prevent progress report from ever going above 1. Always report a 0 before and a 1 at the end of each resize. --- stb_image_resize.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index e642e41..2a6013a 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1520,11 +1520,12 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info) float scale_ratio = stbir_info->vertical_scale; int output_h = stbir_info->output_h; float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support / scale_ratio; - int max_y = stbir_info->input_h + stbir__get_filter_pixel_margin_vertical(stbir_info); + int pixel_margin = stbir__get_filter_pixel_margin_vertical(stbir_info); + int max_y = stbir_info->input_h + pixel_margin; STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); - for (y = -stbir__get_filter_pixel_margin_vertical(stbir_info); y < max_y; y++) + for (y = -pixel_margin; y < max_y; y++) { float out_center_of_in; // Center of the current out scanline in the in scanline space int out_first_scanline, out_last_scanline; @@ -1550,7 +1551,7 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info) // Now the horizontal buffer is ready to write to all ring buffer rows. stbir__resample_vertical_downsample(stbir_info, y, out_first_scanline, out_last_scanline, out_center_of_in); - STBIR_PROGRESS_REPORT((float)(y + stbir__get_filter_pixel_margin_vertical(stbir_info)) / (max_y + stbir__get_filter_pixel_margin_vertical(stbir_info))); + STBIR_PROGRESS_REPORT((float)(y + pixel_margin) / (stbir_info->input_h + pixel_margin * 2)); } stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); @@ -1738,11 +1739,15 @@ static int stbir__resize_allocated(stbir__info *info, stbir__calculate_horizontal_filters(info); + STBIR_PROGRESS_REPORT(0); + if (stbir__use_height_upsampling(info)) stbir__buffer_loop_upsample(info); else stbir__buffer_loop_downsample(info); + STBIR_PROGRESS_REPORT(1); + #ifdef STBIR_DEBUG_OVERWRITE_TEST STBIR__DEBUG_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); STBIR__DEBUG_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); From ca241daefaa9eace0053843bb28e0ae806837db1 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 12:16:23 -0700 Subject: [PATCH 114/168] Use rounding to try to preserve the original value. Fix test case. --- stb_image_resize.h | 16 ++++++++-------- tests/resample_test.cpp | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 2a6013a..f0892cd 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1210,7 +1210,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void int encode_pixel_index = x*channels; for (n = 0; n < channels; n++) - ((unsigned char*)output_buffer)[output_pixel_index + n] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 255); + ((unsigned char*)output_buffer)[output_pixel_index + n] = (unsigned char)(round(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 255)); } break; @@ -1225,7 +1225,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void ((unsigned char*)output_buffer)[output_pixel_index + n] = stbir__linear_to_srgb_uchar(encode_buffer[encode_pixel_index + n]); if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned char*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); + ((unsigned char*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(round(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255)); } break; @@ -1236,7 +1236,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void int encode_pixel_index = x*channels; for (n = 0; n < channels; n++) - ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 65535); + ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(round(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 65535)); } break; @@ -1247,10 +1247,10 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void int encode_pixel_index = x*channels; for (n = 0; n < channels; n++) - ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n])) * 65535); + ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(round(stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n])) * 65535)); if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned short*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255); + ((unsigned short*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned short)(round(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 65535)); } break; @@ -1262,7 +1262,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void int encode_pixel_index = x*channels; for (n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbir__saturate(encode_buffer[encode_pixel_index + n])) * 4294967295); + ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(round(((double)stbir__saturate(encode_buffer[encode_pixel_index + n])) * 4294967295)); } break; @@ -1273,10 +1273,10 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void int encode_pixel_index = x*channels; for (n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n]))) * 4294967295); + ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(round(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n]))) * 4294967295)); if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned int*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned int)(((double)stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel])) * 4294967295); + ((unsigned int*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned int)(round(((double)stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel])) * 4294967295)); } break; diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 9fb6593..469fa05 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -563,7 +563,7 @@ void verify_box(void) STBIR_ASSERT(output44[j][i] == ((n+2)>>2)); t += n; } - STBIR_ASSERT(output11[j][i] == ((t+32)>>6)); + STBIR_ASSERT(output11[0][0] == ((t+32)>>6)); } void verify_filter_normalized(stbir_filter filter, unsigned char* output, int output_size) From 43fbc1d5e36d001ce6ee093228e27c638d249275 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 14:10:14 -0700 Subject: [PATCH 115/168] Rearrange the algebra on two of the filters to avoid repeating decimals which cause precision loss. Use int32 to test filters because it's more accurate. --- stb_image_resize.h | 8 +++---- tests/resample_test.cpp | 59 ++++++++++++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index f0892cd..6e6dca3 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -528,9 +528,9 @@ static float stbir__filter_bicubic(float x) x = (float)fabs(x); if (x < 1.0f) - return 0.66666666666f + x*x*(0.5f*x - 1); + return (4 + x*x*(3*x - 6))/6; else if (x < 2.0f) - return 1.3333333333f + x*(-2 + x*(1 - 0.16666666f * x)); + return (8 + x*(-12 + x*(6 - x)))/6; return (0.0f); } @@ -552,9 +552,9 @@ static float stbir__filter_mitchell(float x) x = (float)fabs(x); if (x < 1.0f) - return 0.8888888888f + x*x*(1.1666666666666f * x - 2.0f); + return (16 + x*x*(21 * x - 36))/18; else if (x < 2.0f) - return 1.777777777777f + x*(-3.3333333333f + x*(2 - 0.3888888888888f*x)); + return (32 + x*(-60 + x*(36 - 7*x)))/18; return (0.0f); } diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 469fa05..de0f94b 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -530,6 +530,7 @@ void test_subpixel_4() STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); } +static unsigned int image88_int[8][8]; static unsigned char image88 [8][8]; static unsigned char output88[8][8]; static unsigned char output44[4][4]; @@ -566,16 +567,12 @@ void verify_box(void) STBIR_ASSERT(output11[0][0] == ((t+32)>>6)); } -void verify_filter_normalized(stbir_filter filter, unsigned char* output, int output_size) +void verify_filter_normalized(stbir_filter filter, int output_size, int value) { - int value = 64; - int i, j; - for (j = 0; j < 8; ++j) - for (i = 0; i < 8; ++i) - image88[j][i] = value; + unsigned int output[64]; - stbir_resize_uint8_generic(image88[0], 8, 8, 0, output, output_size, output_size, 0, 1, -1, 0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); + stbir_resize(image88_int[0], 8, 8, 0, output, output_size, output_size, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, filter, filter, STBIR_COLORSPACE_LINEAR, NULL); for (j = 0; j < output_size; ++j) for (i = 0; i < output_size; ++i) @@ -605,29 +602,35 @@ void test_filters(void) image88[j][i] = i&2 ? 255 : 0; verify_box(); - verify_filter_normalized(STBIR_FILTER_BOX, &output88[0][0], 8); - verify_filter_normalized(STBIR_FILTER_BILINEAR, &output88[0][0], 8); - verify_filter_normalized(STBIR_FILTER_BICUBIC, &output88[0][0], 8); - verify_filter_normalized(STBIR_FILTER_CATMULLROM, &output88[0][0], 8); - verify_filter_normalized(STBIR_FILTER_MITCHELL, &output88[0][0], 8); + int value = 64; - verify_filter_normalized(STBIR_FILTER_BOX, &output44[0][0], 4); - verify_filter_normalized(STBIR_FILTER_BILINEAR, &output44[0][0], 4); - verify_filter_normalized(STBIR_FILTER_BICUBIC, &output44[0][0], 4); - verify_filter_normalized(STBIR_FILTER_CATMULLROM, &output44[0][0], 4); - verify_filter_normalized(STBIR_FILTER_MITCHELL, &output44[0][0], 4); + for (j = 0; j < 8; ++j) + for (i = 0; i < 8; ++i) + image88_int[j][i] = value; - verify_filter_normalized(STBIR_FILTER_BOX, &output22[0][0], 2); - verify_filter_normalized(STBIR_FILTER_BILINEAR, &output22[0][0], 2); - verify_filter_normalized(STBIR_FILTER_BICUBIC, &output22[0][0], 2); - verify_filter_normalized(STBIR_FILTER_CATMULLROM, &output22[0][0], 2); - verify_filter_normalized(STBIR_FILTER_MITCHELL, &output22[0][0], 2); + verify_filter_normalized(STBIR_FILTER_BOX, 8, value); + verify_filter_normalized(STBIR_FILTER_BILINEAR, 8, value); + verify_filter_normalized(STBIR_FILTER_BICUBIC, 8, value); + verify_filter_normalized(STBIR_FILTER_CATMULLROM, 8, value); + verify_filter_normalized(STBIR_FILTER_MITCHELL, 8, value); - verify_filter_normalized(STBIR_FILTER_BOX, &output11[0][0], 1); - verify_filter_normalized(STBIR_FILTER_BILINEAR, &output11[0][0], 1); - verify_filter_normalized(STBIR_FILTER_BICUBIC, &output11[0][0], 1); - verify_filter_normalized(STBIR_FILTER_CATMULLROM, &output11[0][0], 1); - verify_filter_normalized(STBIR_FILTER_MITCHELL, &output11[0][0], 1); + verify_filter_normalized(STBIR_FILTER_BOX, 4, value); + verify_filter_normalized(STBIR_FILTER_BILINEAR, 4, value); + verify_filter_normalized(STBIR_FILTER_BICUBIC, 4, value); + verify_filter_normalized(STBIR_FILTER_CATMULLROM, 4, value); + verify_filter_normalized(STBIR_FILTER_MITCHELL, 4, value); + + verify_filter_normalized(STBIR_FILTER_BOX, 2, value); + verify_filter_normalized(STBIR_FILTER_BILINEAR, 2, value); + verify_filter_normalized(STBIR_FILTER_BICUBIC, 2, value); + verify_filter_normalized(STBIR_FILTER_CATMULLROM, 2, value); + verify_filter_normalized(STBIR_FILTER_MITCHELL, 2, value); + + verify_filter_normalized(STBIR_FILTER_BOX, 1, value); + verify_filter_normalized(STBIR_FILTER_BILINEAR, 1, value); + verify_filter_normalized(STBIR_FILTER_BICUBIC, 1, value); + verify_filter_normalized(STBIR_FILTER_CATMULLROM, 1, value); + verify_filter_normalized(STBIR_FILTER_MITCHELL, 1, value); } @@ -671,7 +674,7 @@ void test_suite(int argc, char **argv) sums[3] += y * stbir__filter_bilinear(x+o); } for (i=0; i < 3; ++i) - STBIR_ASSERT(sums[i] >= 1.0 - 0.02 && sums[i] <= 1.0 + 0.02); + STBIR_ASSERT(sums[i] >= 1.0 - 0.0170 && sums[i] <= 1.0 + 0.0170); } } #endif From 497eab8339be92ab1762592ae9b388cd4be7e24d Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 15:22:29 -0700 Subject: [PATCH 116/168] Normalize downsample coefficients. --- stb_image_resize.h | 20 +++++++++++++------- tests/resample_test.cpp | 20 ++++++++++++++++++-- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 6e6dca3..cc3b697 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -793,8 +793,7 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st } } -#ifdef STBIR_DEBUG -static void stbir__check_downsample_coefficients(stbir__info* stbir_info) +static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) { int i; for (i = 0; i < stbir_info->output_w; i++) @@ -813,10 +812,19 @@ static void stbir__check_downsample_coefficients(stbir__info* stbir_info) } STBIR__DEBUG_ASSERT(total > 0.9f); - STBIR__DEBUG_ASSERT(total <= 1.0f + 1.0f / (pow(2.0f, 8.0f * stbir__type_size[stbir_info->type]) - 1)); + STBIR__DEBUG_ASSERT(total < 1.1f); + + float scale = 1 / total; + + for (j = 0; j < stbir__get_horizontal_contributors(stbir_info); j++) + { + if (i >= stbir_info->horizontal_contributors[j].n0 && i <= stbir_info->horizontal_contributors[j].n1) + *stbir__get_coefficient(stbir_info, j, i - stbir_info->horizontal_contributors[j].n0) *= scale; + else if (i < stbir_info->horizontal_contributors[j].n0) + break; + } } } -#endif // Each scan line uses the same kernel values so we should calculate the kernel // values once and then we can use them for every scan line. @@ -858,9 +866,7 @@ static void stbir__calculate_horizontal_filters(stbir__info* stbir_info) stbir__calculate_coefficients_downsample(stbir_info, stbir_info->horizontal_filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(stbir_info, n), stbir__get_coefficient(stbir_info, n, 0)); } -#ifdef STBIR_DEBUG - stbir__check_downsample_coefficients(stbir_info); -#endif + stbir__normalize_downsample_coefficients(stbir_info); } } diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index de0f94b..02bb92e 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -631,6 +631,22 @@ void test_filters(void) verify_filter_normalized(STBIR_FILTER_BICUBIC, 1, value); verify_filter_normalized(STBIR_FILTER_CATMULLROM, 1, value); verify_filter_normalized(STBIR_FILTER_MITCHELL, 1, value); + + { + // This test is designed to produce coefficients that are very badly denormalized. + int v = 556; + + unsigned int input[100 * 100]; + unsigned int output[11 * 11]; + + for (j = 0; j < 100 * 100; ++j) + input[j] = v; + + stbir_resize(input, 100, 100, 0, output, 11, 11, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BILINEAR, STBIR_FILTER_BILINEAR, STBIR_COLORSPACE_LINEAR, NULL); + + for (j = 0; j < 11 * 11; ++j) + STBIR_ASSERT(v == output[j]); + } } @@ -663,8 +679,8 @@ void test_suite(int argc, char **argv) } #if 1 - for (y = 0.11f; y < 1; y += 0.01f) { - for (x = -1; x < 1; x += 0.05f) { + for (y = 0.11f; y < 1; y += 0.01f) { // Step + for (x = -1; x < 1; x += 0.05f) { // Phase float sums[4] = {0}; float o; for (o=-5; o <= 5; o += y) { From 084baed15ce302aac4bca4949f9533a259c1c54b Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 16:30:06 -0700 Subject: [PATCH 117/168] Replace premul test with better premul test that doesn't require visual inspection. --- tests/resample_test.cpp | 60 +++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 02bb92e..0300f6f 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -384,44 +384,46 @@ void test_subpixel(const char* file, float width_percent, float height_percent, free(output_data); } -void test_premul(const char* file) +unsigned char* pixel(unsigned char* buffer, int x, int y, int c, int w, int n) { - int w, h, n; - unsigned char* input_data = stbi_load(file, &w, &h, &n, 4); - n = 4; + return &buffer[y*w*n + x*n + c]; +} - if (input_data == NULL) - return; +void test_premul() +{ + unsigned char input[2 * 2 * 4]; + unsigned char output[1 * 1 * 4]; + memset(input, 0, sizeof(input)); - // Set alpha for the top half. - for (int x = 0; x < w; x++) - { - for (int y = 0; y < h / 2; y++) - input_data[(y*w + x)*n + 3] = input_data[(y*w + x)*n + 0]; - } + // Top left - solid red + *pixel(input, 0, 0, 0, 2, 4) = 255; + *pixel(input, 0, 0, 3, 2, 4) = 255; - stbi_write_png("test-output/premul-original.png", w, h, n, input_data, 0); + // Bottom left - solid red + *pixel(input, 0, 1, 0, 2, 4) = 255; + *pixel(input, 0, 1, 3, 2, 4) = 255; - int new_w = (int)(w * .1); - int new_h = (int)(h * .1); + // Top right - transparent green + *pixel(input, 1, 0, 1, 2, 4) = 255; + *pixel(input, 1, 0, 3, 2, 4) = 25; - unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); + // Bottom right - transparent green + *pixel(input, 1, 1, 1, 2, 4) = 255; + *pixel(input, 1, 1, 3, 2, 4) = 25; - stbir_resize_uint8_generic(input_data, w, h, 0, output_data, new_w, new_h, 0, n, n - 1, 0, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); + stbir_resize_uint8_generic(input, 2, 2, 0, output, 1, 1, 0, 4, 3, 0, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, &g_context); - char output[200]; - sprintf(output, "test-output/premul-%s", file); - stbi_write_png(output, new_w, new_h, n, output_data, 0); + float r = 1.0f; + float g = 1.0f; + float ra = 1.0; + float ga = (float)25 / 255; + float a = (ra + ga) / 2; - stbir_resize_uint8_generic(input_data, w, h, 0, output_data, new_w, new_h, 0, n, n - 1, STBIR_FLAG_PREMULTIPLIED_ALPHA, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); - - sprintf(output, "test-output/nopremul-%s", file); - stbi_write_png(output, new_w, new_h, n, output_data, 0); - - stbi_image_free(input_data); - - free(output_data); + STBIR_ASSERT(output[0] == (int)(r * ra / 2 / a * 255 + 0.5f)); // 232 + STBIR_ASSERT(output[1] == (int)(g * ga / 2 / a * 255 + 0.5f)); // 23 + STBIR_ASSERT(output[2] == 0); + STBIR_ASSERT(output[3] == (int)(a * 255 + 0.5f)); // 140 } // test that splitting a pow-2 image into tiles produces identical results @@ -713,7 +715,7 @@ void test_suite(int argc, char **argv) test_subpixel_3(); test_subpixel_4(); - test_premul(barbara); + test_premul(); for (i = 0; i < 10; i++) test_subpixel(barbara, 0.5f, 0.5f, (float)i / 10, 1); From d510d70b6a156039f49dbb39d2e080ff28103309 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 16:35:00 -0700 Subject: [PATCH 118/168] Report downsample progress by when we empty the ring buffer, it's more accurate that way. --- stb_image_resize.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index cc3b697..215a0ab 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1500,6 +1500,7 @@ static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessar int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); + STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / output_w); } if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) @@ -1556,8 +1557,6 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info) // Now the horizontal buffer is ready to write to all ring buffer rows. stbir__resample_vertical_downsample(stbir_info, y, out_first_scanline, out_last_scanline, out_center_of_in); - - STBIR_PROGRESS_REPORT((float)(y + pixel_margin) / (stbir_info->input_h + pixel_margin * 2)); } stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); From 4b69c0637eff6ed3741a9ed89ce3a4a4ee16af23 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 17:03:20 -0700 Subject: [PATCH 119/168] If alpha is zero then sneakily replace it with a very small nonzero so that the color data is preserved. Pre-process n1 so that we don't have to do it later on. Can't do n0 since it's used to find the coefficient index. --- stb_image_resize.h | 26 ++++++++++++++++------ tests/resample_test.cpp | 57 +++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 215a0ab..b8fbd16 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -795,12 +795,13 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) { + int num_contributors = stbir__get_horizontal_contributors(stbir_info); int i; for (i = 0; i < stbir_info->output_w; i++) { float total = 0; int j; - for (j = 0; j < stbir__get_horizontal_contributors(stbir_info); j++) + for (j = 0; j < num_contributors; j++) { if (i >= stbir_info->horizontal_contributors[j].n0 && i <= stbir_info->horizontal_contributors[j].n1) { @@ -816,7 +817,7 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) float scale = 1 / total; - for (j = 0; j < stbir__get_horizontal_contributors(stbir_info); j++) + for (j = 0; j < num_contributors; j++) { if (i >= stbir_info->horizontal_contributors[j].n0 && i <= stbir_info->horizontal_contributors[j].n1) *stbir__get_coefficient(stbir_info, j, i - stbir_info->horizontal_contributors[j].n0) *= scale; @@ -824,6 +825,15 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) break; } } + + // Using min to avoid writing into invalid pixels. + for (i = 0; i < num_contributors; i++) + { + stbir__contributors* contributors = &stbir_info->horizontal_contributors[i]; + STBIR__DEBUG_ASSERT(contributors->n1 >= contributors->n0); + + contributors->n1 = stbir__min(contributors->n1, stbir_info->output_w - 1); + } } // Each scan line uses the same kernel values so we should calculate the kernel @@ -1014,6 +1024,10 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) { int decode_pixel_index = x * channels; float alpha = decode_buffer[decode_pixel_index + alpha_channel]; + + if (alpha == 0) + alpha = decode_buffer[decode_pixel_index + alpha_channel] = (float)1 / 17179869184; // 1/2^34 should be small enough that it won't affect anything. + for (c = 0; c < channels; c++) { if (c == alpha_channel) @@ -1129,12 +1143,10 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n int in_x = x - filter_pixel_margin; int in_pixel_index = in_x * channels; - int max_n = stbir__min(n1, output_w-1); + int max_n = n1; int coefficient_group = x*kernel_pixel_width; - STBIR__DEBUG_ASSERT(n1 >= n0); - - // Using min and max to avoid writing into invalid pixels. + // Using max to avoid writing into invalid pixels. for (k = stbir__max(n0, 0); k <= max_n; k++) { int coefficient_index = (k - n0) + coefficient_group; @@ -1199,8 +1211,8 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void int output_pixel_index = x*channels; int encode_pixel_index = x*channels; float alpha = encode_buffer[encode_pixel_index + alpha_channel]; + STBIR__DEBUG_ASSERT(alpha > 0); float reciprocal_alpha = alpha ? 1.0f / alpha : 0; - // @TODO: if final alpha=0, we actually want to have ignored alpha... set alpha to sRGB_to_linear(1/255)/(2^24) so floats will discard it? for (n = 0; n < channels; n++) if (n != alpha_channel) encode_buffer[encode_pixel_index + n] *= reciprocal_alpha; diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 0300f6f..1b23397 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -384,18 +384,21 @@ void test_subpixel(const char* file, float width_percent, float height_percent, free(output_data); } -unsigned char* pixel(unsigned char* buffer, int x, int y, int c, int w, int n) +unsigned int* pixel(unsigned int* buffer, int x, int y, int c, int w, int n) { return &buffer[y*w*n + x*n + c]; } void test_premul() { - unsigned char input[2 * 2 * 4]; - unsigned char output[1 * 1 * 4]; + unsigned int input[2 * 2 * 4]; + unsigned int output[1 * 1 * 4]; + unsigned int output2[2 * 2 * 4]; memset(input, 0, sizeof(input)); + // First a test to make sure premul is working properly. + // Top left - solid red *pixel(input, 0, 0, 0, 2, 4) = 255; *pixel(input, 0, 0, 3, 2, 4) = 255; @@ -412,18 +415,50 @@ void test_premul() *pixel(input, 1, 1, 1, 2, 4) = 255; *pixel(input, 1, 1, 3, 2, 4) = 25; - stbir_resize_uint8_generic(input, 2, 2, 0, output, 1, 1, 0, 4, 3, 0, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, &g_context); + stbir_resize(input, 2, 2, 0, output, 1, 1, 0, STBIR_TYPE_UINT32, 4, 3, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, &g_context); - float r = 1.0f; - float g = 1.0f; - float ra = 1.0; - float ga = (float)25 / 255; + float r = (float)255 / 4294967296; + float g = (float)255 / 4294967296; + float ra = (float)255 / 4294967296; + float ga = (float)25 / 4294967296; float a = (ra + ga) / 2; - STBIR_ASSERT(output[0] == (int)(r * ra / 2 / a * 255 + 0.5f)); // 232 - STBIR_ASSERT(output[1] == (int)(g * ga / 2 / a * 255 + 0.5f)); // 23 + 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[2] == 0); - STBIR_ASSERT(output[3] == (int)(a * 255 + 0.5f)); // 140 + STBIR_ASSERT(output[3] == (int)(a * 4294967296 + 0.5f)); // 140 + + // Now a test to make sure it doesn't clobber existing values. + + // Top right - completely transparent green + *pixel(input, 1, 0, 1, 2, 4) = 255; + *pixel(input, 1, 0, 3, 2, 4) = 0; + + // Bottom right - completely transparent green + *pixel(input, 1, 1, 1, 2, 4) = 255; + *pixel(input, 1, 1, 3, 2, 4) = 0; + + stbir_resize(input, 2, 2, 0, output2, 2, 2, 0, STBIR_TYPE_UINT32, 4, 3, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, &g_context); + + STBIR_ASSERT(*pixel(output2, 0, 0, 0, 2, 4) == 255); + STBIR_ASSERT(*pixel(output2, 0, 0, 1, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 0, 0, 2, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 0, 0, 3, 2, 4) == 255); + + STBIR_ASSERT(*pixel(output2, 0, 1, 0, 2, 4) == 255); + STBIR_ASSERT(*pixel(output2, 0, 1, 1, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 0, 1, 2, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 0, 1, 3, 2, 4) == 255); + + STBIR_ASSERT(*pixel(output2, 1, 0, 0, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 1, 0, 1, 2, 4) == 255); + STBIR_ASSERT(*pixel(output2, 1, 0, 2, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 1, 0, 3, 2, 4) == 0); + + STBIR_ASSERT(*pixel(output2, 1, 1, 0, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 1, 1, 1, 2, 4) == 255); + STBIR_ASSERT(*pixel(output2, 1, 1, 2, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 1, 1, 3, 2, 4) == 0); } // test that splitting a pow-2 image into tiles produces identical results From 8355ea1184dbbf24fb9fe845dad5afb814a1c673 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 18:41:28 -0700 Subject: [PATCH 120/168] Use height, not width, for correct progress report. --- stb_image_resize.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index b8fbd16..15832c7 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1512,7 +1512,7 @@ static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessar int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); - STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / output_w); + STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); } if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) From 969ff7c850d34869cdf5e789abfd8a17dbf8e206 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 20:51:29 -0700 Subject: [PATCH 121/168] Make sure calculate_memory matches with the allocations. Fix some errant asserts. --- stb_image_resize.h | 32 ++++++++++++++++---------------- tests/resample_test.cpp | 6 ++++++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 15832c7..7060ffe 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -585,7 +585,7 @@ stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) // This is the maximum number of input samples that can affect an output sample // with the given filter -stbir__inline static int stbir__get_filter_pixel_width(stbir_filter filter, int input_w, int output_w, float scale) +stbir__inline static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) { STBIR_ASSERT(filter != 0); STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); @@ -598,29 +598,29 @@ stbir__inline static int stbir__get_filter_pixel_width(stbir_filter filter, int stbir__inline static int stbir__get_filter_pixel_width_horizontal(stbir__info* stbir_info) { - return stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); + return stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); } stbir__inline static int stbir__get_filter_pixel_width_vertical(stbir__info* stbir_info) { - return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->input_h, stbir_info->output_h, stbir_info->vertical_scale); + return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->vertical_scale); } // This is how much to expand buffers to account for filters seeking outside // the image boundaries. -stbir__inline static int stbir__get_filter_pixel_margin(stbir_filter filter, int input_w, int output_w, float scale) +stbir__inline static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) { - return stbir__get_filter_pixel_width(filter, input_w, output_w, scale) / 2; + return stbir__get_filter_pixel_width(filter, scale) / 2; } stbir__inline static int stbir__get_filter_pixel_margin_horizontal(stbir__info* stbir_info) { - return stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale) / 2; + return stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale) / 2; } stbir__inline static int stbir__get_filter_pixel_margin_vertical(stbir__info* stbir_info) { - return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->input_h, stbir_info->output_h, stbir_info->vertical_scale) / 2; + return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->vertical_scale) / 2; } stbir__inline static int stbir__get_horizontal_contributors(stbir__info* info) @@ -628,13 +628,13 @@ stbir__inline static int stbir__get_horizontal_contributors(stbir__info* info) if (stbir__use_upsampling(info->horizontal_scale)) return info->output_w; else - return (info->input_w + stbir__get_filter_pixel_margin(info->horizontal_filter, info->input_w, info->output_w, info->horizontal_scale) * 2); + return (info->input_w + stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale) * 2); } stbir__inline static int stbir__get_total_coefficients(stbir__info* info) { return stbir__get_horizontal_contributors(info) - * stbir__get_filter_pixel_width (info->horizontal_filter, info->input_w, info->output_w, info->horizontal_scale); + * stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); } stbir__inline static stbir__contributors* stbir__get_contributor(stbir__info* stbir_info, int n) @@ -645,7 +645,7 @@ stbir__inline static stbir__contributors* stbir__get_contributor(stbir__info* st stbir__inline static float* stbir__get_coefficient(stbir__info* stbir_info, int n, int c) { - int width = stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->input_w, stbir_info->output_w, stbir_info->horizontal_scale); + int width = stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); return &stbir_info->horizontal_coefficients[width*n + c]; } @@ -751,7 +751,7 @@ static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbi float total_filter = 0; float filter_scale; - STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= stbir__get_filter_pixel_width_horizontal(stbir_info)); + STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support * 2)); // Taken directly from stbir__get_filter_pixel_width() which we can't call because we don't know if we're horizontal or vertical. contributor->n0 = in_first_pixel; contributor->n1 = in_last_pixel; @@ -778,7 +778,7 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st { int i; - STBIR__DEBUG_ASSERT(out_last_pixel - out_first_pixel <= stbir__get_filter_pixel_width_horizontal(stbir_info)); + STBIR__DEBUG_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support * 2 / scale_ratio)); // Taken directly from stbir__get_filter_pixel_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; @@ -1619,8 +1619,8 @@ static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir static stbir_uint32 stbir__calculate_memory(stbir__info *info) { - int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->input_w, info->output_w, info->horizontal_scale); - int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->input_h, info->output_h, info->vertical_scale); + int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); int contributors_size = stbir__get_horizontal_contributors(info) * sizeof(stbir__contributors); int horizontal_coefficients_size = stbir__get_total_coefficients(info) * sizeof(float); @@ -1635,7 +1635,7 @@ static stbir_uint32 stbir__calculate_memory(stbir__info *info) STBIR_ASSERT(info->vertical_filter != 0); STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - if (stbir__use_upsampling(info->horizontal_scale)) + if (stbir__use_height_upsampling(info)) // The horizontal buffer is for when we're downsampling the height and we // can't output the result of sampling the decode buffer directly into the // ring buffers. @@ -1736,7 +1736,7 @@ static int stbir__resize_allocated(stbir__info *info, { info->horizontal_buffer = NULL; info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, info->decode_buffer_pixels * info->channels * sizeof(float), float); - info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, info->ring_buffer_length_bytes * stbir__get_filter_pixel_width_horizontal(info), float); + info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, info->ring_buffer_length_bytes * stbir__get_filter_pixel_width_vertical(info), float); STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, info->output_w * info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 1b23397..7234048 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -752,6 +752,12 @@ void test_suite(int argc, char **argv) test_premul(); + // Some tests to make sure errors don't pop up with strange filter/dimension combinations. + stbir_resize(image88, 8, 8, 0, output88, 4, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); + stbir_resize(image88, 8, 8, 0, output88, 4, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_BOX, STBIR_COLORSPACE_SRGB, &g_context); + stbir_resize(image88, 8, 8, 0, output88, 16, 4, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); + stbir_resize(image88, 8, 8, 0, output88, 16, 4, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_BOX, STBIR_COLORSPACE_SRGB, &g_context); + for (i = 0; i < 10; i++) test_subpixel(barbara, 0.5f, 0.5f, (float)i / 10, 1); From f2102d906fd749e85367afec1a6aa7b9486bf804 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 20:58:19 -0700 Subject: [PATCH 122/168] Use the info structure to keep track of our memory block sizes to reduce duplicated code and cut down on errors. --- stb_image_resize.h | 51 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 7060ffe..b6dc994 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -384,6 +384,14 @@ typedef struct float* ring_buffer; float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. + + int horizontal_contributors_size; + int horizontal_coefficients_size; + int vertical_coefficients_size; + int decode_buffer_size; + int horizontal_buffer_size; + int ring_buffer_size; + int encode_buffer_size; } stbir__info; static stbir__inline int stbir__min(int a, int b) @@ -1622,13 +1630,13 @@ static stbir_uint32 stbir__calculate_memory(stbir__info *info) int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); - int contributors_size = stbir__get_horizontal_contributors(info) * sizeof(stbir__contributors); - int horizontal_coefficients_size = stbir__get_total_coefficients(info) * sizeof(float); - int vertical_coefficients_size = filter_height * sizeof(float); - int decode_buffer_size = (info->input_w + pixel_margin*2) * info->channels * sizeof(float); - int horizontal_buffer_size = info->output_w * info->channels * sizeof(float); - int ring_buffer_size = info->output_w * info->channels * filter_height * sizeof(float); - int encode_buffer_size = info->output_w * info->channels * sizeof(float); + info->horizontal_contributors_size = stbir__get_horizontal_contributors(info) * sizeof(stbir__contributors); + info->horizontal_coefficients_size = stbir__get_total_coefficients(info) * sizeof(float); + info->vertical_coefficients_size = filter_height * sizeof(float); + info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); + info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); + info->ring_buffer_size = info->output_w * info->channels * filter_height * sizeof(float); + info->encode_buffer_size = info->output_w * info->channels * sizeof(float); STBIR_ASSERT(info->horizontal_filter != 0); STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late @@ -1639,13 +1647,16 @@ static stbir_uint32 stbir__calculate_memory(stbir__info *info) // The horizontal buffer is for when we're downsampling the height and we // can't output the result of sampling the decode buffer directly into the // ring buffers. - horizontal_buffer_size = 0; + info->horizontal_buffer_size = 0; else // The encode buffer is to retain precision in the height upsampling method // and isn't used when height downsampling. - encode_buffer_size = 0; + info->encode_buffer_size = 0; - return contributors_size + horizontal_coefficients_size + vertical_coefficients_size + decode_buffer_size + horizontal_buffer_size + ring_buffer_size + encode_buffer_size; + return info->horizontal_contributors_size + info->horizontal_coefficients_size + + info->vertical_coefficients_size + info->decode_buffer_size + + info->horizontal_buffer_size + info->ring_buffer_size + + info->encode_buffer_size; } static int stbir__resize_allocated(stbir__info *info, @@ -1725,28 +1736,28 @@ static int stbir__resize_allocated(stbir__info *info, info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); info->decode_buffer_pixels = info->input_w + stbir__get_filter_pixel_margin_horizontal(info) * 2; -#define STBIR__NEXT_MEMPTR(current, old, newtype) (newtype*)(((unsigned char*)current) + old) +#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) info->horizontal_contributors = (stbir__contributors *) tempmem; - info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, stbir__get_horizontal_contributors(info) * sizeof(stbir__contributors), float); - info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__get_total_coefficients(info) * sizeof(float), float); - info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, stbir__get_filter_pixel_width_vertical(info) * sizeof(float), float); + info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); + info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, float); + info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); if (stbir__use_height_upsampling(info)) { info->horizontal_buffer = NULL; - info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, info->decode_buffer_pixels * info->channels * sizeof(float), float); - info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, info->ring_buffer_length_bytes * stbir__get_filter_pixel_width_vertical(info), float); + info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); - STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, info->output_w * info->channels * sizeof(float), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } else { - info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, info->decode_buffer_pixels * info->channels * sizeof(float), float); - info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, info->output_w * info->channels * sizeof(float), float); + info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); info->encode_buffer = NULL; - STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, info->ring_buffer_length_bytes * stbir__get_filter_pixel_width_vertical(info), unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } #undef STBIR__NEXT_MEMPTR From 41e6aad693d1e23dcb06a3a09dc82cf0897c2582 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 21:57:46 -0700 Subject: [PATCH 123/168] Avoid repeated adds and ensure correct round-trip result. --- stb_image_resize.h | 30 +++++++++++++++++++----------- tests/resample_test.cpp | 3 +++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index b6dc994..4965436 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -445,7 +445,7 @@ 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, + 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, @@ -499,16 +499,25 @@ static unsigned char stbir__linear_to_srgb_uchar(float f) { int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp int v = 0; + int i; - if (x >= stbir__srgb_offset_to_linear_scaled[ v+128 ]) v += 128; - if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 64 ]) v += 64; - if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 32 ]) v += 32; - if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 16 ]) v += 16; - if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 8 ]) v += 8; - if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 4 ]) v += 4; - if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 2 ]) v += 2; - if (x >= stbir__srgb_offset_to_linear_scaled[ v+ 1 ]) v += 1; - return (unsigned char) v; + // Everything below 128 is off by 1. This fixes that. + int fix = 0; + + // 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; } static float stbir__filter_box(float x) @@ -1246,7 +1255,6 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void int output_pixel_index = x*channels; int encode_pixel_index = x*channels; - for (n = 0; n < channels; n++) ((unsigned char*)output_buffer)[output_pixel_index + n] = stbir__linear_to_srgb_uchar(encode_buffer[encode_pixel_index + n]); diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 7234048..3ab2a00 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -734,6 +734,9 @@ 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); + #if 0 // linear_to_srgb_uchar table for (i=0; i < 256; ++i) { float f = stbir__srgb_to_linear((i+0.5f)/256.0f); From 1d5c902e2e486511a86916f13726b6963c832077 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 22:07:40 -0700 Subject: [PATCH 124/168] These minor optimizations were probably already done by the compiler but they can't hurt. --- stb_image_resize.h | 79 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 4965436..89edcd0 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1225,14 +1225,13 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void { for (x=0; x < num_pixels; ++x) { - int output_pixel_index = x*channels; - int encode_pixel_index = x*channels; - float alpha = encode_buffer[encode_pixel_index + alpha_channel]; + 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[encode_pixel_index + n] *= reciprocal_alpha; + encode_buffer[pixel_index + n] *= reciprocal_alpha; } } @@ -1241,50 +1240,58 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): for (x=0; x < num_pixels; ++x) { - int output_pixel_index = x*channels; - int encode_pixel_index = x*channels; + int pixel_index = x*channels; for (n = 0; n < channels; n++) - ((unsigned char*)output_buffer)[output_pixel_index + n] = (unsigned char)(round(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 255)); + { + int index = pixel_index + n; + ((unsigned char*)output_buffer)[index] = (unsigned char)(round(stbir__saturate(encode_buffer[index]) * 255)); + } } break; case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): for (x=0; x < num_pixels; ++x) { - int output_pixel_index = x*channels; - int encode_pixel_index = x*channels; + int pixel_index = x*channels; for (n = 0; n < channels; n++) - ((unsigned char*)output_buffer)[output_pixel_index + n] = stbir__linear_to_srgb_uchar(encode_buffer[encode_pixel_index + n]); + { + int index = pixel_index + n; + ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); + } if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned char*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned char)(round(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 255)); + ((unsigned char*)output_buffer)[pixel_index + alpha_channel] = (unsigned char)(round(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 255)); } break; case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): for (x=0; x < num_pixels; ++x) { - int output_pixel_index = x*channels; - int encode_pixel_index = x*channels; + int pixel_index = x*channels; for (n = 0; n < channels; n++) - ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(round(stbir__saturate(encode_buffer[encode_pixel_index + n]) * 65535)); + { + int index = pixel_index + n; + ((unsigned short*)output_buffer)[index] = (unsigned short)(round(stbir__saturate(encode_buffer[index]) * 65535)); + } } break; case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): for (x=0; x < num_pixels; ++x) { - int output_pixel_index = x*channels; - int encode_pixel_index = x*channels; + int pixel_index = x*channels; for (n = 0; n < channels; n++) - ((unsigned short*)output_buffer)[output_pixel_index + n] = (unsigned short)(round(stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n])) * 65535)); + { + int index = pixel_index + n; + ((unsigned short*)output_buffer)[index] = (unsigned short)(round(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535)); + } if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned short*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned short)(round(stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel]) * 65535)); + ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = (unsigned short)(round(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 65535)); } break; @@ -1292,50 +1299,58 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): for (x=0; x < num_pixels; ++x) { - int output_pixel_index = x*channels; - int encode_pixel_index = x*channels; + int pixel_index = x*channels; for (n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(round(((double)stbir__saturate(encode_buffer[encode_pixel_index + n])) * 4294967295)); + { + int index = pixel_index + n; + ((unsigned int*)output_buffer)[index] = (unsigned int)(round(((double)stbir__saturate(encode_buffer[index])) * 4294967295)); + } } break; case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): for (x=0; x < num_pixels; ++x) { - int output_pixel_index = x*channels; - int encode_pixel_index = x*channels; + int pixel_index = x*channels; for (n = 0; n < channels; n++) - ((unsigned int*)output_buffer)[output_pixel_index + n] = (unsigned int)(round(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[encode_pixel_index + n]))) * 4294967295)); + { + int index = pixel_index + n; + ((unsigned int*)output_buffer)[index] = (unsigned int)(round(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295)); + } if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned int*)output_buffer)[output_pixel_index + alpha_channel] = (unsigned int)(round(((double)stbir__saturate(encode_buffer[encode_pixel_index + alpha_channel])) * 4294967295)); + ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)(round(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295)); } break; case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): for (x=0; x < num_pixels; ++x) { - int output_pixel_index = x*channels; - int encode_pixel_index = x*channels; + int pixel_index = x*channels; for (n = 0; n < channels; n++) - ((float*)output_buffer)[output_pixel_index + n] = encode_buffer[encode_pixel_index + n]; + { + int index = pixel_index + n; + ((float*)output_buffer)[index] = encode_buffer[index]; + } } break; case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): for (x=0; x < num_pixels; ++x) { - int output_pixel_index = x*channels; - int encode_pixel_index = x*channels; + int pixel_index = x*channels; for (n = 0; n < channels; n++) - ((float*)output_buffer)[output_pixel_index + n] = stbir__linear_to_srgb(encode_buffer[encode_pixel_index + n]); + { + int index = pixel_index + n; + ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); + } if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((float*)output_buffer)[output_pixel_index + alpha_channel] = encode_buffer[encode_pixel_index + alpha_channel]; + ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; } break; From 17b931047c908f8f9e7c96ec17aef2b5611f81c2 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 22:47:53 -0700 Subject: [PATCH 125/168] Add the epsilon value into the alpha instead of using an if statement. It's a tiny bit faster and it can be removed afterwards. --- stb_image_resize.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 89edcd0..838a579 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1040,10 +1040,9 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++) { int decode_pixel_index = x * channels; - float alpha = decode_buffer[decode_pixel_index + alpha_channel]; - if (alpha == 0) - alpha = decode_buffer[decode_pixel_index + alpha_channel] = (float)1 / 17179869184; // 1/2^34 should be small enough that it won't affect anything. + // 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] += (float)1 / 17179869184); // 1/2^34 should be small enough that it won't affect anything. for (c = 0; c < channels; c++) { @@ -1226,12 +1225,16 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void for (x=0; x < num_pixels; ++x) { 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] -= (float)1 / 17179869184; // 1/2^34 should be small enough that it won't affect anything. } } From c9d67446d27742ca561a1c95fa1cddb6970604a6 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 9 Sep 2014 22:57:15 -0700 Subject: [PATCH 126/168] Reverse allocation routines parameter order so that the context is at the back. --- stb_image_resize.h | 14 +++++++------- tests/resample_test.cpp | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 838a579..e488562 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -7,9 +7,9 @@ #define STBIR_ASSERT(x) to avoid using assert.h. - #define STBIR_MALLOC(context,size) and STBIR_FREE(context,ptr) 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. + #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. QUICK NOTES: Written with emphasis on usage and speed. Only the resize operation is @@ -274,8 +274,8 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int #ifndef STBIR_MALLOC #include -#define STBIR_MALLOC(c,x) malloc(x) -#define STBIR_FREE(c,x) free(x) +#define STBIR_MALLOC(x,c) malloc(x) +#define STBIR_FREE(x,c) free(x) #endif #ifndef _MSC_VER @@ -1831,7 +1831,7 @@ static int stbir__resize_arbitrary( stbir__calculate_transform(&info, s0,t0,s1,t1,transform); stbir__choose_filter(&info, h_filter, v_filter); memory_required = stbir__calculate_memory(&info); - extra_memory = STBIR_MALLOC(alloc_context, memory_required); + extra_memory = STBIR_MALLOC(memory_required, alloc_context); if (!extra_memory) return 0; @@ -1842,7 +1842,7 @@ static int stbir__resize_arbitrary( edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); - STBIR_FREE(alloc_context, extra_memory); + STBIR_FREE(extra_memory, alloc_context); return result; } diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 3ab2a00..f5944e8 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -30,7 +30,7 @@ public: void* memory; } g_context; -void* stbir_malloc(void* context, size_t size) +void* stbir_malloc(size_t size, void* context) { if (!context) return malloc(size); @@ -42,7 +42,7 @@ void* stbir_malloc(void* context, size_t size) return real_context->memory; } -void stbir_free(void* context, void* memory) +void stbir_free(void* memory, void* context) { if (!context) return free(memory); From 69226281065025ded2a831907432af7139694f83 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 10 Sep 2014 15:03:55 -0700 Subject: [PATCH 127/168] Use an even smaller epsilon. Allow the user to avoid the epsilon business if they want to preserve their alpha values. --- stb_image_resize.h | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index e488562..dcf5394 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -241,6 +241,15 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int #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_EPSILON +#define STBIR_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) +#endif + // // //// end header file ///////////////////////////////////////////////////// @@ -1035,6 +1044,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) break; } +#ifndef STBIR_NO_ALPHA_EPSILON if (!(stbir_info->flags & STBIR_FLAG_PREMULTIPLIED_ALPHA)) { for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++) @@ -1042,7 +1052,7 @@ 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] += (float)1 / 17179869184); // 1/2^34 should be small enough that it won't affect anything. + float alpha = (decode_buffer[decode_pixel_index + alpha_channel] += STBIR_EPSILON); for (c = 0; c < channels; c++) { @@ -1053,6 +1063,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) } } } +#endif if (edge_horizontal == STBIR_EDGE_ZERO) { @@ -1220,7 +1231,8 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void int x; int n; - if (!(stbir_info->flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) +#ifndef STBIR_NO_ALPHA_EPSILON + if (!(stbir_info->flags&STBIR_FLAG_PREMULTIPLIED_ALPHA)) { for (x=0; x < num_pixels; ++x) { @@ -1234,9 +1246,10 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void 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] -= (float)1 / 17179869184; // 1/2^34 should be small enough that it won't affect anything. + encode_buffer[pixel_index + alpha_channel] -= STBIR_EPSILON; } } +#endif switch (decode) { From 4e580cf9edb2213635fb6dff9295c6975ca98d6e Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Wed, 10 Sep 2014 15:11:35 -0700 Subject: [PATCH 128/168] 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 953a6378417da760544c7e4ca2bfdebaeffe3fcf Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 10 Sep 2014 19:08:11 -0700 Subject: [PATCH 129/168] Use a carefully shaped trapezoid instead of a box filter to avoid jumps between pixel values. --- stb_image_resize.h | 109 ++++++++++++++++++++++++++++++++------------- tests/resample_test.cpp | 116 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 170 insertions(+), 55 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index dcf5394..35517c3 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -139,7 +139,7 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels typedef enum { STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses - STBIR_FILTER_BOX = 1, + STBIR_FILTER_BOX = 1, // Is actually a trapezoid. See https://developer.nvidia.com/content/non-power-two-mipmapping STBIR_FILTER_BILINEAR = 2, STBIR_FILTER_BICUBIC = 3, // A cubic b spline STBIR_FILTER_CATMULLROM = 4, @@ -321,6 +321,8 @@ typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1] #define STBIR_MAX_CHANNELS 16 #endif +#define STBIR__UNUSED_PARAM(s) s=s; + // must match stbir_datatype static unsigned char stbir__type_size[] = { 1, // STBIR_TYPE_UINT8 @@ -330,12 +332,13 @@ static unsigned char stbir__type_size[] = { }; // Kernel function centered at 0 -typedef float (stbir__kernel_fn)(float x); +typedef float (stbir__kernel_fn)(float x, float scale); +typedef float (stbir__support_fn)(float scale); typedef struct { stbir__kernel_fn* kernel; - float support; + stbir__support_fn* support; } stbir__filter_info; // When upsampling, the contributors are which source pixels contribute. @@ -529,18 +532,36 @@ static unsigned char stbir__linear_to_srgb_uchar(float f) return (unsigned char)v + fix; } -static float stbir__filter_box(float x) +static float stbir__filter_trapezoid(float x, float scale) { - if (x <= -0.5f) - return 0; - else if (x > 0.5f) + STBIR__DEBUG_ASSERT(scale <= 1); + x = (float)fabs(x); + + float halfscale = scale / 2; + float t = 0.5f + halfscale; + + if (x >= t) return 0; else - return 1; + { + float r = 0.5f - halfscale; + if (x <= r) + return 1; + else + return (t - x) / scale; + } } -static float stbir__filter_bilinear(float x) +static float stbir__support_trapezoid(float scale) { + STBIR__DEBUG_ASSERT(scale <= 1); + return 0.5f + scale / 2; +} + +static float stbir__filter_bilinear(float x, float s) +{ + STBIR__UNUSED_PARAM(s) + x = (float)fabs(x); if (x <= 1.0f) @@ -549,8 +570,10 @@ static float stbir__filter_bilinear(float x) return 0; } -static float stbir__filter_bicubic(float x) +static float stbir__filter_bicubic(float x, float s) { + STBIR__UNUSED_PARAM(s) + x = (float)fabs(x); if (x < 1.0f) @@ -561,8 +584,10 @@ static float stbir__filter_bicubic(float x) return (0.0f); } -static float stbir__filter_catmullrom(float x) +static float stbir__filter_catmullrom(float x, float s) { + STBIR__UNUSED_PARAM(s) + x = (float)fabs(x); if (x < 1.0f) @@ -573,8 +598,10 @@ static float stbir__filter_catmullrom(float x) return (0.0f); } -static float stbir__filter_mitchell(float x) +static float stbir__filter_mitchell(float x, float s) { + STBIR__UNUSED_PARAM(s) + x = (float)fabs(x); if (x < 1.0f) @@ -585,13 +612,31 @@ static float stbir__filter_mitchell(float x) return (0.0f); } +static float stbir__support_zero(float s) +{ + STBIR__UNUSED_PARAM(s) + return 0; +} + +static float stbir__support_one(float s) +{ + STBIR__UNUSED_PARAM(s) + return 1; +} + +static float stbir__support_two(float s) +{ + STBIR__UNUSED_PARAM(s) + return 2; +} + static stbir__filter_info stbir__filter_info_table[] = { - { NULL, 0.0f }, - { stbir__filter_box , 0.5f }, - { stbir__filter_bilinear, 1.0f }, - { stbir__filter_bicubic, 2.0f }, - { stbir__filter_catmullrom, 2.0f }, - { stbir__filter_mitchell, 2.0f }, + { NULL, stbir__support_zero }, + { stbir__filter_trapezoid, stbir__support_trapezoid }, + { stbir__filter_bilinear, stbir__support_one }, + { stbir__filter_bicubic, stbir__support_two }, + { stbir__filter_catmullrom, stbir__support_two }, + { stbir__filter_mitchell, stbir__support_two }, }; stbir__inline static int stbir__use_upsampling(float ratio) @@ -617,9 +662,9 @@ stbir__inline static int stbir__get_filter_pixel_width(stbir_filter filter, floa STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support * 2); + return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); else - return (int)ceil(stbir__filter_info_table[filter].support * 2 / scale); + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); } stbir__inline static int stbir__get_filter_pixel_width_horizontal(stbir__info* stbir_info) @@ -771,13 +816,13 @@ static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radi *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); } -static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbir_filter filter, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) +static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) { int i; float total_filter = 0; float filter_scale; - STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support * 2)); // Taken directly from stbir__get_filter_pixel_width() which we can't call because we don't know if we're horizontal or vertical. + STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_filter_pixel_width() which we can't call because we don't know if we're horizontal or vertical. contributor->n0 = in_first_pixel; contributor->n1 = in_last_pixel; @@ -787,7 +832,7 @@ static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbi for (i = 0; i <= in_last_pixel - in_first_pixel; i++) { float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; - total_filter += coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center); + total_filter += coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); } STBIR__DEBUG_ASSERT(total_filter > 0.9); @@ -804,7 +849,7 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st { int i; - STBIR__DEBUG_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support * 2 / scale_ratio)); // Taken directly from stbir__get_filter_pixel_width() which we can't call because we don't know if we're horizontal or vertical. + STBIR__DEBUG_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2 / scale_ratio)); // Taken directly from stbir__get_filter_pixel_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; @@ -815,7 +860,7 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st { float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; float x = out_pixel_center - out_center_of_in; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(x) * scale_ratio; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; } } @@ -873,7 +918,7 @@ static void stbir__calculate_horizontal_filters(stbir__info* stbir_info) if (stbir__use_width_upsampling(stbir_info)) { - float out_pixels_radius = stbir__filter_info_table[stbir_info->horizontal_filter].support * scale_ratio; + float out_pixels_radius = stbir__filter_info_table[stbir_info->horizontal_filter].support(1/scale_ratio) * scale_ratio; // Looping through out pixels for (n = 0; n < total_contributors; n++) @@ -883,12 +928,12 @@ static void stbir__calculate_horizontal_filters(stbir__info* stbir_info) stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, stbir_info->horizontal_shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); - stbir__calculate_coefficients_upsample(stbir_info, stbir_info->horizontal_filter, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(stbir_info, n), stbir__get_coefficient(stbir_info, n, 0)); + stbir__calculate_coefficients_upsample(stbir_info, stbir_info->horizontal_filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(stbir_info, n), stbir__get_coefficient(stbir_info, n, 0)); } } else { - float in_pixels_radius = stbir__filter_info_table[stbir_info->horizontal_filter].support / scale_ratio; + float in_pixels_radius = stbir__filter_info_table[stbir_info->horizontal_filter].support(scale_ratio) / scale_ratio; // Looping through in pixels for (n = 0; n < total_contributors; n++) @@ -1399,7 +1444,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int n0,n1, output_row_start; - stbir__calculate_coefficients_upsample(stbir_info, stbir_info->vertical_filter, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); + stbir__calculate_coefficients_upsample(stbir_info, stbir_info->vertical_filter, stbir_info->vertical_scale, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); n0 = vertical_contributors->n0; n1 = vertical_contributors->n1; @@ -1427,7 +1472,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int c; for (c = 0; c < channels; c++) - encode_buffer[x*channels + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; + encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; } } stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); @@ -1486,7 +1531,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info) { int y; float scale_ratio = stbir_info->vertical_scale; - float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support * scale_ratio; + float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); @@ -1585,7 +1630,7 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info) int y; float scale_ratio = stbir_info->vertical_scale; int output_h = stbir_info->output_h; - float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support / scale_ratio; + float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; int pixel_margin = stbir__get_filter_pixel_margin_vertical(stbir_info); int max_y = stbir_info->input_h + pixel_margin; diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index f5944e8..1a19850 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -48,8 +48,10 @@ void stbir_free(void* memory, void* context) return free(memory); } +//#include void stbir_progress(float p) { + //printf("%f\n", p); STBIR_ASSERT(p >= 0 && p <= 1); } @@ -373,7 +375,7 @@ void test_subpixel(const char* file, float width_percent, float height_percent, unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - stbir_resize_region(input_data, w, h, 0, output_data, new_w, new_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0, 0, s1, t1); + stbir_resize_region(input_data, w, h, 0, output_data, new_w, new_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0, 0, s1, t1); stbi_image_free(input_data); @@ -684,6 +686,72 @@ void test_filters(void) for (j = 0; j < 11 * 11; ++j) STBIR_ASSERT(v == output[j]); } + + { + // Now test the trapezoid filter for downsampling. + unsigned int input[3 * 1]; + unsigned int output[2 * 1]; + + input[0] = 0; + input[1] = 255; + input[2] = 127; + + 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_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)); + } + + { + // Now test the trapezoid filter for upsampling. + unsigned int input[2 * 1]; + unsigned int output[3 * 1]; + + input[0] = 0; + input[1] = 255; + + stbir_resize(input, 2, 1, 0, output, 3, 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] == input[0]); + STBIR_ASSERT(output[1] == (input[0] + input[1]) / 2); + STBIR_ASSERT(output[2] == input[1]); + + stbir_resize(input, 1, 2, 0, output, 1, 3, 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] == input[0]); + STBIR_ASSERT(output[1] == (input[0] + input[1]) / 2); + STBIR_ASSERT(output[2] == input[1]); + } + + { + // Now for some fun. + unsigned char input[2 * 1]; + unsigned char output[127 * 1]; + + input[0] = 0; + input[1] = 255; + + stbir_resize(input, 2, 1, 0, output, 127, 1, 0, STBIR_TYPE_UINT8, 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] == 0); + STBIR_ASSERT(output[127 / 2 - 1] == 0); + STBIR_ASSERT(output[127 / 2] == 128); + STBIR_ASSERT(output[127 / 2 + 1] == 255); + STBIR_ASSERT(output[126] == 255); + stbi_write_png("test-output/trapezoid-upsample-horizontal.png", 127, 1, 1, output, 0); + + stbir_resize(input, 1, 2, 0, output, 1, 127, 0, STBIR_TYPE_UINT8, 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] == 0); + STBIR_ASSERT(output[127 / 2 - 1] == 0); + STBIR_ASSERT(output[127 / 2] == 128); + STBIR_ASSERT(output[127 / 2 + 1] == 255); + STBIR_ASSERT(output[126] == 255); + stbi_write_png("test-output/trapezoid-upsample-vertical.png", 1, 127, 1, output, 0); + } } @@ -699,52 +767,54 @@ void test_suite(int argc, char **argv) else barbara = "barbara.png"; - #if 1 +#if 1 { - float x,y; + float x, y; for (x = -1; x < 1; x += 0.05f) { - float sums[4] = {0}; + float sums[5] = { 0 }; float o; - for (o=-5; o <= 5; ++o) { - sums[0] += stbir__filter_mitchell(x+o); - sums[1] += stbir__filter_catmullrom(x+o); - sums[2] += stbir__filter_bicubic(x+o); - sums[3] += stbir__filter_bilinear(x+o); + for (o = -5; o <= 5; ++o) { + sums[0] += stbir__filter_mitchell(x + o, 1); + sums[1] += stbir__filter_catmullrom(x + o, 1); + sums[2] += stbir__filter_bicubic(x + o, 1); + sums[3] += stbir__filter_bilinear(x + o, 1); + sums[4] += stbir__filter_trapezoid(x + o, 0.5f); } - for (i=0; i < 4; ++i) + for (i = 0; i < 5; ++i) STBIR_ASSERT(sums[i] >= 1.0 - 0.001 && sums[i] <= 1.0 + 0.001); } - #if 1 +#if 1 for (y = 0.11f; y < 1; y += 0.01f) { // Step for (x = -1; x < 1; x += 0.05f) { // Phase - float sums[4] = {0}; + float sums[5] = { 0 }; float o; - for (o=-5; o <= 5; o += y) { - sums[0] += y * stbir__filter_mitchell(x+o); - sums[1] += y * stbir__filter_catmullrom(x+o); - sums[2] += y * stbir__filter_bicubic(x+o); - sums[3] += y * stbir__filter_bilinear(x+o); + for (o = -5; o <= 5; o += y) { + sums[0] += y * stbir__filter_mitchell(x + o, 1); + sums[1] += y * stbir__filter_catmullrom(x + o, 1); + sums[2] += y * stbir__filter_bicubic(x + o, 1); + sums[4] += y * stbir__filter_trapezoid(x + o, 0.5f); + sums[3] += y * stbir__filter_bilinear(x + o, 1); } - for (i=0; i < 3; ++i) + for (i = 0; i < 3; ++i) STBIR_ASSERT(sums[i] >= 1.0 - 0.0170 && sums[i] <= 1.0 + 0.0170); } } - #endif +#endif } - #endif +#endif for (i = 0; i < 256; i++) - STBIR_ASSERT(stbir__linear_to_srgb_uchar(stbir__srgb_to_linear(float(i)/255)) == i); + STBIR_ASSERT(stbir__linear_to_srgb_uchar(stbir__srgb_to_linear(float(i) / 255)) == i); - #if 0 // linear_to_srgb_uchar table +#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 +#endif test_filters(); From 46dc8f84fb2fbc2c95b4cd8aa73daf1c9297ac39 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 10 Sep 2014 20:12:38 -0700 Subject: [PATCH 130/168] Use vertical pixel width for the ring buffer. --- stb_image_resize.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 35517c3..f454286 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1561,7 +1561,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info) else { stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir__get_filter_pixel_width_horizontal(stbir_info); + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir__get_filter_pixel_width_vertical(stbir_info); } } } From 793818d1af46128c6b987a0c3bdb8424a00b2377 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Wed, 10 Sep 2014 20:26:14 -0700 Subject: [PATCH 131/168] Skip zero-coefficient contributors, a fairly decent speedup. --- stb_image_resize.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/stb_image_resize.h b/stb_image_resize.h index f454286..5315820 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -843,6 +843,15 @@ static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbi for (i = 0; i <= in_last_pixel - in_first_pixel; i++) coefficient_group[i] *= filter_scale; + + for (i = in_last_pixel - in_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } } static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) @@ -862,6 +871,15 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st float x = out_pixel_center - out_center_of_in; coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; } + + for (i = out_last_pixel - out_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } } static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) From 30c7a981ec335c2267b3cec2955cde42376b8fd9 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Thu, 11 Sep 2014 01:47:50 -0700 Subject: [PATCH 132/168] 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 133/168] 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 134/168] 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 135/168] 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) From 36b417f6b4cd3da9abd3b66a1448b9dbccebd329 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 11 Sep 2014 09:39:03 -0700 Subject: [PATCH 136/168] Optimizations: Skip zero-coefficient contributions. Reduce the size of the coefficients array when downsampling. --- stb_image_resize.h | 99 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 19 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 5315820..3ac111f 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -694,6 +694,14 @@ stbir__inline static int stbir__get_filter_pixel_margin_vertical(stbir__info* st return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->vertical_scale) / 2; } +stbir__inline static int stbir__get_coefficient_width(stbir_filter filter, float scale) +{ + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); +} + stbir__inline static int stbir__get_horizontal_contributors(stbir__info* info) { if (stbir__use_upsampling(info->horizontal_scale)) @@ -705,7 +713,7 @@ stbir__inline static int stbir__get_horizontal_contributors(stbir__info* info) stbir__inline static int stbir__get_total_coefficients(stbir__info* info) { return stbir__get_horizontal_contributors(info) - * stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); + * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); } stbir__inline static stbir__contributors* stbir__get_contributor(stbir__info* stbir_info, int n) @@ -714,9 +722,13 @@ stbir__inline static stbir__contributors* stbir__get_contributor(stbir__info* st return &stbir_info->horizontal_contributors[n]; } +// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, +// if you change it here change it there too. stbir__inline static float* stbir__get_coefficient(stbir__info* stbir_info, int n, int c) { - int width = stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); + int width = stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); + STBIR__DEBUG_ASSERT(c >= 0 && c < width); + STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_total_coefficients(stbir_info)); return &stbir_info->horizontal_coefficients[width*n + c]; } @@ -822,7 +834,7 @@ static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbi float total_filter = 0; float filter_scale; - STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_filter_pixel_width() which we can't call because we don't know if we're horizontal or vertical. + STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 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 = in_first_pixel; contributor->n1 = in_last_pixel; @@ -832,9 +844,21 @@ static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbi for (i = 0; i <= in_last_pixel - in_first_pixel; i++) { float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; - total_filter += coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); + coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); + + // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) + if (i == 0 && !coefficient_group[i]) + { + contributor->n0 = ++in_first_pixel; + i--; + continue; + } + + total_filter += coefficient_group[i]; } + STBIR__DEBUG_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); + STBIR__DEBUG_ASSERT(total_filter > 0.9); STBIR__DEBUG_ASSERT(total_filter < 1.1f); // Make sure it's not way off. @@ -858,7 +882,7 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st { int i; - STBIR__DEBUG_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2 / scale_ratio)); // Taken directly from stbir__get_filter_pixel_width() which we can't call because we don't know if we're horizontal or vertical. + STBIR__DEBUG_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; @@ -872,6 +896,8 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; } + STBIR__DEBUG_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--) { if (coefficient_group[i]) @@ -885,11 +911,14 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) { int num_contributors = stbir__get_horizontal_contributors(stbir_info); - int i; + int num_coefficients = stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); + int i, j; + int skip; + for (i = 0; i < stbir_info->output_w; i++) { float total = 0; - int j; + for (j = 0; j < num_contributors; j++) { if (i >= stbir_info->horizontal_contributors[j].n0 && i <= stbir_info->horizontal_contributors[j].n1) @@ -915,11 +944,42 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) } } + // Optimize: Skip zero coefficients and contributions outside of image bounds. + // Do this after normalizing because normalization depends on the n0/n1 values. + for (j = 0; j < num_contributors; j++) + { + int range, max; + + skip = 0; + while (*stbir__get_coefficient(stbir_info, j, skip) == 0) + skip++; + + stbir_info->horizontal_contributors[j].n0 += skip; + + while (stbir_info->horizontal_contributors[j].n0 < 0) + { + stbir_info->horizontal_contributors[j].n0++; + skip++; + } + + range = stbir_info->horizontal_contributors[j].n1 - stbir_info->horizontal_contributors[j].n0 + 1; + max = stbir__min(num_coefficients, range); + + for (i = 0; i < max; i++) + { + if (i + skip >= stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale)) + break; + + *stbir__get_coefficient(stbir_info, j, i) = *stbir__get_coefficient(stbir_info, j, i + skip); + } + + continue; + } + // Using min to avoid writing into invalid pixels. for (i = 0; i < num_contributors; i++) { stbir__contributors* contributors = &stbir_info->horizontal_contributors[i]; - STBIR__DEBUG_ASSERT(contributors->n1 >= contributors->n0); contributors->n1 = stbir__min(contributors->n1, stbir_info->output_w - 1); } @@ -1182,6 +1242,7 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, float* decode_buffer = stbir__get_decode_buffer(stbir_info); stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); for (x = 0; x < output_w; x++) { @@ -1189,7 +1250,7 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, int n1 = horizontal_contributors[x].n1; int out_pixel_index = x * channels; - int coefficient_group_index = x * kernel_pixel_width; + int coefficient_group = coefficient_width * x; int coefficient_counter = 0; STBIR__DEBUG_ASSERT(n1 >= n0); @@ -1200,9 +1261,10 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, for (k = n0; k <= n1; k++) { - int coefficient_index = coefficient_group_index + (coefficient_counter++); int in_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_index]; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + + STBIR__DEBUG_ASSERT(coefficient != 0); int c; for (c = 0; c < channels; c++) @@ -1221,6 +1283,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n float* decode_buffer = stbir__get_decode_buffer(stbir_info); stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); int filter_pixel_margin = stbir__get_filter_pixel_margin_horizontal(stbir_info); int max_x = input_w + filter_pixel_margin * 2; @@ -1234,14 +1297,14 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n int in_x = x - filter_pixel_margin; int in_pixel_index = in_x * channels; int max_n = n1; - int coefficient_group = x*kernel_pixel_width; + int coefficient_group = coefficient_width * x; - // Using max to avoid writing into invalid pixels. - for (k = stbir__max(n0, 0); k <= max_n; k++) + for (k = n0; k <= max_n; k++) { - int coefficient_index = (k - n0) + coefficient_group; int out_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_index]; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + + STBIR__DEBUG_ASSERT(coefficient != 0); int c; for (c = 0; c < channels; c++) @@ -1524,10 +1587,8 @@ static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); STBIR__DEBUG_ASSERT(n0 >= in_first_scanline); STBIR__DEBUG_ASSERT(n1 <= in_last_scanline); - STBIR__DEBUG_ASSERT(n1 >= n0); - // Using min and max to avoid writing into ring buffers that will be thrown out. - for (k = stbir__max(n0, 0); k <= max_n; k++) + for (k = n0; k <= max_n; k++) { int coefficient_index = k - n0; From ca88b6fba6845077eb801179d5a43712afceba0e Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Thu, 11 Sep 2014 12:45:18 -0700 Subject: [PATCH 137/168] Calculate and store vertical contributors first so that they can be normalized and optimized. --- stb_image_resize.h | 184 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 108 insertions(+), 76 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index a409cba..8e8b1a8 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -463,7 +463,7 @@ typedef struct stbir__contributors* horizontal_contributors; float* horizontal_coefficients; - stbir__contributors vertical_contributors; + stbir__contributors* vertical_contributors; float* vertical_coefficients; int decode_buffer_pixels; @@ -481,6 +481,7 @@ typedef struct int horizontal_contributors_size; int horizontal_coefficients_size; + int vertical_contributors_size; int vertical_coefficients_size; int decode_buffer_size; int horizontal_buffer_size; @@ -777,34 +778,73 @@ stbir__inline static int stbir__get_coefficient_width(stbir_filter filter, float return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); } -stbir__inline static int stbir__get_horizontal_contributors(stbir__info* info) +stbir__inline static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) { - if (stbir__use_upsampling(info->horizontal_scale)) - return info->output_w; + if (stbir__use_upsampling(scale)) + return output_size; else - return (info->input_w + stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale) * 2); + return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); } -stbir__inline static int stbir__get_total_coefficients(stbir__info* info) +stbir__inline static int stbir__get_horizontal_contributors(stbir__info* info) +{ + return stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); +} + +stbir__inline static int stbir__get_vertical_contributors(stbir__info* info) +{ + return stbir__get_contributors(info->vertical_scale, info->vertical_filter, info->input_h, info->output_h); +} + +stbir__inline static int stbir__get_total_horizontal_coefficients(stbir__info* info) { return stbir__get_horizontal_contributors(info) * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); } -stbir__inline static stbir__contributors* stbir__get_contributor(stbir__info* stbir_info, int n) +stbir__inline static int stbir__get_total_vertical_coefficients(stbir__info* info) +{ + return stbir__get_vertical_contributors(info) + * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); +} + +stbir__inline static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) +{ + return &contributors[n]; +} + +stbir__inline static stbir__contributors* stbir__get_horizontal_contributor(stbir__info* stbir_info, int n) { STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_horizontal_contributors(stbir_info)); - return &stbir_info->horizontal_contributors[n]; + return stbir__get_contributor(stbir_info->horizontal_contributors, n); +} + +stbir__inline static stbir__contributors* stbir__get_vertical_contributor(stbir__info* stbir_info, int n) +{ + STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_vertical_contributors(stbir_info)); + return stbir__get_contributor(stbir_info->vertical_contributors, n); } // For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, // if you change it here change it there too. -stbir__inline static float* stbir__get_coefficient(stbir__info* stbir_info, int n, int c) +stbir__inline static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) { - int width = stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); - STBIR__DEBUG_ASSERT(c >= 0 && c < width); - STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_total_coefficients(stbir_info)); - return &stbir_info->horizontal_coefficients[width*n + c]; + int width = stbir__get_coefficient_width(filter, scale); + return &coefficients[width*n + c]; +} + +stbir__inline static float* stbir__get_horizontal_coefficient(stbir__info* stbir_info, int n, int c) +{ + STBIR__DEBUG_ASSERT(c >= 0 && c < stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale)); + STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_total_horizontal_coefficients(stbir_info)); + return stbir__get_coefficient(stbir_info->horizontal_coefficients, stbir_info->horizontal_filter, stbir_info->horizontal_scale, n, c); +} + +stbir__inline static float* stbir__get_vertical_coefficient(stbir__info* stbir_info, int n, int c) +{ + STBIR__DEBUG_ASSERT(c >= 0 && c < stbir__get_coefficient_width(stbir_info->vertical_filter, stbir_info->vertical_scale)); + STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_total_vertical_coefficients(stbir_info)); + return stbir__get_coefficient(stbir_info->vertical_coefficients, stbir_info->vertical_filter, stbir_info->vertical_scale, n, c); } static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) @@ -983,26 +1023,26 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st } } -static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) +static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) { - int num_contributors = stbir__get_horizontal_contributors(stbir_info); - int num_coefficients = stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); + int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); int i, j; int skip; - for (i = 0; i < stbir_info->output_w; i++) + for (i = 0; i < output_size; i++) { float scale; float total = 0; for (j = 0; j < num_contributors; j++) { - if (i >= stbir_info->horizontal_contributors[j].n0 && i <= stbir_info->horizontal_contributors[j].n1) + if (i >= contributors[j].n0 && i <= contributors[j].n1) { - float coefficient = *stbir__get_coefficient(stbir_info, j, i - stbir_info->horizontal_contributors[j].n0); + float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); total += coefficient; } - else if (i < stbir_info->horizontal_contributors[j].n0) + else if (i < contributors[j].n0) break; } @@ -1013,9 +1053,9 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) for (j = 0; j < num_contributors; j++) { - if (i >= stbir_info->horizontal_contributors[j].n0 && i <= stbir_info->horizontal_contributors[j].n1) - *stbir__get_coefficient(stbir_info, j, i - stbir_info->horizontal_contributors[j].n0) *= scale; - else if (i < stbir_info->horizontal_contributors[j].n0) + if (i >= contributors[j].n0 && i <= contributors[j].n1) + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; + else if (i < contributors[j].n0) break; } } @@ -1027,26 +1067,26 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) int range, max; skip = 0; - while (*stbir__get_coefficient(stbir_info, j, skip) == 0) + while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) skip++; - stbir_info->horizontal_contributors[j].n0 += skip; + contributors[j].n0 += skip; - while (stbir_info->horizontal_contributors[j].n0 < 0) + while (contributors[j].n0 < 0) { - stbir_info->horizontal_contributors[j].n0++; + contributors[j].n0++; skip++; } - range = stbir_info->horizontal_contributors[j].n1 - stbir_info->horizontal_contributors[j].n0 + 1; + range = contributors[j].n1 - contributors[j].n0 + 1; max = stbir__min(num_coefficients, range); for (i = 0; i < max; i++) { - if (i + skip >= stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale)) + if (i + skip >= stbir__get_coefficient_width(filter, scale_ratio)) break; - *stbir__get_coefficient(stbir_info, j, i) = *stbir__get_coefficient(stbir_info, j, i + skip); + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); } continue; @@ -1054,25 +1094,19 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info) // Using min to avoid writing into invalid pixels. for (i = 0; i < num_contributors; i++) - { - stbir__contributors* contributors = &stbir_info->horizontal_contributors[i]; - - contributors->n1 = stbir__min(contributors->n1, stbir_info->output_w - 1); - } + contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); } // Each scan line uses the same kernel values so we should calculate the kernel // values once and then we can use them for every scan line. -static void stbir__calculate_horizontal_filters(stbir__info* stbir_info) +static void stbir__calculate_filters(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) { int n; - float scale_ratio = stbir_info->horizontal_scale; + int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - int total_contributors = stbir__get_horizontal_contributors(stbir_info); - - if (stbir__use_width_upsampling(stbir_info)) + if (stbir__use_upsampling(scale_ratio)) { - float out_pixels_radius = stbir__filter_info_table[stbir_info->horizontal_filter].support(1/scale_ratio) * scale_ratio; + float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; // Looping through out pixels for (n = 0; n < total_contributors; n++) @@ -1080,28 +1114,28 @@ static void stbir__calculate_horizontal_filters(stbir__info* stbir_info) float in_center_of_out; // Center of the current out pixel in the in pixel space int in_first_pixel, in_last_pixel; - stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, stbir_info->horizontal_shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); + stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); - stbir__calculate_coefficients_upsample(stbir_info, stbir_info->horizontal_filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(stbir_info, n), stbir__get_coefficient(stbir_info, n, 0)); + stbir__calculate_coefficients_upsample(stbir_info, filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); } } else { - float in_pixels_radius = stbir__filter_info_table[stbir_info->horizontal_filter].support(scale_ratio) / scale_ratio; + float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; // Looping through in pixels for (n = 0; n < total_contributors; n++) { float out_center_of_in; // Center of the current out pixel in the in pixel space int out_first_pixel, out_last_pixel; - int n_adjusted = n - stbir__get_filter_pixel_margin_horizontal(stbir_info); + int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); - stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, stbir_info->horizontal_shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); + stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); - stbir__calculate_coefficients_downsample(stbir_info, stbir_info->horizontal_filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(stbir_info, n), stbir__get_coefficient(stbir_info, n, 0)); + stbir__calculate_coefficients_downsample(stbir_info, filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); } - stbir__normalize_downsample_coefficients(stbir_info); + stbir__normalize_downsample_coefficients(stbir_info, contributors, coefficients, filter, scale_ratio, shift, input_size, output_size); } } @@ -1585,7 +1619,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in { int x, k; int output_w = stbir_info->output_w; - stbir__contributors* vertical_contributors = &stbir_info->vertical_contributors; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; float* vertical_coefficients = stbir_info->vertical_coefficients; int channels = stbir_info->channels; int alpha_channel = stbir_info->alpha_channel; @@ -1595,6 +1629,8 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in void* output_data = stbir_info->output_data; float* encode_buffer = stbir_info->encode_buffer; int decode = STBIR__DECODE(type, colorspace); + int coefficient_width = stbir__get_coefficient_width(stbir_info->vertical_filter, stbir_info->vertical_scale); + int contributor = n; float* ring_buffer = stbir_info->ring_buffer; int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; @@ -1603,17 +1639,14 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); int n0,n1, output_row_start; + int coefficient_group = coefficient_width * contributor; - stbir__calculate_coefficients_upsample(stbir_info, stbir_info->vertical_filter, stbir_info->vertical_scale, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); - - n0 = vertical_contributors->n0; - n1 = vertical_contributors->n1; + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; output_row_start = n * stbir_info->output_stride_bytes; STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); - STBIR__DEBUG_ASSERT(n0 >= in_first_scanline); - STBIR__DEBUG_ASSERT(n1 <= in_last_scanline); memset(encode_buffer, 0, output_w * sizeof(float) * channels); @@ -1622,13 +1655,11 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int in_pixel_index = x * channels; int coefficient_counter = 0; - STBIR__DEBUG_ASSERT(n1 >= n0); - for (k = n0; k <= n1; k++) { int coefficient_index = coefficient_counter++; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_index]; + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; int c; for (c = 0; c < channels; c++) @@ -1643,36 +1674,34 @@ static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, int x, k; int output_w = stbir_info->output_w; int output_h = stbir_info->output_h; - stbir__contributors* vertical_contributors = &stbir_info->vertical_contributors; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; float* vertical_coefficients = stbir_info->vertical_coefficients; int channels = stbir_info->channels; int kernel_pixel_width = stbir__get_filter_pixel_width_vertical(stbir_info); void* output_data = stbir_info->output_data; float* horizontal_buffer = stbir_info->horizontal_buffer; + int coefficient_width = stbir__get_coefficient_width(stbir_info->vertical_filter, stbir_info->vertical_scale); + int contributor = n + stbir__get_filter_pixel_margin_vertical(stbir_info); float* ring_buffer = stbir_info->ring_buffer; int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - int n0,n1,max_n; + int n0,n1; - stbir__calculate_coefficients_downsample(stbir_info, stbir_info->vertical_filter, stbir_info->vertical_scale, in_first_scanline, in_last_scanline, in_center_of_out, vertical_contributors, vertical_coefficients); - - n0 = vertical_contributors->n0; - n1 = vertical_contributors->n1; - max_n = stbir__min(n1, output_h - 1); + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); - STBIR__DEBUG_ASSERT(n0 >= in_first_scanline); - STBIR__DEBUG_ASSERT(n1 <= in_last_scanline); - for (k = n0; k <= max_n; k++) + for (k = n0; k <= n1; k++) { int coefficient_index = k - n0; + int coefficient_group = coefficient_width * contributor; + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_index]; for (x = 0; x < output_w; x++) { @@ -1873,8 +1902,9 @@ static stbir_uint32 stbir__calculate_memory(stbir__info *info) int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); info->horizontal_contributors_size = stbir__get_horizontal_contributors(info) * sizeof(stbir__contributors); - info->horizontal_coefficients_size = stbir__get_total_coefficients(info) * sizeof(float); - info->vertical_coefficients_size = filter_height * sizeof(float); + info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); + info->vertical_contributors_size = stbir__get_vertical_contributors(info) * sizeof(stbir__contributors); + info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); info->ring_buffer_size = info->output_w * info->channels * filter_height * sizeof(float); @@ -1896,9 +1926,9 @@ static stbir_uint32 stbir__calculate_memory(stbir__info *info) info->encode_buffer_size = 0; return info->horizontal_contributors_size + info->horizontal_coefficients_size - + info->vertical_coefficients_size + info->decode_buffer_size - + info->horizontal_buffer_size + info->ring_buffer_size - + info->encode_buffer_size; + + info->vertical_contributors_size + info->vertical_coefficients_size + + info->decode_buffer_size + info->horizontal_buffer_size + + info->ring_buffer_size + info->encode_buffer_size; } static int stbir__resize_allocated(stbir__info *info, @@ -1981,7 +2011,8 @@ static int stbir__resize_allocated(stbir__info *info, info->horizontal_contributors = (stbir__contributors *) tempmem; info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); - info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, float); + info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); + info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); if (stbir__use_height_upsampling(info)) @@ -2006,7 +2037,8 @@ static int stbir__resize_allocated(stbir__info *info, // This signals that the ring buffer is empty info->ring_buffer_begin_index = -1; - stbir__calculate_horizontal_filters(info); + stbir__calculate_filters(info, info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); + stbir__calculate_filters(info, info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); STBIR_PROGRESS_REPORT(0); From df128b7995a3117a4b357b989eb325955d5e7a38 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Fri, 12 Sep 2014 03:53:36 -0700 Subject: [PATCH 138/168] update STBIR__UNUSED_PARAM to match stb_image.h version --- stb_image_resize.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 7a228f9..8c648d7 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -403,7 +403,11 @@ typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1] -#define STBIR__UNUSED_PARAM(s) s=s; +#ifdef _MSC_VER +#define STBIR__UNUSED_PARAM(v) (void)(v) +#else +#define STBIR__UNUSED_PARAM(v) (void)sizeof(v) +#endif // must match stbir_datatype static unsigned char stbir__type_size[] = { From a9778b8dbd2313ec67bc41bdb1d8268d11c9f588 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Fri, 12 Sep 2014 04:46:41 -0700 Subject: [PATCH 139/168] tweak docs; fix compile error in UNUSED_PARAM stuff --- stb_image_resize.h | 44 +++++++++++++++++++++++++++----------------- tests/resample_test.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 9085783..0fdf47d 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -47,13 +47,19 @@ For interactive use with slow resize operations, you can install a progress-report callback: - #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) + #define STBIR_PROGRESS_REPORT(val) some_func(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) + static void my_progress_report(float progress); + #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) + + #define STB_IMAGE_RESIZE_IMPLEMENTATION + #include "stb_image_resize.h" + + static void my_progress_report(float progress) { printf("Progress: %f%%\n", progress*100); } @@ -87,7 +93,9 @@ 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. + it is resampled identically to other channels. This produces + the correct results for premultiplied-alpha images, but produces + less-than-ideal results for non-premultiplied-alpha images. 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, then the resizer weights the contribution of input pixels @@ -105,7 +113,11 @@ 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 + 6. You can suppress the behavior described in #5 and make + all-0-alpha pixels have 0 in all channels by #defining + STBIR_NO_ALPHA_EPSILON. + + 7. 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 @@ -123,7 +135,8 @@ and modify this file as you see fit. TODO - Installable filters + Don't decode all of the image data when only processing a subtile + 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 ) @@ -640,7 +653,7 @@ static float stbir__support_trapezoid(float scale) static float stbir__filter_bilinear(float x, float s) { - STBIR__UNUSED_PARAM(s) + STBIR__UNUSED_PARAM(s); x = (float)fabs(x); @@ -652,7 +665,7 @@ static float stbir__filter_bilinear(float x, float s) static float stbir__filter_bicubic(float x, float s) { - STBIR__UNUSED_PARAM(s) + STBIR__UNUSED_PARAM(s); x = (float)fabs(x); @@ -666,7 +679,7 @@ static float stbir__filter_bicubic(float x, float s) static float stbir__filter_catmullrom(float x, float s) { - STBIR__UNUSED_PARAM(s) + STBIR__UNUSED_PARAM(s); x = (float)fabs(x); @@ -680,7 +693,7 @@ static float stbir__filter_catmullrom(float x, float s) static float stbir__filter_mitchell(float x, float s) { - STBIR__UNUSED_PARAM(s) + STBIR__UNUSED_PARAM(s); x = (float)fabs(x); @@ -694,19 +707,19 @@ static float stbir__filter_mitchell(float x, float s) static float stbir__support_zero(float s) { - STBIR__UNUSED_PARAM(s) + STBIR__UNUSED_PARAM(s); return 0; } static float stbir__support_one(float s) { - STBIR__UNUSED_PARAM(s) + STBIR__UNUSED_PARAM(s); return 1; } static float stbir__support_two(float s) { - STBIR__UNUSED_PARAM(s) + STBIR__UNUSED_PARAM(s); return 2; } @@ -1281,7 +1294,6 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) break; } -#ifndef STBIR_NO_ALPHA_EPSILON if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) { for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++) @@ -1290,11 +1302,12 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) // 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]; +#ifndef STBIR_NO_ALPHA_EPSILON if (stbir_info->type != STBIR_TYPE_FLOAT) { alpha += STBIR_ALPHA_EPSILON; decode_buffer[decode_pixel_index + alpha_channel] = alpha; } - +#endif for (c = 0; c < channels; c++) { if (c == alpha_channel) @@ -1304,7 +1317,6 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) } } } -#endif if (edge_horizontal == STBIR_EDGE_ZERO) { @@ -1474,7 +1486,6 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void int x; int n; -#ifndef STBIR_NO_ALPHA_EPSILON if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) { for (x=0; x < num_pixels; ++x) @@ -1492,7 +1503,6 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void // conversion. } } -#endif switch (decode) { diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 6028cd5..07b8837 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -124,6 +124,23 @@ inline float mtfrand() return (float)(mtrand() % ninenine)/ninenine; } +static void resizer(int argc, char **argv) +{ + unsigned char* input_pixels; + unsigned char* output_pixels; + int w, h; + int n; + int out_w, out_h; + input_pixels = stbi_load(argv[1], &w, &h, &n, 0); + out_w = w/4; + out_h = h/4; + output_pixels = (unsigned char*) malloc(out_w*out_h*n); + //stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, -1,0); + stbir_resize_uint8(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n); + stbi_write_png("output.png", out_w, out_h, n, output_pixels, 0); + exit(0); +} + void test_suite(int argc, char **argv); @@ -135,6 +152,8 @@ int main(int argc, char** argv) int n; int out_w, out_h, out_stride; + //resizer(argc, argv); + #if 1 test_suite(argc, argv); return 0; @@ -733,6 +752,12 @@ void test_filters(void) STBIR_ASSERT(output[2] == input[1]); } + // checkerboard + { + + + } + { // Now for some fun. unsigned char input[2 * 1]; From da2aa8f6b77006105af174b9911161ac059d5501 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Fri, 12 Sep 2014 05:04:38 -0700 Subject: [PATCH 140/168] checkboard test to verify srgb --- tests/resample_test.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 07b8837..59a500f 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -754,12 +754,26 @@ void test_filters(void) // checkerboard { + unsigned char input[64][64]; + unsigned char output[16][16]; + int i,j; + for (j=0; j < 64; ++j) + for (i=0; i < 64; ++i) + input[j][i] = (i^j)&1 ? 255 : 0; + stbir_resize_uint8_generic(input[0], 64, 64, 0, output[0],16,16,0, 1,-1,0,STBIR_EDGE_WRAP,STBIR_FILTER_DEFAULT,STBIR_COLORSPACE_LINEAR,0); + for (j=0; j < 16; ++j) + for (i=0; i < 16; ++i) + STBIR_ASSERT(output[j][i] == 128); + stbir_resize_uint8_srgb_edgemode(input[0], 64, 64, 0, output[0],16,16,0, 1,-1,0,STBIR_EDGE_WRAP); + for (j=0; j < 16; ++j) + for (i=0; i < 16; ++i) + STBIR_ASSERT(output[j][i] == 188); } { - // Now for some fun. + // Test trapezoid box filter unsigned char input[2 * 1]; unsigned char output[127 * 1]; From 29b36b3dea91a3df743b54bbe196f98eeb4ec071 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Fri, 12 Sep 2014 05:28:24 -0700 Subject: [PATCH 141/168] rename filters, document them --- stb_image_resize.h | 20 ++++++++++---------- tests/resample_test.cpp | 36 ++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 0fdf47d..c9247d0 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -135,7 +135,7 @@ and modify this file as you see fit. TODO - Don't decode all of the image data when only processing a subtile + Don't decode all of the image data when only processing a partial tile Installable filters? Resize that respects alpha test coverage (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: @@ -247,11 +247,11 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels typedef enum { STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses - STBIR_FILTER_BOX = 1, // Is actually a trapezoid. See https://developer.nvidia.com/content/non-power-two-mipmapping - STBIR_FILTER_BILINEAR = 2, - STBIR_FILTER_BICUBIC = 3, // A cubic b spline - STBIR_FILTER_CATMULLROM = 4, - STBIR_FILTER_MITCHELL = 5, + STBIR_FILTER_BOX = 1, // Actually a trapezoid. See https://developer.nvidia.com/content/non-power-two-mipmapping + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBIC = 3, // A cubic b-spline: why this one ???????????? + STBIR_FILTER_CATMULLROM = 4, // interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 } stbir_filter; typedef enum @@ -651,7 +651,7 @@ static float stbir__support_trapezoid(float scale) return 0.5f + scale / 2; } -static float stbir__filter_bilinear(float x, float s) +static float stbir__filter_triangle(float x, float s) { STBIR__UNUSED_PARAM(s); @@ -663,7 +663,7 @@ static float stbir__filter_bilinear(float x, float s) return 0; } -static float stbir__filter_bicubic(float x, float s) +static float stbir__filter_cubic(float x, float s) { STBIR__UNUSED_PARAM(s); @@ -726,8 +726,8 @@ static float stbir__support_two(float s) static stbir__filter_info stbir__filter_info_table[] = { { NULL, stbir__support_zero }, { stbir__filter_trapezoid, stbir__support_trapezoid }, - { stbir__filter_bilinear, stbir__support_one }, - { stbir__filter_bicubic, stbir__support_two }, + { stbir__filter_triangle, stbir__support_one }, + { stbir__filter_cubic, stbir__support_two }, { stbir__filter_catmullrom, stbir__support_two }, { stbir__filter_mitchell, stbir__support_two }, }; diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 59a500f..fb66c6e 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -584,7 +584,7 @@ void test_subpixel_4() unsigned char output[8 * 8]; - stbir_resize_region(image, 8, 8, 0, output, 8, 8, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BILINEAR, STBIR_FILTER_BILINEAR, STBIR_COLORSPACE_LINEAR, &g_context, 0, 0, 1, 1); + stbir_resize_region(image, 8, 8, 0, output, 8, 8, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_TRIANGLE, STBIR_FILTER_TRIANGLE, STBIR_COLORSPACE_LINEAR, &g_context, 0, 0, 1, 1); STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); } @@ -672,26 +672,26 @@ void test_filters(void) image88_int[j][i] = value; verify_filter_normalized(STBIR_FILTER_BOX, 8, value); - verify_filter_normalized(STBIR_FILTER_BILINEAR, 8, value); - verify_filter_normalized(STBIR_FILTER_BICUBIC, 8, value); + verify_filter_normalized(STBIR_FILTER_TRIANGLE, 8, value); + verify_filter_normalized(STBIR_FILTER_CUBIC, 8, value); verify_filter_normalized(STBIR_FILTER_CATMULLROM, 8, value); verify_filter_normalized(STBIR_FILTER_MITCHELL, 8, value); verify_filter_normalized(STBIR_FILTER_BOX, 4, value); - verify_filter_normalized(STBIR_FILTER_BILINEAR, 4, value); - verify_filter_normalized(STBIR_FILTER_BICUBIC, 4, value); + verify_filter_normalized(STBIR_FILTER_TRIANGLE, 4, value); + verify_filter_normalized(STBIR_FILTER_CUBIC, 4, value); verify_filter_normalized(STBIR_FILTER_CATMULLROM, 4, value); verify_filter_normalized(STBIR_FILTER_MITCHELL, 4, value); verify_filter_normalized(STBIR_FILTER_BOX, 2, value); - verify_filter_normalized(STBIR_FILTER_BILINEAR, 2, value); - verify_filter_normalized(STBIR_FILTER_BICUBIC, 2, value); + verify_filter_normalized(STBIR_FILTER_TRIANGLE, 2, value); + verify_filter_normalized(STBIR_FILTER_CUBIC, 2, value); verify_filter_normalized(STBIR_FILTER_CATMULLROM, 2, value); verify_filter_normalized(STBIR_FILTER_MITCHELL, 2, value); verify_filter_normalized(STBIR_FILTER_BOX, 1, value); - verify_filter_normalized(STBIR_FILTER_BILINEAR, 1, value); - verify_filter_normalized(STBIR_FILTER_BICUBIC, 1, value); + verify_filter_normalized(STBIR_FILTER_TRIANGLE, 1, value); + verify_filter_normalized(STBIR_FILTER_CUBIC, 1, value); verify_filter_normalized(STBIR_FILTER_CATMULLROM, 1, value); verify_filter_normalized(STBIR_FILTER_MITCHELL, 1, value); @@ -705,7 +705,7 @@ void test_filters(void) for (j = 0; j < 100 * 100; ++j) input[j] = v; - stbir_resize(input, 100, 100, 0, output, 11, 11, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BILINEAR, STBIR_FILTER_BILINEAR, STBIR_COLORSPACE_LINEAR, NULL); + stbir_resize(input, 100, 100, 0, output, 11, 11, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_TRIANGLE, STBIR_FILTER_TRIANGLE, STBIR_COLORSPACE_LINEAR, NULL); for (j = 0; j < 11 * 11; ++j) STBIR_ASSERT(v == output[j]); @@ -821,8 +821,8 @@ void test_suite(int argc, char **argv) for (o = -5; o <= 5; ++o) { sums[0] += stbir__filter_mitchell(x + o, 1); sums[1] += stbir__filter_catmullrom(x + o, 1); - sums[2] += stbir__filter_bicubic(x + o, 1); - sums[3] += stbir__filter_bilinear(x + o, 1); + sums[2] += stbir__filter_cubic(x + o, 1); + sums[3] += stbir__filter_triangle(x + o, 1); sums[4] += stbir__filter_trapezoid(x + o, 0.5f); } for (i = 0; i < 5; ++i) @@ -837,9 +837,9 @@ void test_suite(int argc, char **argv) for (o = -5; o <= 5; o += y) { sums[0] += y * stbir__filter_mitchell(x + o, 1); sums[1] += y * stbir__filter_catmullrom(x + o, 1); - sums[2] += y * stbir__filter_bicubic(x + o, 1); + sums[2] += y * stbir__filter_cubic(x + o, 1); sums[4] += y * stbir__filter_trapezoid(x + o, 0.5f); - sums[3] += y * stbir__filter_bilinear(x + o, 1); + sums[3] += y * stbir__filter_triangle(x + o, 1); } for (i = 0; i < 3; ++i) STBIR_ASSERT(sums[i] >= 1.0 - 0.0170 && sums[i] <= 1.0 + 0.0170); @@ -916,14 +916,14 @@ void test_suite(int argc, char **argv) // filter tests resize_image(barbara, 2, 2, STBIR_FILTER_BOX , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-nearest.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_BILINEAR , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_BICUBIC , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_TRIANGLE , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_CUBIC , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); resize_image(barbara, 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-catmullrom.png"); resize_image(barbara, 2, 2, STBIR_FILTER_MITCHELL , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-mitchell.png"); resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BOX , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-nearest.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BILINEAR , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BICUBIC , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_TRIANGLE , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CUBIC , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png"); resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_MITCHELL , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); From 0fa5b5c66f0d38956a0726c92b4efca23cf5c6fd Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 05:52:19 -0700 Subject: [PATCH 142/168] rename cubic to cubicbspline; tweak filter type documentation --- stb_image_resize.h | 12 ++++++------ tests/resample_test.cpp | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index c9247d0..7a5549e 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -246,12 +246,12 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels typedef enum { - STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses - STBIR_FILTER_BOX = 1, // Actually a trapezoid. See https://developer.nvidia.com/content/non-power-two-mipmapping - STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering - STBIR_FILTER_CUBIC = 3, // A cubic b-spline: why this one ???????????? - STBIR_FILTER_CATMULLROM = 4, // interpolating cubic spline - STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 } stbir_filter; typedef enum diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index fb66c6e..d746f6c 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -673,25 +673,25 @@ void test_filters(void) verify_filter_normalized(STBIR_FILTER_BOX, 8, value); verify_filter_normalized(STBIR_FILTER_TRIANGLE, 8, value); - verify_filter_normalized(STBIR_FILTER_CUBIC, 8, value); + verify_filter_normalized(STBIR_FILTER_CUBICBSPLINE, 8, value); verify_filter_normalized(STBIR_FILTER_CATMULLROM, 8, value); verify_filter_normalized(STBIR_FILTER_MITCHELL, 8, value); verify_filter_normalized(STBIR_FILTER_BOX, 4, value); verify_filter_normalized(STBIR_FILTER_TRIANGLE, 4, value); - verify_filter_normalized(STBIR_FILTER_CUBIC, 4, value); + verify_filter_normalized(STBIR_FILTER_CUBICBSPLINE, 4, value); verify_filter_normalized(STBIR_FILTER_CATMULLROM, 4, value); verify_filter_normalized(STBIR_FILTER_MITCHELL, 4, value); verify_filter_normalized(STBIR_FILTER_BOX, 2, value); verify_filter_normalized(STBIR_FILTER_TRIANGLE, 2, value); - verify_filter_normalized(STBIR_FILTER_CUBIC, 2, value); + verify_filter_normalized(STBIR_FILTER_CUBICBSPLINE, 2, value); verify_filter_normalized(STBIR_FILTER_CATMULLROM, 2, value); verify_filter_normalized(STBIR_FILTER_MITCHELL, 2, value); verify_filter_normalized(STBIR_FILTER_BOX, 1, value); verify_filter_normalized(STBIR_FILTER_TRIANGLE, 1, value); - verify_filter_normalized(STBIR_FILTER_CUBIC, 1, value); + verify_filter_normalized(STBIR_FILTER_CUBICBSPLINE, 1, value); verify_filter_normalized(STBIR_FILTER_CATMULLROM, 1, value); verify_filter_normalized(STBIR_FILTER_MITCHELL, 1, value); @@ -915,17 +915,17 @@ void test_suite(int argc, char **argv) test_channels(barbara, 2, 2, 4); // filter tests - resize_image(barbara, 2, 2, STBIR_FILTER_BOX , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-nearest.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_TRIANGLE , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_CUBIC , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-catmullrom.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_MITCHELL , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-mitchell.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_BOX , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-nearest.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_TRIANGLE , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_CUBICBSPLINE, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_CATMULLROM , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-catmullrom.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_MITCHELL , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-mitchell.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BOX , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-nearest.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_TRIANGLE , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CUBIC , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_MITCHELL , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BOX , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-nearest.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_TRIANGLE , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CUBICBSPLINE, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CATMULLROM , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_MITCHELL , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); for (i = 10; i < 100; i++) { From 60064f980357b621e1f4988039ca066735eddd43 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 05:56:06 -0700 Subject: [PATCH 143/168] add stb_image_resize.h to readme --- tools/README.list | 25 +++++++++++++------------ tools/make_readme.dsp | 4 ++++ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/tools/README.list b/tools/README.list index ff51c96..d65a60a 100644 --- a/tools/README.list +++ b/tools/README.list @@ -1,12 +1,13 @@ -stb_vorbis.c | audio | decode ogg vorbis files from file/memory to float/16-bit signed output -stb_image.h | graphics | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC -stb_truetype.h | graphics | parse, decode, and rasterize characters from truetype fonts -stb_image_write.h | graphics | image writing to disk: PNG, TGA, BMP -stretchy_buffer.h | utility | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++ -stb_textedit.h | UI | guts of a text editor for games etc implementing them from scratch -stb_dxt.h | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor -stb_herringbone_wang_tile.h | games | herringbone Wang tile map generator -stb_perlin.h | 3D graphics | revised Perlin noise (3D input, 1D output) -stb_c_lexer.h | parsing | simplify writing parsers for C-like languages -stb_divide.h | math | more useful 32-bit modulus e.g. "euclidean divide" -stb.h | misc | helper functions for C, mostly redundant in C++; basically author's personal stuff +stb_vorbis.c | audio | decode ogg vorbis files from file/memory to float/16-bit signed output +stb_image.h | graphics | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC +stb_truetype.h | graphics | parse, decode, and rasterize characters from truetype fonts +stb_image_write.h | graphics | image writing to disk: PNG, TGA, BMP +stb_image_resize.h | graphics | resize images larger/smaller with good quality +stretchy_buffer.h | utility | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++ +stb_textedit.h | UI | guts of a text editor for games etc implementing them from scratch +stb_dxt.h | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor +stb_perlin.h | 3D graphics | revised Perlin noise (3D input, 1D output) +stb_herringbone_wang_tile.h | games | herringbone Wang tile map generator +stb_c_lexer.h | parsing | simplify writing parsers for C-like languages +stb_divide.h | math | more useful 32-bit modulus e.g. "euclidean divide" +stb.h | misc | helper functions for C, mostly redundant in C++; basically author's personal stuff diff --git a/tools/make_readme.dsp b/tools/make_readme.dsp index 4dbd54f..1cc079a 100644 --- a/tools/make_readme.dsp +++ b/tools/make_readme.dsp @@ -84,5 +84,9 @@ LINK32=link.exe SOURCE=.\make_readme.c # End Source File +# Begin Source File + +SOURCE=.\README.list +# End Source File # End Target # End Project From a2f1cadde87b58d348aca1caceae7dfe6e0e90e3 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 06:03:48 -0700 Subject: [PATCH 144/168] convert tabs to spaces so it's consistent with other stb libs --- stb_image_resize.h | 2610 ++++++++++++++++++++++++++-------------------------- 1 file changed, 1305 insertions(+), 1305 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 7a5549e..32e4480 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1,145 +1,145 @@ /* stb_image_resize - v0.90 - public domain image resizing by Jorge L Rodriguez (@VinoBS) - 2014 - http://github.com/nothings/stb + http://github.com/nothings/stb Written with emphasis on usage and speed. Only scaling is currently supported, no rotations or translations. - DOCUMENTATION + DOCUMENTATION - 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. + 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. - API - See the "header file" section of the source for API documentation. + API + See the "header file" section of the source for API documentation. - 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: + 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: - #define STBIR_MALLOC(size,context) ... - #define STBIR_FREE(ptr,context) ... + #define STBIR_MALLOC(size,context) ... + #define STBIR_FREE(ptr,context) ... - 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. + 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. - ASSERT - Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + 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 + 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 + #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. + 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. + 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: + PROGRESS + For interactive use with slow resize operations, you can install + a progress-report callback: - #define STBIR_PROGRESS_REPORT(val) some_func(val) + #define STBIR_PROGRESS_REPORT(val) some_func(val) - The parameter val is a float which goes from 0 to 1 as progress is made. + The parameter val is a float which goes from 0 to 1 as progress is made. - For example: + For example: - static void my_progress_report(float progress); - #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) + static void my_progress_report(float progress); + #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) - #define STB_IMAGE_RESIZE_IMPLEMENTATION - #include "stb_image_resize.h" + #define STB_IMAGE_RESIZE_IMPLEMENTATION + #include "stb_image_resize.h" - static void my_progress_report(float progress) - { - printf("Progress: %f%%\n", progress*100); - } + static 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: + 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. + 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.) + 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. + 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. + 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. This produces - the correct results for premultiplied-alpha images, but produces - less-than-ideal results for non-premultiplied-alpha images. + 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. This produces + the correct results for premultiplied-alpha images, but produces + less-than-ideal results for non-premultiplied-alpha images. - 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.) + 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 suppress the behavior described in #5 and make - all-0-alpha pixels have 0 in all channels by #defining - STBIR_NO_ALPHA_EPSILON. + 6. You can suppress the behavior described in #5 and make + all-0-alpha pixels have 0 in all channels by #defining + STBIR_NO_ALPHA_EPSILON. - 7. 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.) + 7. 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 + 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. + 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 - Don't decode all of the image data when only processing a partial tile - 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 ) + TODO + Don't decode all of the image data when only processing a partial tile + 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 @@ -217,10 +217,10 @@ STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int inp typedef enum { - STBIR_EDGE_CLAMP = 1, - STBIR_EDGE_REFLECT = 2, - STBIR_EDGE_WRAP = 3, - STBIR_EDGE_ZERO = 4, + STBIR_EDGE_CLAMP = 1, + STBIR_EDGE_REFLECT = 2, + STBIR_EDGE_WRAP = 3, + STBIR_EDGE_ZERO = 4, } stbir_edge; // This function adds the ability to specify how requests to sample off the edge of the image are handled. @@ -246,20 +246,20 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels typedef enum { - STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses - STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios - STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering - STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque - STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline - STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 } stbir_filter; typedef enum { - STBIR_COLORSPACE_LINEAR, - STBIR_COLORSPACE_SRGB, + STBIR_COLORSPACE_LINEAR, + STBIR_COLORSPACE_SRGB, - STBIR_MAX_COLORSPACES, + STBIR_MAX_COLORSPACES, } stbir_colorspace; // The following functions are all identical except for the type of the image data @@ -290,7 +290,7 @@ STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int // // This extends the medium API as follows: // -// * uint32 image type +// * uint32 image type // * not typesafe // * separate filter types for each axis // * separate edge modes for each axis @@ -299,12 +299,12 @@ STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int typedef enum { - STBIR_TYPE_UINT8 , - STBIR_TYPE_UINT16, - STBIR_TYPE_UINT32, - STBIR_TYPE_FLOAT , + STBIR_TYPE_UINT8 , + STBIR_TYPE_UINT16, + STBIR_TYPE_UINT32, + STBIR_TYPE_FLOAT , - STBIR_MAX_TYPES + STBIR_MAX_TYPES } stbir_datatype; STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, @@ -424,10 +424,10 @@ typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1] // must match stbir_datatype static unsigned char stbir__type_size[] = { - 1, // STBIR_TYPE_UINT8 - 2, // STBIR_TYPE_UINT16 - 4, // STBIR_TYPE_UINT32 - 4, // STBIR_TYPE_FLOAT + 1, // STBIR_TYPE_UINT8 + 2, // STBIR_TYPE_UINT16 + 4, // STBIR_TYPE_UINT32 + 4, // STBIR_TYPE_FLOAT }; // Kernel function centered at 0 @@ -436,1706 +436,1706 @@ typedef float (stbir__support_fn)(float scale); typedef struct { - stbir__kernel_fn* kernel; - stbir__support_fn* support; + stbir__kernel_fn* kernel; + stbir__support_fn* support; } stbir__filter_info; // When upsampling, the contributors are which source pixels contribute. // When downsampling, the contributors are which destination pixels are contributed to. typedef struct { - int n0; // First contributing pixel - int n1; // Last contributing pixel + int n0; // First contributing pixel + int n1; // Last contributing pixel } stbir__contributors; typedef struct { - const void* input_data; - int input_w; - int input_h; - int input_stride_bytes; + const void* input_data; + int input_w; + int input_h; + int input_stride_bytes; - void* output_data; - int output_w; - int output_h; - int output_stride_bytes; + void* output_data; + int output_w; + int output_h; + int output_stride_bytes; - float s0, t0, s1, t1; + float s0, t0, s1, t1; - float horizontal_shift; // Units: output pixels - float vertical_shift; // Units: output pixels - float horizontal_scale; - float vertical_scale; + float horizontal_shift; // Units: output pixels + float vertical_shift; // Units: output pixels + float horizontal_scale; + float vertical_scale; - int channels; - int alpha_channel; - stbir_uint32 flags; - stbir_datatype type; - stbir_filter horizontal_filter; - stbir_filter vertical_filter; - stbir_edge edge_horizontal; - stbir_edge edge_vertical; - stbir_colorspace colorspace; + int channels; + int alpha_channel; + stbir_uint32 flags; + stbir_datatype type; + stbir_filter horizontal_filter; + stbir_filter vertical_filter; + stbir_edge edge_horizontal; + stbir_edge edge_vertical; + stbir_colorspace colorspace; - stbir__contributors* horizontal_contributors; - float* horizontal_coefficients; + stbir__contributors* horizontal_contributors; + float* horizontal_coefficients; - stbir__contributors* vertical_contributors; - float* vertical_coefficients; + stbir__contributors* vertical_contributors; + float* vertical_coefficients; - int decode_buffer_pixels; - float* decode_buffer; + int decode_buffer_pixels; + float* decode_buffer; - float* horizontal_buffer; + float* horizontal_buffer; - int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) - int ring_buffer_first_scanline; - int ring_buffer_last_scanline; - int ring_buffer_begin_index; - float* ring_buffer; + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; + float* ring_buffer; - float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. + float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. - int horizontal_contributors_size; - int horizontal_coefficients_size; - int vertical_contributors_size; - int vertical_coefficients_size; - int decode_buffer_size; - int horizontal_buffer_size; - int ring_buffer_size; - int encode_buffer_size; + int horizontal_contributors_size; + int horizontal_coefficients_size; + int vertical_contributors_size; + int vertical_coefficients_size; + int decode_buffer_size; + int horizontal_buffer_size; + int ring_buffer_size; + int encode_buffer_size; } stbir__info; static stbir__inline int stbir__min(int a, int b) { - return a < b ? a : b; + return a < b ? a : b; } static stbir__inline int stbir__max(int a, int b) { - return a > b ? a : b; + return a > b ? a : b; } static stbir__inline float stbir__saturate(float x) { - if (x < 0) - return 0; + if (x < 0) + return 0; - if (x > 1) - return 1; + if (x > 1) + return 1; - return x; + return x; } static float stbir__srgb_uchar_to_linear_float[256] = { - 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, - 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, - 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, - 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, - 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, - 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, - 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, - 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, - 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, - 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, - 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, - 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, - 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, - 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, - 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, - 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, - 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, - 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, - 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, - 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, - 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, - 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, - 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, - 0.982251f, 0.991102f, 1.0f + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f }; // sRGB transition values, scaled by 1<<28 static int stbir__srgb_offset_to_linear_scaled[256] = { - 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, + 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) { - if (f <= 0.04045f) - return f / 12.92f; - else - return (float)pow((f + 0.055f) / 1.055f, 2.4f); + if (f <= 0.04045f) + return f / 12.92f; + else + return (float)pow((f + 0.055f) / 1.055f, 2.4f); } static float stbir__linear_to_srgb(float f) { - if (f <= 0.0031308f) - return f * 12.92f; - else - return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; + if (f <= 0.0031308f) + return f * 12.92f; + else + return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; } static unsigned char stbir__linear_to_srgb_uchar(float f) { - int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp - int v = 0; - int i; + int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp + int v = 0; + int i; - 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; + 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; - return (unsigned char) v; + return (unsigned char) v; } static float stbir__filter_trapezoid(float x, float scale) { - float halfscale = scale / 2; - float t = 0.5f + halfscale; - STBIR__DEBUG_ASSERT(scale <= 1); + float halfscale = scale / 2; + float t = 0.5f + halfscale; + STBIR__DEBUG_ASSERT(scale <= 1); - x = (float)fabs(x); + x = (float)fabs(x); - if (x >= t) - return 0; - else - { - float r = 0.5f - halfscale; - if (x <= r) - return 1; - else - return (t - x) / scale; - } + if (x >= t) + return 0; + else + { + float r = 0.5f - halfscale; + if (x <= r) + return 1; + else + return (t - x) / scale; + } } static float stbir__support_trapezoid(float scale) { - STBIR__DEBUG_ASSERT(scale <= 1); - return 0.5f + scale / 2; + STBIR__DEBUG_ASSERT(scale <= 1); + return 0.5f + scale / 2; } static float stbir__filter_triangle(float x, float s) { - STBIR__UNUSED_PARAM(s); + STBIR__UNUSED_PARAM(s); - x = (float)fabs(x); + x = (float)fabs(x); - if (x <= 1.0f) - return 1 - x; - else - return 0; + if (x <= 1.0f) + return 1 - x; + else + return 0; } static float stbir__filter_cubic(float x, float s) { - STBIR__UNUSED_PARAM(s); + STBIR__UNUSED_PARAM(s); - x = (float)fabs(x); + x = (float)fabs(x); - if (x < 1.0f) - return (4 + x*x*(3*x - 6))/6; - else if (x < 2.0f) - return (8 + x*(-12 + x*(6 - x)))/6; + if (x < 1.0f) + return (4 + x*x*(3*x - 6))/6; + else if (x < 2.0f) + return (8 + x*(-12 + x*(6 - x)))/6; - return (0.0f); + return (0.0f); } static float stbir__filter_catmullrom(float x, float s) { - STBIR__UNUSED_PARAM(s); + STBIR__UNUSED_PARAM(s); - x = (float)fabs(x); + x = (float)fabs(x); - if (x < 1.0f) - return 1 - x*x*(2.5f - 1.5f*x); - else if (x < 2.0f) - return 2 - x*(4 + x*(0.5f*x - 2.5f)); + if (x < 1.0f) + return 1 - x*x*(2.5f - 1.5f*x); + else if (x < 2.0f) + return 2 - x*(4 + x*(0.5f*x - 2.5f)); - return (0.0f); + return (0.0f); } static float stbir__filter_mitchell(float x, float s) { - STBIR__UNUSED_PARAM(s); + STBIR__UNUSED_PARAM(s); - x = (float)fabs(x); + x = (float)fabs(x); - if (x < 1.0f) - return (16 + x*x*(21 * x - 36))/18; - else if (x < 2.0f) - return (32 + x*(-60 + x*(36 - 7*x)))/18; + if (x < 1.0f) + return (16 + x*x*(21 * x - 36))/18; + else if (x < 2.0f) + return (32 + x*(-60 + x*(36 - 7*x)))/18; - return (0.0f); + return (0.0f); } static float stbir__support_zero(float s) { - STBIR__UNUSED_PARAM(s); - return 0; + STBIR__UNUSED_PARAM(s); + return 0; } static float stbir__support_one(float s) { - STBIR__UNUSED_PARAM(s); - return 1; + STBIR__UNUSED_PARAM(s); + return 1; } static float stbir__support_two(float s) { - STBIR__UNUSED_PARAM(s); - return 2; + STBIR__UNUSED_PARAM(s); + return 2; } static stbir__filter_info stbir__filter_info_table[] = { - { NULL, stbir__support_zero }, - { stbir__filter_trapezoid, stbir__support_trapezoid }, - { stbir__filter_triangle, stbir__support_one }, - { stbir__filter_cubic, stbir__support_two }, - { stbir__filter_catmullrom, stbir__support_two }, - { stbir__filter_mitchell, stbir__support_two }, + { NULL, stbir__support_zero }, + { stbir__filter_trapezoid, stbir__support_trapezoid }, + { stbir__filter_triangle, stbir__support_one }, + { stbir__filter_cubic, stbir__support_two }, + { stbir__filter_catmullrom, stbir__support_two }, + { stbir__filter_mitchell, stbir__support_two }, }; stbir__inline static int stbir__use_upsampling(float ratio) { - return ratio > 1; + return ratio > 1; } stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) { - return stbir__use_upsampling(stbir_info->horizontal_scale); + return stbir__use_upsampling(stbir_info->horizontal_scale); } stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) { - return stbir__use_upsampling(stbir_info->vertical_scale); + return stbir__use_upsampling(stbir_info->vertical_scale); } // This is the maximum number of input samples that can affect an output sample // with the given filter stbir__inline static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) { - STBIR_ASSERT(filter != 0); - STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(filter != 0); + STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); - else - return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); } stbir__inline static int stbir__get_filter_pixel_width_horizontal(stbir__info* stbir_info) { - return stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); + return stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); } stbir__inline static int stbir__get_filter_pixel_width_vertical(stbir__info* stbir_info) { - return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->vertical_scale); + return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->vertical_scale); } // This is how much to expand buffers to account for filters seeking outside // the image boundaries. stbir__inline static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) { - return stbir__get_filter_pixel_width(filter, scale) / 2; + return stbir__get_filter_pixel_width(filter, scale) / 2; } stbir__inline static int stbir__get_filter_pixel_margin_horizontal(stbir__info* stbir_info) { - return stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale) / 2; + return stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale) / 2; } stbir__inline static int stbir__get_filter_pixel_margin_vertical(stbir__info* stbir_info) { - return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->vertical_scale) / 2; + return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->vertical_scale) / 2; } stbir__inline static int stbir__get_coefficient_width(stbir_filter filter, float scale) { - if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); - else - return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); } stbir__inline static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) { - if (stbir__use_upsampling(scale)) - return output_size; - else - return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); + if (stbir__use_upsampling(scale)) + return output_size; + else + return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); } stbir__inline static int stbir__get_horizontal_contributors(stbir__info* info) { - return stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); + return stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); } stbir__inline static int stbir__get_vertical_contributors(stbir__info* info) { - return stbir__get_contributors(info->vertical_scale, info->vertical_filter, info->input_h, info->output_h); + return stbir__get_contributors(info->vertical_scale, info->vertical_filter, info->input_h, info->output_h); } stbir__inline static int stbir__get_total_horizontal_coefficients(stbir__info* info) { - return stbir__get_horizontal_contributors(info) - * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); + return stbir__get_horizontal_contributors(info) + * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); } stbir__inline static int stbir__get_total_vertical_coefficients(stbir__info* info) { - return stbir__get_vertical_contributors(info) - * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); + return stbir__get_vertical_contributors(info) + * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); } stbir__inline static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) { - return &contributors[n]; + return &contributors[n]; } stbir__inline static stbir__contributors* stbir__get_horizontal_contributor(stbir__info* stbir_info, int n) { - STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_horizontal_contributors(stbir_info)); - return stbir__get_contributor(stbir_info->horizontal_contributors, n); + STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_horizontal_contributors(stbir_info)); + return stbir__get_contributor(stbir_info->horizontal_contributors, n); } stbir__inline static stbir__contributors* stbir__get_vertical_contributor(stbir__info* stbir_info, int n) { - STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_vertical_contributors(stbir_info)); - return stbir__get_contributor(stbir_info->vertical_contributors, n); + STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_vertical_contributors(stbir_info)); + return stbir__get_contributor(stbir_info->vertical_contributors, n); } // For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, // if you change it here change it there too. stbir__inline static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) { - int width = stbir__get_coefficient_width(filter, scale); - return &coefficients[width*n + c]; + int width = stbir__get_coefficient_width(filter, scale); + return &coefficients[width*n + c]; } stbir__inline static float* stbir__get_horizontal_coefficient(stbir__info* stbir_info, int n, int c) { - STBIR__DEBUG_ASSERT(c >= 0 && c < stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale)); - STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_total_horizontal_coefficients(stbir_info)); - return stbir__get_coefficient(stbir_info->horizontal_coefficients, stbir_info->horizontal_filter, stbir_info->horizontal_scale, n, c); + STBIR__DEBUG_ASSERT(c >= 0 && c < stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale)); + STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_total_horizontal_coefficients(stbir_info)); + return stbir__get_coefficient(stbir_info->horizontal_coefficients, stbir_info->horizontal_filter, stbir_info->horizontal_scale, n, c); } stbir__inline static float* stbir__get_vertical_coefficient(stbir__info* stbir_info, int n, int c) { - STBIR__DEBUG_ASSERT(c >= 0 && c < stbir__get_coefficient_width(stbir_info->vertical_filter, stbir_info->vertical_scale)); - STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_total_vertical_coefficients(stbir_info)); - return stbir__get_coefficient(stbir_info->vertical_coefficients, stbir_info->vertical_filter, stbir_info->vertical_scale, n, c); + STBIR__DEBUG_ASSERT(c >= 0 && c < stbir__get_coefficient_width(stbir_info->vertical_filter, stbir_info->vertical_scale)); + STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_total_vertical_coefficients(stbir_info)); + return stbir__get_coefficient(stbir_info->vertical_coefficients, stbir_info->vertical_filter, stbir_info->vertical_scale, n, c); } static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) { - switch (edge) - { - case STBIR_EDGE_ZERO: - return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later + switch (edge) + { + case STBIR_EDGE_ZERO: + return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later - case STBIR_EDGE_CLAMP: - if (n < 0) - return 0; + case STBIR_EDGE_CLAMP: + if (n < 0) + return 0; - if (n >= max) - return max - 1; + if (n >= max) + return max - 1; - return n; // NOTREACHED + return n; // NOTREACHED - case STBIR_EDGE_REFLECT: - { - if (n < 0) - { - if (n < max) - return -n; - else - return max - 1; - } + case STBIR_EDGE_REFLECT: + { + if (n < 0) + { + if (n < max) + return -n; + else + return max - 1; + } - if (n >= max) - { - int max2 = max * 2; - if (n >= max2) - return 0; - else - return max2 - n - 1; - } + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } - return n; // NOTREACHED - } + return n; // NOTREACHED + } - case STBIR_EDGE_WRAP: - if (n >= 0) - return (n % max); - else - { - int m = (-n) % max; + case STBIR_EDGE_WRAP: + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; - if (m != 0) - m = max - m; + if (m != 0) + m = max - m; - return (m); - } - return n; // NOTREACHED + return (m); + } + return n; // NOTREACHED - default: - STBIR__UNIMPLEMENTED("Unimplemented edge type"); - return 0; - } + default: + STBIR__UNIMPLEMENTED("Unimplemented edge type"); + return 0; + } } stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) { - // avoid per-pixel switch - if (n >= 0 && n < max) - return n; - return stbir__edge_wrap_slow(edge, n, max); + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow(edge, n, max); } // What input pixels contribute to this output pixel? static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) { - float out_pixel_center = (float)n + 0.5f; - float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; - float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + float out_pixel_center = (float)n + 0.5f; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; - float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; - float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; - *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; - *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); - *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); + *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; + *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); + *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); } // What output pixels does this input pixel contribute to? static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) { - float in_pixel_center = (float)n + 0.5f; - float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; - float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + float in_pixel_center = (float)n + 0.5f; + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; - float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; - float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; - *out_center_of_in = in_pixel_center * scale_ratio - out_shift; - *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); - *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); + *out_center_of_in = in_pixel_center * scale_ratio - out_shift; + *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); + *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); } static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) { - int i; - float total_filter = 0; - float filter_scale; + int i; + float total_filter = 0; + float filter_scale; - STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 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__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 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 = in_first_pixel; - contributor->n1 = in_last_pixel; + contributor->n0 = in_first_pixel; + contributor->n1 = in_last_pixel; - STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0); + STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0); - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - { - float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); - // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) - if (i == 0 && !coefficient_group[i]) - { - contributor->n0 = ++in_first_pixel; - i--; - continue; - } + // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) + if (i == 0 && !coefficient_group[i]) + { + contributor->n0 = ++in_first_pixel; + i--; + continue; + } - total_filter += coefficient_group[i]; - } + total_filter += coefficient_group[i]; + } - STBIR__DEBUG_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); + STBIR__DEBUG_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); - STBIR__DEBUG_ASSERT(total_filter > 0.9); - STBIR__DEBUG_ASSERT(total_filter < 1.1f); // Make sure it's not way off. + STBIR__DEBUG_ASSERT(total_filter > 0.9); + STBIR__DEBUG_ASSERT(total_filter < 1.1f); // Make sure it's not way off. - // Make sure the sum of all coefficients is 1. - filter_scale = 1 / total_filter; + // Make sure the sum of all coefficients is 1. + filter_scale = 1 / total_filter; - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - coefficient_group[i] *= filter_scale; + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + coefficient_group[i] *= filter_scale; - for (i = in_last_pixel - in_first_pixel; i >= 0; i--) - { - if (coefficient_group[i]) - break; + for (i = in_last_pixel - in_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; - // This line has no weight. We can skip it. - contributor->n1 = contributor->n0 + i - 1; - } + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } } static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) { - int i; + int i; - STBIR__DEBUG_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__DEBUG_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; + contributor->n0 = out_first_pixel; + contributor->n1 = out_last_pixel; - STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0); + STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0); - for (i = 0; i <= out_last_pixel - out_first_pixel; i++) - { - float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; - float x = out_pixel_center - out_center_of_in; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; - } + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; + } - STBIR__DEBUG_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); + STBIR__DEBUG_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--) - { - if (coefficient_group[i]) - break; + for (i = out_last_pixel - out_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; - // This line has no weight. We can skip it. - contributor->n1 = contributor->n0 + i - 1; - } + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } } static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) { - int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); - int i, j; - int skip; + int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); + int i, j; + int skip; - for (i = 0; i < output_size; i++) - { - float scale; - float total = 0; + for (i = 0; i < output_size; i++) + { + float scale; + float total = 0; - for (j = 0; j < num_contributors; j++) - { - if (i >= contributors[j].n0 && i <= contributors[j].n1) - { - float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); - total += coefficient; - } - else if (i < contributors[j].n0) - break; - } + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + { + float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); + total += coefficient; + } + else if (i < contributors[j].n0) + break; + } - STBIR__DEBUG_ASSERT(total > 0.9f); - STBIR__DEBUG_ASSERT(total < 1.1f); + STBIR__DEBUG_ASSERT(total > 0.9f); + STBIR__DEBUG_ASSERT(total < 1.1f); - scale = 1 / total; + scale = 1 / total; - for (j = 0; j < num_contributors; j++) - { - if (i >= contributors[j].n0 && i <= contributors[j].n1) - *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; - else if (i < contributors[j].n0) - break; - } - } + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; + else if (i < contributors[j].n0) + break; + } + } - // Optimize: Skip zero coefficients and contributions outside of image bounds. - // Do this after normalizing because normalization depends on the n0/n1 values. - for (j = 0; j < num_contributors; j++) - { - int range, max; + // Optimize: Skip zero coefficients and contributions outside of image bounds. + // Do this after normalizing because normalization depends on the n0/n1 values. + for (j = 0; j < num_contributors; j++) + { + int range, max; - skip = 0; - while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) - skip++; + skip = 0; + while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) + skip++; - contributors[j].n0 += skip; + contributors[j].n0 += skip; - while (contributors[j].n0 < 0) - { - contributors[j].n0++; - skip++; - } + while (contributors[j].n0 < 0) + { + contributors[j].n0++; + skip++; + } - range = contributors[j].n1 - contributors[j].n0 + 1; - max = stbir__min(num_coefficients, range); + range = contributors[j].n1 - contributors[j].n0 + 1; + max = stbir__min(num_coefficients, range); - for (i = 0; i < max; i++) - { - if (i + skip >= stbir__get_coefficient_width(filter, scale_ratio)) - break; + for (i = 0; i < max; i++) + { + if (i + skip >= stbir__get_coefficient_width(filter, scale_ratio)) + break; - *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); - } + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); + } - continue; - } + continue; + } - // Using min to avoid writing into invalid pixels. - for (i = 0; i < num_contributors; i++) - contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); + // Using min to avoid writing into invalid pixels. + for (i = 0; i < num_contributors; i++) + contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); } // Each scan line uses the same kernel values so we should calculate the kernel // values once and then we can use them for every scan line. static void stbir__calculate_filters(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) { - int n; - int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + int n; + int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - if (stbir__use_upsampling(scale_ratio)) - { - float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; + if (stbir__use_upsampling(scale_ratio)) + { + float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; - // Looping through out pixels - for (n = 0; n < total_contributors; n++) - { - float in_center_of_out; // Center of the current out pixel in the in pixel space - int in_first_pixel, in_last_pixel; + // Looping through out pixels + for (n = 0; n < total_contributors; n++) + { + float in_center_of_out; // Center of the current out pixel in the in pixel space + int in_first_pixel, in_last_pixel; - stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); + stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); - stbir__calculate_coefficients_upsample(stbir_info, filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); - } - } - else - { - float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; + stbir__calculate_coefficients_upsample(stbir_info, filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + } + else + { + float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; - // Looping through in pixels - for (n = 0; n < total_contributors; n++) - { - float out_center_of_in; // Center of the current out pixel in the in pixel space - int out_first_pixel, out_last_pixel; - int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); + // Looping through in pixels + for (n = 0; n < total_contributors; n++) + { + float out_center_of_in; // Center of the current out pixel in the in pixel space + int out_first_pixel, out_last_pixel; + int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); - stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); + stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); - stbir__calculate_coefficients_downsample(stbir_info, filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); - } + stbir__calculate_coefficients_downsample(stbir_info, filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } - stbir__normalize_downsample_coefficients(stbir_info, contributors, coefficients, filter, scale_ratio, shift, input_size, output_size); - } + stbir__normalize_downsample_coefficients(stbir_info, contributors, coefficients, filter, scale_ratio, shift, input_size, output_size); + } } static float* stbir__get_decode_buffer(stbir__info* stbir_info) { - // The 0 index of the decode buffer starts after the margin. This makes - // it okay to use negative indexes on the decode buffer. - return &stbir_info->decode_buffer[stbir__get_filter_pixel_margin_horizontal(stbir_info) * stbir_info->channels]; + // The 0 index of the decode buffer starts after the margin. This makes + // it okay to use negative indexes on the decode buffer. + return &stbir_info->decode_buffer[stbir__get_filter_pixel_margin_horizontal(stbir_info) * stbir_info->channels]; } #define STBIR__DECODE(type, colorspace) ((type) * (STBIR_MAX_COLORSPACES) + (colorspace)) static void stbir__decode_scanline(stbir__info* stbir_info, int n) { - int c; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int input_w = stbir_info->input_w; - int input_stride_bytes = stbir_info->input_stride_bytes; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir_edge edge_horizontal = stbir_info->edge_horizontal; - stbir_edge edge_vertical = stbir_info->edge_vertical; - int in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; - const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; - int max_x = input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info); - int decode = STBIR__DECODE(type, colorspace); + int c; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int input_w = stbir_info->input_w; + int input_stride_bytes = stbir_info->input_stride_bytes; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir_edge edge_horizontal = stbir_info->edge_horizontal; + stbir_edge edge_vertical = stbir_info->edge_vertical; + int in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; + const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; + int max_x = input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info); + int decode = STBIR__DECODE(type, colorspace); - int x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); + int x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); - // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, - // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO - if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) - { - for (; x < max_x; x++) - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - return; - } + // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, + // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO + if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) + { + for (; x < max_x; x++) + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + return; + } - switch (decode) - { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255; - } - break; + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255; + } + break; - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255; - } - break; + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255; + } + break; - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535; - } - break; + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535; + } + break; - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535; - } - break; + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535; + } + break; - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295); - } - break; + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295); + } + break; - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295); - } - break; + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295); + } + break; - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; - } - break; + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; + } + break; - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; - } + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; + } - break; + break; - default: - STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); - break; - } + default: + STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; + } - if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) - { - for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++) - { - int decode_pixel_index = x * channels; + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++) + { + 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]; + // 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]; #ifndef STBIR_NO_ALPHA_EPSILON - if (stbir_info->type != STBIR_TYPE_FLOAT) { - alpha += STBIR_ALPHA_EPSILON; - decode_buffer[decode_pixel_index + alpha_channel] = alpha; - } + if (stbir_info->type != STBIR_TYPE_FLOAT) { + alpha += STBIR_ALPHA_EPSILON; + decode_buffer[decode_pixel_index + alpha_channel] = alpha; + } #endif - for (c = 0; c < channels; c++) - { - if (c == alpha_channel) - continue; + for (c = 0; c < channels; c++) + { + if (c == alpha_channel) + continue; - decode_buffer[decode_pixel_index + c] *= alpha; - } - } - } + decode_buffer[decode_pixel_index + c] *= alpha; + } + } + } - if (edge_horizontal == STBIR_EDGE_ZERO) - { - for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < 0; x++) - { - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - } - for (x = input_w; x < max_x; x++) - { - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - } - } + if (edge_horizontal == STBIR_EDGE_ZERO) + { + for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < 0; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + for (x = input_w; x < max_x; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + } } static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) { - return &ring_buffer[index * ring_buffer_length]; + return &ring_buffer[index * ring_buffer_length]; } static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) { - int ring_buffer_index; - float* ring_buffer; + int ring_buffer_index; + float* ring_buffer; - if (stbir_info->ring_buffer_begin_index < 0) - { - ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; - stbir_info->ring_buffer_first_scanline = n; - } - else - { - ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline) + 1) % stbir__get_filter_pixel_width_vertical(stbir_info); - STBIR__DEBUG_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); - } + if (stbir_info->ring_buffer_begin_index < 0) + { + ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; + stbir_info->ring_buffer_first_scanline = n; + } + else + { + ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline) + 1) % stbir__get_filter_pixel_width_vertical(stbir_info); + STBIR__DEBUG_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); + } - ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); - memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); + ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); + memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); - stbir_info->ring_buffer_last_scanline = n; + stbir_info->ring_buffer_last_scanline = n; - return ring_buffer; + return ring_buffer; } static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, float* output_buffer) { - int x, k; - int output_w = stbir_info->output_w; - int kernel_pixel_width = stbir__get_filter_pixel_width_horizontal(stbir_info); - int channels = stbir_info->channels; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; - float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); + int x, k; + int output_w = stbir_info->output_w; + int kernel_pixel_width = stbir__get_filter_pixel_width_horizontal(stbir_info); + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); - for (x = 0; x < output_w; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; + for (x = 0; x < output_w; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; - int out_pixel_index = x * channels; - int coefficient_group = coefficient_width * x; - int coefficient_counter = 0; + int out_pixel_index = x * channels; + int coefficient_group = coefficient_width * x; + int coefficient_counter = 0; - STBIR__DEBUG_ASSERT(n1 >= n0); - STBIR__DEBUG_ASSERT(n0 >= -stbir__get_filter_pixel_margin_horizontal(stbir_info)); - STBIR__DEBUG_ASSERT(n1 >= -stbir__get_filter_pixel_margin_horizontal(stbir_info)); - STBIR__DEBUG_ASSERT(n0 < stbir_info->input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info)); - STBIR__DEBUG_ASSERT(n1 < stbir_info->input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info)); + STBIR__DEBUG_ASSERT(n1 >= n0); + STBIR__DEBUG_ASSERT(n0 >= -stbir__get_filter_pixel_margin_horizontal(stbir_info)); + STBIR__DEBUG_ASSERT(n1 >= -stbir__get_filter_pixel_margin_horizontal(stbir_info)); + STBIR__DEBUG_ASSERT(n0 < stbir_info->input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info)); + STBIR__DEBUG_ASSERT(n1 < stbir_info->input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info)); - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR__DEBUG_ASSERT(coefficient != 0); + STBIR__DEBUG_ASSERT(coefficient != 0); - int c; - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - } + int c; + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + } } static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n, float* output_buffer) { - int x, k; - int input_w = stbir_info->input_w; - int output_w = stbir_info->output_w; - int kernel_pixel_width = stbir__get_filter_pixel_width_horizontal(stbir_info); - int channels = stbir_info->channels; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; - float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); - int filter_pixel_margin = stbir__get_filter_pixel_margin_horizontal(stbir_info); - int max_x = input_w + filter_pixel_margin * 2; + int x, k; + int input_w = stbir_info->input_w; + int output_w = stbir_info->output_w; + int kernel_pixel_width = stbir__get_filter_pixel_width_horizontal(stbir_info); + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); + int filter_pixel_margin = stbir__get_filter_pixel_margin_horizontal(stbir_info); + int max_x = input_w + filter_pixel_margin * 2; - STBIR__DEBUG_ASSERT(!stbir__use_width_upsampling(stbir_info)); + STBIR__DEBUG_ASSERT(!stbir__use_width_upsampling(stbir_info)); - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * channels; - int max_n = n1; - int coefficient_group = coefficient_width * x; + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * channels; + int max_n = n1; + int coefficient_group = coefficient_width * x; - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR__DEBUG_ASSERT(coefficient != 0); + STBIR__DEBUG_ASSERT(coefficient != 0); - int c; - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - } + int c; + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + } } static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) { - // Decode the nth scanline from the source image into the decode buffer. - stbir__decode_scanline(stbir_info, n); + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); - // Now resample it into the ring buffer. - if (stbir__use_width_upsampling(stbir_info)) - stbir__resample_horizontal_upsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); - else - stbir__resample_horizontal_downsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + // Now resample it into the ring buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + else + stbir__resample_horizontal_downsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); - // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. } static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) { - // Decode the nth scanline from the source image into the decode buffer. - stbir__decode_scanline(stbir_info, n); + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); - memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); + memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); - // Now resample it into the horizontal buffer. - if (stbir__use_width_upsampling(stbir_info)) - stbir__resample_horizontal_upsample(stbir_info, n, stbir_info->horizontal_buffer); - else - stbir__resample_horizontal_downsample(stbir_info, n, stbir_info->horizontal_buffer); + // Now resample it into the horizontal buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, n, stbir_info->horizontal_buffer); + else + stbir__resample_horizontal_downsample(stbir_info, n, stbir_info->horizontal_buffer); - // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. + // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. } // Get the specified scan line from the ring buffer. static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_size, int ring_buffer_length) { - int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_size; - return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); + int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_size; + return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); } 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; - int n; + int x; + int n; - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) - { - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; - float alpha = encode_buffer[pixel_index + alpha_channel]; - 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; + float alpha = encode_buffer[pixel_index + alpha_channel]; + 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. - // Because we only add it for integer types, it will automatically be discarded on integer - // conversion. - } - } + // 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. + } + } - switch (decode) - { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned char*)output_buffer)[index] = (unsigned char)(floor(stbir__saturate(encode_buffer[index]) * 255 + 0.5f)); - } - } - break; + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned char*)output_buffer)[index] = (unsigned char)(floor(stbir__saturate(encode_buffer[index]) * 255 + 0.5f)); + } + } + break; - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); - } + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); + } - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned char*)output_buffer)[pixel_index + alpha_channel] = (unsigned char)(floor(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 255+0.5f)); - } - break; + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned char*)output_buffer)[pixel_index + alpha_channel] = (unsigned char)(floor(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 255+0.5f)); + } + break; - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned short*)output_buffer)[index] = (unsigned short)(floor(stbir__saturate(encode_buffer[index]) * 65535+0.5f)); - } - } - break; + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned short*)output_buffer)[index] = (unsigned short)(floor(stbir__saturate(encode_buffer[index]) * 65535+0.5f)); + } + } + break; - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned short*)output_buffer)[index] = (unsigned short)(floor(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535 + 0.5f)); - } + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((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)(floor(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 65535 + 0.5f)); - } + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = (unsigned short)(floor(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 65535 + 0.5f)); + } - break; + break; - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned int*)output_buffer)[index] = (unsigned int)(floor(((double)stbir__saturate(encode_buffer[index])) * 4294967295 + 0.5f)); - } - } - break; + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned int*)output_buffer)[index] = (unsigned int)(floor(((double)stbir__saturate(encode_buffer[index])) * 4294967295 + 0.5f)); + } + } + break; - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned int*)output_buffer)[index] = (unsigned int)(floor(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295 + 0.5f)); - } + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((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)(floor(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295 + 0.5f)); - } - break; + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)(floor(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295 + 0.5f)); + } + break; - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((float*)output_buffer)[index] = encode_buffer[index]; - } - } - break; + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((float*)output_buffer)[index] = encode_buffer[index]; + } + } + break; - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); - } + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); + } - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; - } - break; + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; + } + break; - default: - STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); - break; - } + default: + STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; + } } static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) { - int x, k; - int output_w = stbir_info->output_w; - stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; - float* vertical_coefficients = stbir_info->vertical_coefficients; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int kernel_pixel_width = stbir__get_filter_pixel_width_vertical(stbir_info); - void* output_data = stbir_info->output_data; - float* encode_buffer = stbir_info->encode_buffer; - int decode = STBIR__DECODE(type, colorspace); - int coefficient_width = stbir__get_coefficient_width(stbir_info->vertical_filter, stbir_info->vertical_scale); - int contributor = n; + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int kernel_pixel_width = stbir__get_filter_pixel_width_vertical(stbir_info); + void* output_data = stbir_info->output_data; + float* encode_buffer = stbir_info->encode_buffer; + int decode = STBIR__DECODE(type, colorspace); + int coefficient_width = stbir__get_coefficient_width(stbir_info->vertical_filter, stbir_info->vertical_scale); + int contributor = n; - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; - int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - int n0,n1, output_row_start; - int coefficient_group = coefficient_width * contributor; + int n0,n1, output_row_start; + int coefficient_group = coefficient_width * contributor; - n0 = vertical_contributors[contributor].n0; - n1 = vertical_contributors[contributor].n1; + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; - output_row_start = n * stbir_info->output_stride_bytes; + output_row_start = n * stbir_info->output_stride_bytes; - STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); + STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); - memset(encode_buffer, 0, output_w * sizeof(float) * channels); + memset(encode_buffer, 0, output_w * sizeof(float) * channels); - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * channels; - int coefficient_counter = 0; + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + int coefficient_counter = 0; - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - int c; - for (c = 0; c < channels; c++) - encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; - } - } - stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); + int c; + for (c = 0; c < channels; c++) + encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; + } + } + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); } static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) { - int x, k; - int output_w = stbir_info->output_w; - int output_h = stbir_info->output_h; - stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; - float* vertical_coefficients = stbir_info->vertical_coefficients; - int channels = stbir_info->channels; - int kernel_pixel_width = stbir__get_filter_pixel_width_vertical(stbir_info); - void* output_data = stbir_info->output_data; - float* horizontal_buffer = stbir_info->horizontal_buffer; - int coefficient_width = stbir__get_coefficient_width(stbir_info->vertical_filter, stbir_info->vertical_scale); - int contributor = n + stbir__get_filter_pixel_margin_vertical(stbir_info); + int x, k; + int output_w = stbir_info->output_w; + int output_h = stbir_info->output_h; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int kernel_pixel_width = stbir__get_filter_pixel_width_vertical(stbir_info); + void* output_data = stbir_info->output_data; + float* horizontal_buffer = stbir_info->horizontal_buffer; + int coefficient_width = stbir__get_coefficient_width(stbir_info->vertical_filter, stbir_info->vertical_scale); + int contributor = n + stbir__get_filter_pixel_margin_vertical(stbir_info); - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; - int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - int n0,n1; + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + int n0,n1; - n0 = vertical_contributors[contributor].n0; - n1 = vertical_contributors[contributor].n1; + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; - STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); + STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); - for (k = n0; k <= n1; k++) - { - int coefficient_index = k - n0; - int coefficient_group = coefficient_width * contributor; - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (k = n0; k <= n1; k++) + { + int coefficient_index = k - n0; + int coefficient_group = coefficient_width * contributor; + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * channels; + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; - int c; - for (c = 0; c < channels; c++) - ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; - } - } + int c; + for (c = 0; c < channels; c++) + ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; + } + } } static void stbir__buffer_loop_upsample(stbir__info* stbir_info) { - int y; - float scale_ratio = stbir_info->vertical_scale; - float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; + int y; + float scale_ratio = stbir_info->vertical_scale; + float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; - STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); + STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); - for (y = 0; y < stbir_info->output_h; y++) - { - float in_center_of_out = 0; // Center of the current out scanline in the in scanline space - int in_first_scanline = 0, in_last_scanline = 0; + for (y = 0; y < stbir_info->output_h; y++) + { + float in_center_of_out = 0; // Center of the current out scanline in the in scanline space + int in_first_scanline = 0, in_last_scanline = 0; - stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); + stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); - STBIR__DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbir__get_filter_pixel_width_vertical(stbir_info)); + STBIR__DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbir__get_filter_pixel_width_vertical(stbir_info)); - if (stbir_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (in_first_scanline > stbir_info->ring_buffer_first_scanline) - { - if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbir_info->ring_buffer_begin_index = -1; - stbir_info->ring_buffer_first_scanline = 0; - stbir_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir__get_filter_pixel_width_vertical(stbir_info); - } - } - } + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (in_first_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir__get_filter_pixel_width_vertical(stbir_info); + } + } + } - // Load in new ones. - if (stbir_info->ring_buffer_begin_index < 0) - stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); - while (in_last_scanline > stbir_info->ring_buffer_last_scanline) - stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + while (in_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); - // Now all buffers should be ready to write a row of vertical sampling. - stbir__resample_vertical_upsample(stbir_info, y, in_first_scanline, in_last_scanline, in_center_of_out); + // Now all buffers should be ready to write a row of vertical sampling. + stbir__resample_vertical_upsample(stbir_info, y, in_first_scanline, in_last_scanline, in_center_of_out); - STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); - } + STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); + } } static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) { - int output_stride_bytes = stbir_info->output_stride_bytes; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int output_w = stbir_info->output_w; - void* output_data = stbir_info->output_data; - int decode = STBIR__DECODE(type, colorspace); + int output_stride_bytes = stbir_info->output_stride_bytes; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int output_w = stbir_info->output_w; + void* output_data = stbir_info->output_data; + int decode = STBIR__DECODE(type, colorspace); - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - if (stbir_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) - { - if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) - { - int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; - float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); - stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); - STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); - } + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) + { + int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; + float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); + STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); + } - if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbir_info->ring_buffer_begin_index = -1; - stbir_info->ring_buffer_first_scanline = 0; - stbir_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir__get_filter_pixel_width_vertical(stbir_info); - } - } - } + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir__get_filter_pixel_width_vertical(stbir_info); + } + } + } } static void stbir__buffer_loop_downsample(stbir__info* stbir_info) { - int y; - float scale_ratio = stbir_info->vertical_scale; - int output_h = stbir_info->output_h; - float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; - int pixel_margin = stbir__get_filter_pixel_margin_vertical(stbir_info); - int max_y = stbir_info->input_h + pixel_margin; + int y; + float scale_ratio = stbir_info->vertical_scale; + int output_h = stbir_info->output_h; + float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; + int pixel_margin = stbir__get_filter_pixel_margin_vertical(stbir_info); + int max_y = stbir_info->input_h + pixel_margin; - STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); + STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); - for (y = -pixel_margin; y < max_y; y++) - { - float out_center_of_in; // Center of the current out scanline in the in scanline space - int out_first_scanline, out_last_scanline; + for (y = -pixel_margin; y < max_y; y++) + { + float out_center_of_in; // Center of the current out scanline in the in scanline space + int out_first_scanline, out_last_scanline; - stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); + stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); - STBIR__DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbir__get_filter_pixel_width_vertical(stbir_info)); + STBIR__DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbir__get_filter_pixel_width_vertical(stbir_info)); - if (out_last_scanline < 0 || out_first_scanline >= output_h) - continue; + if (out_last_scanline < 0 || out_first_scanline >= output_h) + continue; - stbir__empty_ring_buffer(stbir_info, out_first_scanline); + stbir__empty_ring_buffer(stbir_info, out_first_scanline); - stbir__decode_and_resample_downsample(stbir_info, y); + stbir__decode_and_resample_downsample(stbir_info, y); - // Load in new ones. - if (stbir_info->ring_buffer_begin_index < 0) - stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); - while (out_last_scanline > stbir_info->ring_buffer_last_scanline) - stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + while (out_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); - // Now the horizontal buffer is ready to write to all ring buffer rows. - stbir__resample_vertical_downsample(stbir_info, y, out_first_scanline, out_last_scanline, out_center_of_in); - } + // Now the horizontal buffer is ready to write to all ring buffer rows. + stbir__resample_vertical_downsample(stbir_info, y, out_first_scanline, out_last_scanline, out_center_of_in); + } - stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); + stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); } static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) { - info->input_w = input_w; - info->input_h = input_h; - info->output_w = output_w; - info->output_h = output_h; - info->channels = channels; + info->input_w = input_w; + info->input_h = input_h; + info->output_w = output_w; + info->output_h = output_h; + info->channels = channels; } static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) { - info->s0 = s0; - info->t0 = t0; - info->s1 = s1; - info->t1 = t1; + info->s0 = s0; + info->t0 = t0; + info->s1 = s1; + info->t1 = t1; - if (transform) - { - info->horizontal_scale = transform[0]; - info->vertical_scale = transform[1]; - info->horizontal_shift = transform[2]; - info->vertical_shift = transform[3]; - } - else - { - info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); - info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); + if (transform) + { + info->horizontal_scale = transform[0]; + info->vertical_scale = transform[1]; + info->horizontal_shift = transform[2]; + info->vertical_shift = transform[3]; + } + else + { + info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); + info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); - info->horizontal_shift = s0 * info->input_w / (s1 - s0); - info->vertical_shift = t0 * info->input_h / (t1 - t0); - } + info->horizontal_shift = s0 * info->input_w / (s1 - s0); + info->vertical_shift = t0 * info->input_h / (t1 - t0); + } } static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) { - if (h_filter == 0) - h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; - if (v_filter == 0) - v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; - info->horizontal_filter = h_filter; - info->vertical_filter = v_filter; + if (h_filter == 0) + h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + if (v_filter == 0) + v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + info->horizontal_filter = h_filter; + info->vertical_filter = v_filter; } static stbir_uint32 stbir__calculate_memory(stbir__info *info) { - int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); - int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); + int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); - info->horizontal_contributors_size = stbir__get_horizontal_contributors(info) * sizeof(stbir__contributors); - info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); - info->vertical_contributors_size = stbir__get_vertical_contributors(info) * sizeof(stbir__contributors); - info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); - info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); - info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); - info->ring_buffer_size = info->output_w * info->channels * filter_height * sizeof(float); - info->encode_buffer_size = info->output_w * info->channels * sizeof(float); + info->horizontal_contributors_size = stbir__get_horizontal_contributors(info) * sizeof(stbir__contributors); + info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); + info->vertical_contributors_size = stbir__get_vertical_contributors(info) * sizeof(stbir__contributors); + info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); + info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); + info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); + info->ring_buffer_size = info->output_w * info->channels * filter_height * sizeof(float); + info->encode_buffer_size = info->output_w * info->channels * sizeof(float); - STBIR_ASSERT(info->horizontal_filter != 0); - STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - STBIR_ASSERT(info->vertical_filter != 0); - STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(info->horizontal_filter != 0); + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(info->vertical_filter != 0); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - if (stbir__use_height_upsampling(info)) - // The horizontal buffer is for when we're downsampling the height and we - // can't output the result of sampling the decode buffer directly into the - // ring buffers. - info->horizontal_buffer_size = 0; - else - // The encode buffer is to retain precision in the height upsampling method - // and isn't used when height downsampling. - info->encode_buffer_size = 0; + if (stbir__use_height_upsampling(info)) + // The horizontal buffer is for when we're downsampling the height and we + // can't output the result of sampling the decode buffer directly into the + // ring buffers. + info->horizontal_buffer_size = 0; + else + // The encode buffer is to retain precision in the height upsampling method + // and isn't used when height downsampling. + info->encode_buffer_size = 0; - return info->horizontal_contributors_size + info->horizontal_coefficients_size - + info->vertical_contributors_size + info->vertical_coefficients_size - + info->decode_buffer_size + info->horizontal_buffer_size - + info->ring_buffer_size + info->encode_buffer_size; + return info->horizontal_contributors_size + info->horizontal_coefficients_size + + info->vertical_contributors_size + info->vertical_coefficients_size + + info->decode_buffer_size + info->horizontal_buffer_size + + info->ring_buffer_size + info->encode_buffer_size; } static int stbir__resize_allocated(stbir__info *info, const void* input_data, int input_stride_in_bytes, - void* output_data, int output_stride_in_bytes, - int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, - void* tempmem, size_t tempmem_size_in_bytes) + void* output_data, int output_stride_in_bytes, + int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, + void* tempmem, size_t tempmem_size_in_bytes) { - size_t memory_required = stbir__calculate_memory(info); + size_t memory_required = stbir__calculate_memory(info); - int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; - int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; + int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; + int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; #ifdef STBIR_DEBUG_OVERWRITE_TEST #define OVERWRITE_ARRAY_SIZE 8 - unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; - size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; - memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); + size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; + memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); #endif - STBIR_ASSERT(info->channels >= 0); + STBIR_ASSERT(info->channels >= 0); - if (info->channels < 0) - return 0; + if (info->channels < 0) + return 0; - STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; - if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; + if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; - if (alpha_channel < 0) - flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; + if (alpha_channel < 0) + flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; - if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) - STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); + 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) - return 0; + if (alpha_channel >= info->channels) + return 0; - STBIR_ASSERT(tempmem); + STBIR_ASSERT(tempmem); - if (!tempmem) - return 0; + if (!tempmem) + return 0; - STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); + STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); - if (tempmem_size_in_bytes < memory_required) - return 0; + if (tempmem_size_in_bytes < memory_required) + return 0; - memset(tempmem, 0, tempmem_size_in_bytes); + memset(tempmem, 0, tempmem_size_in_bytes); - info->input_data = input_data; - info->input_stride_bytes = width_stride_input; + info->input_data = input_data; + info->input_stride_bytes = width_stride_input; - info->output_data = output_data; - info->output_stride_bytes = width_stride_output; + info->output_data = output_data; + info->output_stride_bytes = width_stride_output; - info->alpha_channel = alpha_channel; - info->flags = flags; - info->type = type; - info->edge_horizontal = edge_horizontal; - info->edge_vertical = edge_vertical; - info->colorspace = colorspace; + info->alpha_channel = alpha_channel; + info->flags = flags; + info->type = type; + info->edge_horizontal = edge_horizontal; + info->edge_vertical = edge_vertical; + info->colorspace = colorspace; - info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); - info->decode_buffer_pixels = info->input_w + stbir__get_filter_pixel_margin_horizontal(info) * 2; + info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); + info->decode_buffer_pixels = info->input_w + stbir__get_filter_pixel_margin_horizontal(info) * 2; #define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) - info->horizontal_contributors = (stbir__contributors *) tempmem; - info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); - info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); - info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); - info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); + info->horizontal_contributors = (stbir__contributors *) tempmem; + info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); + info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); + info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); + info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); - if (stbir__use_height_upsampling(info)) - { - info->horizontal_buffer = NULL; - info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); - info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); + if (stbir__use_height_upsampling(info)) + { + info->horizontal_buffer = NULL; + info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); - STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - else - { - info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); - info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); - info->encode_buffer = NULL; + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + else + { + info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); + info->encode_buffer = NULL; - STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } #undef STBIR__NEXT_MEMPTR - // This signals that the ring buffer is empty - info->ring_buffer_begin_index = -1; + // This signals that the ring buffer is empty + info->ring_buffer_begin_index = -1; - stbir__calculate_filters(info, info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); - stbir__calculate_filters(info, info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); + stbir__calculate_filters(info, info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); + stbir__calculate_filters(info, info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); - STBIR_PROGRESS_REPORT(0); + STBIR_PROGRESS_REPORT(0); - if (stbir__use_height_upsampling(info)) - stbir__buffer_loop_upsample(info); - else - stbir__buffer_loop_downsample(info); + if (stbir__use_height_upsampling(info)) + stbir__buffer_loop_upsample(info); + else + stbir__buffer_loop_downsample(info); - STBIR_PROGRESS_REPORT(1); + STBIR_PROGRESS_REPORT(1); #ifdef STBIR_DEBUG_OVERWRITE_TEST - STBIR__DEBUG_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBIR__DEBUG_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); - STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); #endif - return 1; + return 1; } static int stbir__resize_arbitrary( - void *alloc_context, + void *alloc_context, const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, float *transform, - int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_filter h_filter, stbir_filter v_filter, - stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, float *transform, + int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_filter h_filter, stbir_filter v_filter, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) { - stbir__info info; - int result; - size_t memory_required; - void* extra_memory; + stbir__info info; + int result; + size_t memory_required; + void* extra_memory; - stbir__setup(&info, input_w, input_h, output_w, output_h, channels); - stbir__calculate_transform(&info, s0,t0,s1,t1,transform); - stbir__choose_filter(&info, h_filter, v_filter); - memory_required = stbir__calculate_memory(&info); - extra_memory = STBIR_MALLOC(memory_required, alloc_context); + stbir__setup(&info, input_w, input_h, output_w, output_h, channels); + stbir__calculate_transform(&info, s0,t0,s1,t1,transform); + stbir__choose_filter(&info, h_filter, v_filter); + memory_required = stbir__calculate_memory(&info); + extra_memory = STBIR_MALLOC(memory_required, alloc_context); - if (!extra_memory) - return 0; + if (!extra_memory) + return 0; - result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, - output_data, output_stride_in_bytes, - alpha_channel, flags, type, - edge_horizontal, edge_vertical, - colorspace, extra_memory, memory_required); + result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, + output_data, output_stride_in_bytes, + alpha_channel, flags, type, + edge_horizontal, edge_vertical, + colorspace, extra_memory, memory_required); - STBIR_FREE(extra_memory, alloc_context); + STBIR_FREE(extra_memory, alloc_context); - return result; + return result; } STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels) { - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); } STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels) { - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); } STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags) { - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); } STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, @@ -2143,10 +2143,10 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode) { - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); } STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, @@ -2155,10 +2155,10 @@ STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); } STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, @@ -2167,10 +2167,10 @@ STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); } @@ -2180,10 +2180,10 @@ STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); } @@ -2195,10 +2195,10 @@ STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context) { - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); } @@ -2212,15 +2212,15 @@ STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int float x_scale, float y_scale, float x_offset, float y_offset) { - float transform[4]; - transform[0] = x_scale; - transform[1] = y_scale; - transform[2] = x_offset; - transform[3] = y_offset; - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); + float transform[4]; + transform[0] = x_scale; + transform[1] = y_scale; + transform[2] = x_offset; + transform[3] = y_offset; + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); } STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, @@ -2232,10 +2232,10 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int stbir_colorspace space, void *alloc_context, float s0, float t0, float s1, float t1) { - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); } #endif // STB_IMAGE_RESIZE_IMPLEMENTATION From 59898db41159a10f11de2bd60c2d8e7ae99c0a0e Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 06:05:00 -0700 Subject: [PATCH 145/168] tweak grammar --- stb_image_resize.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 32e4480..ac7c49a 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -82,7 +82,7 @@ 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.) + that doesn't exist in the source image.) 2. Artists should not edit premultiplied-alpha images; artists want non-premultiplied alpha images. Thus, art tools generally output From 3ee97c221f8e433a129f13e9739a201b9cff9e51 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 06:12:40 -0700 Subject: [PATCH 146/168] compile as C --- stb_image_resize.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index ac7c49a..7c8397c 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1393,10 +1393,10 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, { int in_pixel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + int c; STBIR__DEBUG_ASSERT(coefficient != 0); - int c; for (c = 0; c < channels; c++) output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; } @@ -1433,10 +1433,10 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n { int out_pixel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + int c; STBIR__DEBUG_ASSERT(coefficient != 0); - int c; for (c = 0; c < channels; c++) output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; } From c9600c012f63310d6f19e06bc4404d08c4d601b1 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 06:23:07 -0700 Subject: [PATCH 147/168] unroll channel loops in downsampler --- stb_image_resize.h | 114 ++++++++++++++++++++++++++++++++++++++++++------ tests/resample_test.cpp | 16 +++++++ 2 files changed, 116 insertions(+), 14 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 7c8397c..693d068 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1429,16 +1429,64 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n int max_n = n1; int coefficient_group = coefficient_width * x; - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - int c; + switch (channels) { + case 1: + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + break; - STBIR__DEBUG_ASSERT(coefficient != 0); + case 2: + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_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; + } + break; - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + case 3: + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_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; + } + break; + + case 4: + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_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; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + break; + + default: + for (k = n0; k <= max_n; k++) + { + int c; + int out_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + break; } } } @@ -1717,13 +1765,51 @@ static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * channels; + switch (channels) { + case 1: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 1; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 2; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 3; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 4; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; - int c; - for (c = 0; c < channels; c++) - ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; + int c; + for (c = 0; c < channels; c++) + ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; + } + break; } } } diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index d746f6c..d96562f 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -141,6 +141,21 @@ static void resizer(int argc, char **argv) exit(0); } +static void performance(int argc, char **argv) +{ + unsigned char* input_pixels; + unsigned char* output_pixels; + int w, h; + int n, i; + int out_w, out_h; + input_pixels = stbi_load(argv[1], &w, &h, &n, 0); + out_w = w/4; + out_h = h/4; + output_pixels = (unsigned char*) malloc(out_w*out_h*n); + for (i=0; i < 100; ++i) + stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, -1,0); + exit(0); +} void test_suite(int argc, char **argv); @@ -153,6 +168,7 @@ int main(int argc, char** argv) int out_w, out_h, out_stride; //resizer(argc, argv); + performance(argc, argv); #if 1 test_suite(argc, argv); From 972456cb6363aed88677bc7ddf68f633cb529cdf Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 06:53:53 -0700 Subject: [PATCH 148/168] explicit performance tests variants --- tests/resample_test.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index d96562f..335025f 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -145,15 +145,28 @@ static void performance(int argc, char **argv) { unsigned char* input_pixels; unsigned char* output_pixels; - int w, h; + int w, h, count; int n, i; - int out_w, out_h; + int out_w, out_h, srgb=1; input_pixels = stbi_load(argv[1], &w, &h, &n, 0); - out_w = w/4; - out_h = h/4; + #if 0 + out_w = w/4; out_h h/4; count=100; // 1 + #elif 0 + out_w = w*2; out_h = h/4; count=20; // 2 // note this is structured pessimily, would be much faster to downsample vertically first + #elif 0 + out_w = w/4; out_h = h*2; count=50; // 3 + #elif 0 + out_w = w*3; out_h = h*3; count=5; srgb=0; // 4 + #else + out_w = w*3; out_h = h*3; count=3; // 5 // this is dominated by linear->sRGB conversion + #endif + output_pixels = (unsigned char*) malloc(out_w*out_h*n); - for (i=0; i < 100; ++i) - stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, -1,0); + for (i=0; i < count; ++i) + if (srgb) + stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, -1,0); + else + stbir_resize_uint8(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n); exit(0); } From 9c2ae9bdb865fac93dcb6a6ff3af91a479e7a2f3 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 07:29:04 -0700 Subject: [PATCH 149/168] cache filter-width derived computations that use floor/ceil because those were showing up like crazy on VC6 profile despite only being per-scanline-ish?!? unroll inner loop in horizontal_resample --- stb_image_resize.h | 209 +++++++++++++++++++++++++++++------------------------ 1 file changed, 115 insertions(+), 94 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 693d068..06604dc 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -488,6 +488,16 @@ typedef struct float* horizontal_buffer; + // cache these because ceil/floor are inexplicably showing up in profile + int horizontal_coefficient_width; + int vertical_coefficient_width; + int horizontal_filter_pixel_width; + int vertical_filter_pixel_width; + int horizontal_filter_pixel_margin; + int vertical_filter_pixel_margin; + int horizontal_num_contributors; + int vertical_num_contributors; + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) int ring_buffer_first_scanline; int ring_buffer_last_scanline; @@ -749,7 +759,7 @@ stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) // This is the maximum number of input samples that can affect an output sample // with the given filter -stbir__inline static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) +static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) { STBIR_ASSERT(filter != 0); STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); @@ -760,34 +770,14 @@ stbir__inline static int stbir__get_filter_pixel_width(stbir_filter filter, floa return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); } -stbir__inline static int stbir__get_filter_pixel_width_horizontal(stbir__info* stbir_info) -{ - return stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); -} - -stbir__inline static int stbir__get_filter_pixel_width_vertical(stbir__info* stbir_info) -{ - return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->vertical_scale); -} - // This is how much to expand buffers to account for filters seeking outside // the image boundaries. -stbir__inline static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) +static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) { return stbir__get_filter_pixel_width(filter, scale) / 2; } -stbir__inline static int stbir__get_filter_pixel_margin_horizontal(stbir__info* stbir_info) -{ - return stbir__get_filter_pixel_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale) / 2; -} - -stbir__inline static int stbir__get_filter_pixel_margin_vertical(stbir__info* stbir_info) -{ - return stbir__get_filter_pixel_width(stbir_info->vertical_filter, stbir_info->vertical_scale) / 2; -} - -stbir__inline static int stbir__get_coefficient_width(stbir_filter filter, float scale) +static int stbir__get_coefficient_width(stbir_filter filter, float scale) { if (stbir__use_upsampling(scale)) return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); @@ -795,7 +785,7 @@ stbir__inline static int stbir__get_coefficient_width(stbir_filter filter, float return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); } -stbir__inline static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) +static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) { if (stbir__use_upsampling(scale)) return output_size; @@ -803,25 +793,15 @@ stbir__inline static int stbir__get_contributors(float scale, stbir_filter filte return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); } -stbir__inline static int stbir__get_horizontal_contributors(stbir__info* info) +static int stbir__get_total_horizontal_coefficients(stbir__info* info) { - return stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); -} - -stbir__inline static int stbir__get_vertical_contributors(stbir__info* info) -{ - return stbir__get_contributors(info->vertical_scale, info->vertical_filter, info->input_h, info->output_h); -} - -stbir__inline static int stbir__get_total_horizontal_coefficients(stbir__info* info) -{ - return stbir__get_horizontal_contributors(info) + return info->horizontal_num_contributors * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); } -stbir__inline static int stbir__get_total_vertical_coefficients(stbir__info* info) +static int stbir__get_total_vertical_coefficients(stbir__info* info) { - return stbir__get_vertical_contributors(info) + return info->vertical_num_contributors * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); } @@ -832,13 +812,13 @@ stbir__inline static stbir__contributors* stbir__get_contributor(stbir__contribu stbir__inline static stbir__contributors* stbir__get_horizontal_contributor(stbir__info* stbir_info, int n) { - STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_horizontal_contributors(stbir_info)); + STBIR__DEBUG_ASSERT(n >= 0 && n < stbir_info->horizontal_num_contributors); return stbir__get_contributor(stbir_info->horizontal_contributors, n); } stbir__inline static stbir__contributors* stbir__get_vertical_contributor(stbir__info* stbir_info, int n) { - STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_vertical_contributors(stbir_info)); + STBIR__DEBUG_ASSERT(n >= 0 && n < stbir_info->vertical_num_contributors); return stbir__get_contributor(stbir_info->vertical_contributors, n); } @@ -850,20 +830,6 @@ stbir__inline static float* stbir__get_coefficient(float* coefficients, stbir_fi return &coefficients[width*n + c]; } -stbir__inline static float* stbir__get_horizontal_coefficient(stbir__info* stbir_info, int n, int c) -{ - STBIR__DEBUG_ASSERT(c >= 0 && c < stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale)); - STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_total_horizontal_coefficients(stbir_info)); - return stbir__get_coefficient(stbir_info->horizontal_coefficients, stbir_info->horizontal_filter, stbir_info->horizontal_scale, n, c); -} - -stbir__inline static float* stbir__get_vertical_coefficient(stbir__info* stbir_info, int n, int c) -{ - STBIR__DEBUG_ASSERT(c >= 0 && c < stbir__get_coefficient_width(stbir_info->vertical_filter, stbir_info->vertical_scale)); - STBIR__DEBUG_ASSERT(n >= 0 && n < stbir__get_total_vertical_coefficients(stbir_info)); - return stbir__get_coefficient(stbir_info->vertical_coefficients, stbir_info->vertical_filter, stbir_info->vertical_scale, n, c); -} - static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) { switch (edge) @@ -1081,7 +1047,7 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info, st // Do this after normalizing because normalization depends on the n0/n1 values. for (j = 0; j < num_contributors; j++) { - int range, max; + int range, max, width; skip = 0; while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) @@ -1098,9 +1064,10 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info, st range = contributors[j].n1 - contributors[j].n0 + 1; max = stbir__min(num_coefficients, range); + width = stbir__get_coefficient_width(filter, scale_ratio); for (i = 0; i < max; i++) { - if (i + skip >= stbir__get_coefficient_width(filter, scale_ratio)) + if (i + skip >= width) break; *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); @@ -1160,7 +1127,7 @@ static float* stbir__get_decode_buffer(stbir__info* stbir_info) { // The 0 index of the decode buffer starts after the margin. This makes // it okay to use negative indexes on the decode buffer. - return &stbir_info->decode_buffer[stbir__get_filter_pixel_margin_horizontal(stbir_info) * stbir_info->channels]; + return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; } #define STBIR__DECODE(type, colorspace) ((type) * (STBIR_MAX_COLORSPACES) + (colorspace)) @@ -1179,10 +1146,10 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) stbir_edge edge_vertical = stbir_info->edge_vertical; int in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; - int max_x = input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info); + int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; int decode = STBIR__DECODE(type, colorspace); - int x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); + int x = -stbir_info->horizontal_filter_pixel_margin; // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO @@ -1296,7 +1263,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) { - for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < max_x; x++) + for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) { int decode_pixel_index = x * channels; @@ -1320,7 +1287,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) if (edge_horizontal == STBIR_EDGE_ZERO) { - for (x = -stbir__get_filter_pixel_margin_horizontal(stbir_info); x < 0; x++) + for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) { for (c = 0; c < channels; c++) decode_buffer[x*channels + c] = 0; @@ -1350,7 +1317,7 @@ static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) } else { - ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline) + 1) % stbir__get_filter_pixel_width_vertical(stbir_info); + ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline) + 1) % stbir_info->vertical_filter_pixel_width; STBIR__DEBUG_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); } @@ -1367,12 +1334,12 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, { int x, k; int output_w = stbir_info->output_w; - int kernel_pixel_width = stbir__get_filter_pixel_width_horizontal(stbir_info); + int kernel_pixel_width = stbir_info->horizontal_filter_pixel_width; int channels = stbir_info->channels; float* decode_buffer = stbir__get_decode_buffer(stbir_info); stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); + int coefficient_width = stbir_info->horizontal_coefficient_width; for (x = 0; x < output_w; x++) { @@ -1384,21 +1351,65 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, int coefficient_counter = 0; STBIR__DEBUG_ASSERT(n1 >= n0); - STBIR__DEBUG_ASSERT(n0 >= -stbir__get_filter_pixel_margin_horizontal(stbir_info)); - STBIR__DEBUG_ASSERT(n1 >= -stbir__get_filter_pixel_margin_horizontal(stbir_info)); - STBIR__DEBUG_ASSERT(n0 < stbir_info->input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info)); - STBIR__DEBUG_ASSERT(n1 < stbir_info->input_w + stbir__get_filter_pixel_margin_horizontal(stbir_info)); + STBIR__DEBUG_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR__DEBUG_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR__DEBUG_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + STBIR__DEBUG_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - int c; - - STBIR__DEBUG_ASSERT(coefficient != 0); - - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR__DEBUG_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; + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR__DEBUG_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; + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR__DEBUG_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; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + int c; + STBIR__DEBUG_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + break; } } } @@ -1408,13 +1419,13 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n int x, k; int input_w = stbir_info->input_w; int output_w = stbir_info->output_w; - int kernel_pixel_width = stbir__get_filter_pixel_width_horizontal(stbir_info); + int kernel_pixel_width = stbir_info->horizontal_filter_pixel_width; int channels = stbir_info->channels; float* decode_buffer = stbir__get_decode_buffer(stbir_info); stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir__get_coefficient_width(stbir_info->horizontal_filter, stbir_info->horizontal_scale); - int filter_pixel_margin = stbir__get_filter_pixel_margin_horizontal(stbir_info); + int coefficient_width = stbir_info->horizontal_coefficient_width; + int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; int max_x = input_w + filter_pixel_margin * 2; STBIR__DEBUG_ASSERT(!stbir__use_width_upsampling(stbir_info)); @@ -1687,11 +1698,11 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int alpha_channel = stbir_info->alpha_channel; int type = stbir_info->type; int colorspace = stbir_info->colorspace; - int kernel_pixel_width = stbir__get_filter_pixel_width_vertical(stbir_info); + int kernel_pixel_width = stbir_info->vertical_filter_pixel_width; void* output_data = stbir_info->output_data; float* encode_buffer = stbir_info->encode_buffer; int decode = STBIR__DECODE(type, colorspace); - int coefficient_width = stbir__get_coefficient_width(stbir_info->vertical_filter, stbir_info->vertical_scale); + int coefficient_width = stbir_info->vertical_coefficient_width; int contributor = n; float* ring_buffer = stbir_info->ring_buffer; @@ -1739,11 +1750,11 @@ static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; float* vertical_coefficients = stbir_info->vertical_coefficients; int channels = stbir_info->channels; - int kernel_pixel_width = stbir__get_filter_pixel_width_vertical(stbir_info); + int kernel_pixel_width = stbir_info->vertical_filter_pixel_width; void* output_data = stbir_info->output_data; float* horizontal_buffer = stbir_info->horizontal_buffer; - int coefficient_width = stbir__get_coefficient_width(stbir_info->vertical_filter, stbir_info->vertical_scale); - int contributor = n + stbir__get_filter_pixel_margin_vertical(stbir_info); + int coefficient_width = stbir_info->vertical_coefficient_width; + int contributor = n + stbir_info->vertical_filter_pixel_margin; float* ring_buffer = stbir_info->ring_buffer; int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; @@ -1829,7 +1840,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info) stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); - STBIR__DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbir__get_filter_pixel_width_vertical(stbir_info)); + STBIR__DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbir_info->vertical_filter_pixel_width); if (stbir_info->ring_buffer_begin_index >= 0) { @@ -1848,7 +1859,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info) else { stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir__get_filter_pixel_width_vertical(stbir_info); + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->vertical_filter_pixel_width; } } } @@ -1906,7 +1917,7 @@ static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessar else { stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir__get_filter_pixel_width_vertical(stbir_info); + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->vertical_filter_pixel_width; } } } @@ -1918,7 +1929,7 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info) float scale_ratio = stbir_info->vertical_scale; int output_h = stbir_info->output_h; float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; - int pixel_margin = stbir__get_filter_pixel_margin_vertical(stbir_info); + int pixel_margin = stbir_info->vertical_filter_pixel_margin; int max_y = stbir_info->input_h + pixel_margin; STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); @@ -1930,7 +1941,7 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info) stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); - STBIR__DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbir__get_filter_pixel_width_vertical(stbir_info)); + STBIR__DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbir_info->vertical_filter_pixel_width); if (out_last_scanline < 0 || out_first_scanline >= output_h) continue; @@ -2001,9 +2012,12 @@ static stbir_uint32 stbir__calculate_memory(stbir__info *info) int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); - info->horizontal_contributors_size = stbir__get_horizontal_contributors(info) * sizeof(stbir__contributors); + info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); + info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); + + info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); - info->vertical_contributors_size = stbir__get_vertical_contributors(info) * sizeof(stbir__contributors); + info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); @@ -2104,8 +2118,15 @@ static int stbir__resize_allocated(stbir__info *info, info->edge_vertical = edge_vertical; info->colorspace = colorspace; + info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); + info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); - info->decode_buffer_pixels = info->input_w + stbir__get_filter_pixel_margin_horizontal(info) * 2; + info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; #define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) From 7a8c9196d1980c51f4618cfb79bc047ca22fc8db Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 07:41:39 -0700 Subject: [PATCH 150/168] unroll vertical_upsample inner loop, and reverse loop nesting to minimize work --- stb_image_resize.h | 91 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 15 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 06604dc..3853ca5 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1703,6 +1703,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in float* encode_buffer = stbir_info->encode_buffer; int decode = STBIR__DECODE(type, colorspace); int coefficient_width = stbir_info->vertical_coefficient_width; + int coefficient_counter; int contributor = n; float* ring_buffer = stbir_info->ring_buffer; @@ -1723,21 +1724,81 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in memset(encode_buffer, 0, output_w * sizeof(float) * channels); - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * channels; - int coefficient_counter = 0; - - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - - int c; - for (c = 0; c < channels; c++) - encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; - } + coefficient_counter = 0; + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + } + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + } + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + } + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; + } + } + break; + default: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + int c; + for (c = 0; c < channels; c++) + encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; + } + } + break; } stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); } From adbbe8dabd14f32d2c7052f5a72bf8f04613c6d1 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 07:46:09 -0700 Subject: [PATCH 151/168] move x loop inside switch in horizontal_downsample --- stb_image_resize.h | 84 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 3853ca5..71d076b 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1430,18 +1430,18 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n STBIR__DEBUG_ASSERT(!stbir__use_width_upsampling(stbir_info)); - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; + switch (channels) { + case 1: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * channels; - int max_n = n1; - int coefficient_group = coefficient_width * x; + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 1; + int max_n = n1; + int coefficient_group = coefficient_width * x; - switch (channels) { - case 1: for (k = n0; k <= max_n; k++) { int out_pixel_index = k * 1; @@ -1449,9 +1449,20 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n STBIR__DEBUG_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; } - break; + } + break; + + case 2: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 2; + int max_n = n1; + int coefficient_group = coefficient_width * x; - case 2: for (k = n0; k <= max_n; k++) { int out_pixel_index = k * 2; @@ -1460,9 +1471,20 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n 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; } - break; + } + break; + + case 3: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 3; + int max_n = n1; + int coefficient_group = coefficient_width * x; - case 3: for (k = n0; k <= max_n; k++) { int out_pixel_index = k * 3; @@ -1472,9 +1494,20 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n 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; } - break; + } + break; + + case 4: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 4; + int max_n = n1; + int coefficient_group = coefficient_width * x; - case 4: for (k = n0; k <= max_n; k++) { int out_pixel_index = k * 4; @@ -1485,9 +1518,20 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; } - break; + } + break; + + default: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * channels; + int max_n = n1; + int coefficient_group = coefficient_width * x; - default: for (k = n0; k <= max_n; k++) { int c; @@ -1497,8 +1541,8 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n for (c = 0; c < channels; c++) output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; } - break; - } + } + break; } } From 1dcca19ae0503fdf57f285028c5ec04beb272c6b Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 08:06:39 -0700 Subject: [PATCH 152/168] because of we divide by /255 not /256, we lose numerical precision, so can't guarantee that box filter results round to same results as naive --- tests/resample_test.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 335025f..1938c2c 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -155,7 +155,7 @@ static void performance(int argc, char **argv) out_w = w*2; out_h = h/4; count=20; // 2 // note this is structured pessimily, would be much faster to downsample vertically first #elif 0 out_w = w/4; out_h = h*2; count=50; // 3 - #elif 0 + #elif 1 out_w = w*3; out_h = h*3; count=5; srgb=0; // 4 #else out_w = w*3; out_h = h*3; count=3; // 5 // this is dominated by linear->sRGB conversion @@ -181,7 +181,7 @@ int main(int argc, char** argv) int out_w, out_h, out_stride; //resizer(argc, argv); - performance(argc, argv); + //performance(argc, argv); #if 1 test_suite(argc, argv); @@ -648,10 +648,10 @@ void verify_box(void) + image88[j*2+0][i*2+1] + image88[j*2+1][i*2+0] + image88[j*2+1][i*2+1]; - STBIR_ASSERT(output44[j][i] == ((n+2)>>2)); + STBIR_ASSERT(output44[j][i] == ((n+2)>>2) || output44[j][i] == ((n+1)>>2)); // can't guarantee exact rounding due to numerical precision t += n; } - STBIR_ASSERT(output11[0][0] == ((t+32)>>6)); + STBIR_ASSERT(output11[0][0] == ((t+32)>>6) || output11[0][0] == ((t+31)>>6)); // can't guarantee exact rounding due to numerical precision } void verify_filter_normalized(stbir_filter filter, int output_size, unsigned int value) @@ -675,6 +675,8 @@ void test_filters(void) { int i,j; + mtsrand(0); + for (i=0; i < sizeof(image88); ++i) image88[0][i] = mtrand() & 255; verify_box(); From 9a6af9a8d3fea77932563813cf37a7a193b95d5e Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 08:12:37 -0700 Subject: [PATCH 153/168] replace int(floor()) with int(), since that produces same results for non-negative values and is much faster on 32-bit x86 --- stb_image_resize.h | 204 ++++++++++++++++++++++++------------------------ tests/resample_test.cpp | 2 +- tests/resize.dsp | 5 +- 3 files changed, 107 insertions(+), 104 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 71d076b..35ecae0 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1607,128 +1607,130 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void } } + #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) //#define STBIR__ROUND_INT(f) (floor((f)+0.5)) + switch (decode) { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) { - int index = pixel_index + n; - ((unsigned char*)output_buffer)[index] = (unsigned char)(floor(stbir__saturate(encode_buffer[index]) * 255 + 0.5f)); + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned char*)output_buffer)[index] = (unsigned char) STBIR__ROUND_INT(stbir__saturate(encode_buffer[index]) * 255); + } } - } - break; + break; - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) { - int index = pixel_index + n; - ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned char*)output_buffer)[pixel_index + alpha_channel] = (unsigned char) STBIR__ROUND_INT(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 255); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__saturate(encode_buffer[index]) * 65535); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = (unsigned short)STBIR__ROUND_INT(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 65535); } - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned char*)output_buffer)[pixel_index + alpha_channel] = (unsigned char)(floor(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 255+0.5f)); - } - break; + break; - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) { - int index = pixel_index + n; - ((unsigned short*)output_buffer)[index] = (unsigned short)(floor(stbir__saturate(encode_buffer[index]) * 65535+0.5f)); + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[index])) * 4294967295); + } } - } - break; + break; - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) { - int index = pixel_index + n; - ((unsigned short*)output_buffer)[index] = (unsigned short)(floor(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535 + 0.5f)); + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_INT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295); } + break; - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = (unsigned short)(floor(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 65535 + 0.5f)); - } - - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) { - int index = pixel_index + n; - ((unsigned int*)output_buffer)[index] = (unsigned int)(floor(((double)stbir__saturate(encode_buffer[index])) * 4294967295 + 0.5f)); + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((float*)output_buffer)[index] = encode_buffer[index]; + } } - } - break; + break; - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) { - int index = pixel_index + n; - ((unsigned int*)output_buffer)[index] = (unsigned int)(floor(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295 + 0.5f)); + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; } + break; - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)(floor(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295 + 0.5f)); - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((float*)output_buffer)[index] = encode_buffer[index]; - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; - } - break; - - default: - STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); - break; + default: + STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; } } diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 1938c2c..71902aa 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -181,7 +181,7 @@ int main(int argc, char** argv) int out_w, out_h, out_stride; //resizer(argc, argv); - //performance(argc, argv); + performance(argc, argv); #if 1 test_suite(argc, argv); diff --git a/tests/resize.dsp b/tests/resize.dsp index df52874..2c170f2 100644 --- a/tests/resize.dsp +++ b/tests/resize.dsp @@ -39,9 +39,10 @@ RSC=rc.exe # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /Z7 /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -49,7 +50,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 !ELSEIF "$(CFG)" == "resize - Win32 Debug" From 8849501a3f2251068921d338f999019312906cad Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 08:21:14 -0700 Subject: [PATCH 154/168] add documentation explaining why even a 64K table for srgb_to_uint8 would be inaccurate --- stb_image_resize.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stb_image_resize.h b/stb_image_resize.h index 35ecae0..288f799 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -565,6 +565,10 @@ static float stbir__srgb_uchar_to_linear_float[256] = { }; // sRGB transition values, scaled by 1<<28 +// note that if you only scaled by 1<<16, all the values would be 4K smaller, +// so [1] would be ~10, and so that would have around 5% error (10 +- 0.5) +// at the boundary between uint8 0 and 1. This also means that a 64K-entry table +// would have the same 5% error there. static int stbir__srgb_offset_to_linear_scaled[256] = { 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, From f711fdcb0f019fdaacc24c9315e5bffd115a9ed3 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 08:22:15 -0700 Subject: [PATCH 155/168] disable performance test in test code, back to regression test path --- tests/resample_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 71902aa..1f654e6 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -149,13 +149,13 @@ static void performance(int argc, char **argv) int n, i; int out_w, out_h, srgb=1; input_pixels = stbi_load(argv[1], &w, &h, &n, 0); - #if 0 + #if 1 out_w = w/4; out_h h/4; count=100; // 1 #elif 0 out_w = w*2; out_h = h/4; count=20; // 2 // note this is structured pessimily, would be much faster to downsample vertically first #elif 0 out_w = w/4; out_h = h*2; count=50; // 3 - #elif 1 + #elif 0 out_w = w*3; out_h = h*3; count=5; srgb=0; // 4 #else out_w = w*3; out_h = h*3; count=3; // 5 // this is dominated by linear->sRGB conversion @@ -181,7 +181,7 @@ int main(int argc, char** argv) int out_w, out_h, out_stride; //resizer(argc, argv); - performance(argc, argv); + //performance(argc, argv); #if 1 test_suite(argc, argv); From 385d7a417c70c1e17e0a37dbb6d72f4c05c34116 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 13 Sep 2014 08:50:10 -0700 Subject: [PATCH 156/168] delete some unused functions; get rid of stbir__inline for some functions that don't need it --- stb_image_resize.h | 16 ++-------------- tests/resize.dsp | 2 +- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 288f799..a6e453d 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -809,26 +809,14 @@ static int stbir__get_total_vertical_coefficients(stbir__info* info) * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); } -stbir__inline static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) +static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) { return &contributors[n]; } -stbir__inline static stbir__contributors* stbir__get_horizontal_contributor(stbir__info* stbir_info, int n) -{ - STBIR__DEBUG_ASSERT(n >= 0 && n < stbir_info->horizontal_num_contributors); - return stbir__get_contributor(stbir_info->horizontal_contributors, n); -} - -stbir__inline static stbir__contributors* stbir__get_vertical_contributor(stbir__info* stbir_info, int n) -{ - STBIR__DEBUG_ASSERT(n >= 0 && n < stbir_info->vertical_num_contributors); - return stbir__get_contributor(stbir_info->vertical_contributors, n); -} - // For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, // if you change it here change it there too. -stbir__inline static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) +static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) { int width = stbir__get_coefficient_width(filter, scale); return &coefficients[width*n + c]; diff --git a/tests/resize.dsp b/tests/resize.dsp index 2c170f2..e670af2 100644 --- a/tests/resize.dsp +++ b/tests/resize.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /Z7 /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /G6 /W3 /GX /Z7 /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe From f0ba7f5f7eebd279d29b639dceb775ac46a21810 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 13 Sep 2014 13:42:50 -0700 Subject: [PATCH 157/168] Get rid of old perf tests. --- tests/resample_test.cpp | 81 +------------------------------------------------ 1 file changed, 1 insertion(+), 80 deletions(-) diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 1f654e6..2bca200 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -150,7 +150,7 @@ static void performance(int argc, char **argv) int out_w, out_h, srgb=1; input_pixels = stbi_load(argv[1], &w, &h, &n, 0); #if 1 - out_w = w/4; out_h h/4; count=100; // 1 + out_w = w/4; out_h = h/4; count=100; // 1 #elif 0 out_w = w*2; out_h = h/4; count=20; // 2 // note this is structured pessimily, would be much faster to downsample vertically first #elif 0 @@ -174,89 +174,10 @@ void test_suite(int argc, char **argv); int main(int argc, char** argv) { - unsigned char* input_data; - unsigned char* output_data; - int w, h; - int n; - int out_w, out_h, out_stride; - //resizer(argc, argv); //performance(argc, argv); -#if 1 test_suite(argc, argv); - return 0; -#endif - - if (argc <= 1) - { - printf("No input image\n"); - return 1; - } - - input_data = stbi_load(argv[1], &w, &h, &n, 0); - if (!input_data) - { - printf("Input image could not be loaded"); - return 1; - } - - out_w = 512; - out_h = 512; - out_stride = (out_w + 10) * n; - - output_data = (unsigned char*)malloc(out_stride * out_h); - - int in_w = 512; - int in_h = 512; - - float s0 = 0.25f; - float t0 = 0.25f; - float s1 = 0.75f; - float t1 = 0.75f; - - // Cut out the outside 64 pixels all around to test the stride. - int border = 64; - STBIR_ASSERT(in_w + border <= w); - STBIR_ASSERT(in_h + border <= h); - -#ifdef PERF_TEST - struct timeb initial_time_millis, final_time_millis; - - long average = 0; - for (int j = 0; j < 10; j++) - { - ftime(&initial_time_millis); - for (int i = 0; i < 100; i++) - stbir_resize(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, STBIR_TYPE_UINT8, n, n - 1, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); - ftime(&final_time_millis); - long lapsed_ms = (long)(final_time_millis.time - initial_time_millis.time) * 1000 + (final_time_millis.millitm - initial_time_millis.millitm); - printf("Resample: %dms\n", lapsed_ms); - - average += lapsed_ms; - } - - average /= 10; - - printf("Average: %dms\n", average); - - stbi_image_free(input_data); - - stbi_write_png("output.png", out_w, out_h, n, output_data, out_stride); -#else - stbir_resize_region(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, STBIR_TYPE_UINT8, n, n-1, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, s0, t0, s1, t1); - - stbi_write_png("output-region.png", out_w, out_h, n, output_data, out_stride); - - stbir_resize_subpixel(input_data + w * border * n + border * n, in_w, in_h, w*n, output_data, out_w, out_h, out_stride, STBIR_TYPE_UINT8, n, n-1, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, in_w*s0, in_h*t0, 0.5f, 0.5f); - - stbi_write_png("output-subpixel.png", out_w, out_h, n, output_data, out_stride); - - stbi_image_free(input_data); -#endif - - free(output_data); - return 0; } From 3c261481a6d59e0dc4f14d432dc80a7c71b9cb76 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Sat, 13 Sep 2014 14:33:13 -0700 Subject: [PATCH 158/168] Add a very quick guide. --- stb_image_resize.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index a6e453d..7ea2a47 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -12,7 +12,11 @@ #define STB_IMAGE_RESIZE_IMPLEMENTATION before the #include. That will create the implementation in that file. - API + VERY QUICK GUIDE + A typical resize of a in_w by in_h image to out_w by out_h with 4 channels where channel #3 is the alpha channel might look like: + int success = stbir_resize_uint8_srgb_edgemode(input_pixels, in_w, in_h, 0, output_pixels, out_w, out_h, 0, 4, 3, 0, STBIR_EDGE_CLAMP); + + FULL API See the "header file" section of the source for API documentation. MEMORY ALLOCATION From 9f66b441bd7013c9ad8c87f3baad314432fffd93 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 14 Sep 2014 14:55:23 -0700 Subject: [PATCH 159/168] new quickstart docs; reformat docs; new simple uint32 test --- stb_image_resize.h | 244 +++++++++++++++++++++++++----------------------- tests/resample_test.cpp | 36 +++++++ 2 files changed, 163 insertions(+), 117 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 7ea2a47..9c21dc1 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1,161 +1,171 @@ -/* stb_image_resize - v0.90 - public domain image resizing - by Jorge L Rodriguez (@VinoBS) - 2014 - http://github.com/nothings/stb +/* stb_image_resize - v0.90 - public domain image resizing + by Jorge L Rodriguez (@VinoBS) - 2014 + http://github.com/nothings/stb - Written with emphasis on usage and speed. Only scaling is - currently supported, no rotations or translations. + Written with emphasis on usability, portability, and efficiency. (No + SIMD or threads, so it will not be the fastest implementation around.) + Only scaling is supported, no rotations or translations. - DOCUMENTATION + 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. - 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. + QUICKSTART + stbir_resize_uint8( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, num_channels) + stbir_resize_float(...) + stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_ chan , 0) + stbir_resize_uint8_srgb_edgemode( + input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) + WRAP/REFLECT/ZERO - VERY QUICK GUIDE - A typical resize of a in_w by in_h image to out_w by out_h with 4 channels where channel #3 is the alpha channel might look like: - int success = stbir_resize_uint8_srgb_edgemode(input_pixels, in_w, in_h, 0, output_pixels, out_w, out_h, 0, 4, 3, 0, STBIR_EDGE_CLAMP); + FULL API + See the "header file" section of the source for API documentation. - FULL API - See the "header file" section of the source for API documentation. + ADDITIONAL DOCUMENTATION - 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: + 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: - #define STBIR_MALLOC(size,context) ... - #define STBIR_FREE(ptr,context) ... + #define STBIR_MALLOC(size,context) ... + #define STBIR_FREE(ptr,context) ... - 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. + 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. - ASSERT - Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + 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 + 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 + #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. + 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. + 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: + PROGRESS + For interactive use with slow resize operations, you can install + a progress-report callback: - #define STBIR_PROGRESS_REPORT(val) some_func(val) + #define STBIR_PROGRESS_REPORT(val) some_func(val) - The parameter val is a float which goes from 0 to 1 as progress is made. + The parameter val is a float which goes from 0 to 1 as progress is made. - For example: + For example: - static void my_progress_report(float progress); - #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) + static void my_progress_report(float progress); + #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) - #define STB_IMAGE_RESIZE_IMPLEMENTATION - #include "stb_image_resize.h" + #define STB_IMAGE_RESIZE_IMPLEMENTATION + #include "stb_image_resize.h" - static void my_progress_report(float progress) - { - printf("Progress: %f%%\n", progress*100); - } + static 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: + 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. + 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 image.) + 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 image.) - 2. Artists should not edit premultiplied-alpha images; artists - want non-premultiplied alpha images. Thus, art tools generally output - non-premultiplied alpha images. + 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. + 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. This produces - the correct results for premultiplied-alpha images, but produces - less-than-ideal results for non-premultiplied-alpha images. + 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. This produces + the correct results for premultiplied-alpha images, but produces + less-than-ideal results for non-premultiplied-alpha images. - 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.) + 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 suppress the behavior described in #5 and make - all-0-alpha pixels have 0 in all channels by #defining - STBIR_NO_ALPHA_EPSILON. + 6. You can suppress the behavior described in #5 and make + all-0-alpha pixels have 0 in all channels by #defining + STBIR_NO_ALPHA_EPSILON. - 7. 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.) + 7. 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 + 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. + 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 - Don't decode all of the image data when only processing a partial tile - 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 ) + TODO + Don't decode all of the image data when only processing a partial tile + 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 #define STBIR_INCLUDE_STB_IMAGE_RESIZE_H -typedef unsigned char stbir_uint8; - #ifdef _MSC_VER +typedef unsigned char stbir_uint8; typedef unsigned short stbir_uint16; typedef unsigned int stbir_uint32; #else #include +typedef uint8_t stbir_uint8; typedef uint16_t stbir_uint16; typedef uint32_t stbir_uint32; #endif @@ -372,7 +382,7 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int #ifndef STBIR_MALLOC #include #define STBIR_MALLOC(size,c) malloc(size) -#define STBIR_FREE(ptr,c) free(ptr) +#define STBIR_FREE(ptr,c) free(ptr) #endif #ifndef _MSC_VER diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 2bca200..db38e40 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -750,6 +750,40 @@ void test_filters(void) } } +#define UMAX32 4294967295U + +static void write32(char *filename, stbir_uint32 *output, int w, int h) +{ + stbir_uint8 *data = (stbir_uint8*) malloc(w*h*3); + for (int i=0; i < w*h*3; ++i) + data[i] = output[i]>>24; + stbi_write_png(filename, w, h, 3, data, 0); + free(data); +} + +static void test_32(void) +{ + int w=100,h=120,x,y, out_w,out_h; + stbir_uint32 *input = (stbir_uint32*) malloc(4 * 3 * w * h); + stbir_uint32 *output = (stbir_uint32*) malloc(4 * 3 * 3*w * 3*h); + for (y=0; y < h; ++y) { + for (x=0; x < w; ++x) { + input[y*3*w + x*3 + 0] = x * ( UMAX32/w ); + input[y*3*w + x*3 + 1] = y * ( UMAX32/h ); + input[y*3*w + x*3 + 2] = UMAX32/2; + } + } + out_w = w*33/16; + out_h = h*33/16; + stbir_resize(input,w,h,0,output,out_w,out_h,0,STBIR_TYPE_UINT32,3,-1,0,STBIR_EDGE_CLAMP,STBIR_EDGE_CLAMP,STBIR_FILTER_DEFAULT,STBIR_FILTER_DEFAULT,STBIR_COLORSPACE_LINEAR,NULL); + write32("test-output/seantest_1.png", output,out_w,out_h); + + out_w = w*16/33; + out_h = h*16/33; + stbir_resize(input,w,h,0,output,out_w,out_h,0,STBIR_TYPE_UINT32,3,-1,0,STBIR_EDGE_CLAMP,STBIR_EDGE_CLAMP,STBIR_FILTER_DEFAULT,STBIR_FILTER_DEFAULT,STBIR_COLORSPACE_LINEAR,NULL); + write32("test-output/seantest_2.png", output,out_w,out_h); +} + void test_suite(int argc, char **argv) { @@ -758,6 +792,8 @@ void test_suite(int argc, char **argv) _mkdir("test-output"); + test_32(); + if (argc > 1) barbara = argv[1]; else From bdbf1e0ef41565f6c1d4a1e865e807a0c45ded56 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sun, 14 Sep 2014 15:01:10 -0700 Subject: [PATCH 160/168] uint32 images need to round with uint32 cast, not int cast --- stb_image_resize.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 9c21dc1..9f8268a 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1613,7 +1613,8 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void } } - #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) //#define STBIR__ROUND_INT(f) (floor((f)+0.5)) + #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) + #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) switch (decode) { @@ -1684,7 +1685,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)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[index])) * 4294967295); + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * 4294967295); } } break; @@ -1697,7 +1698,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)STBIR__ROUND_INT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295); + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295); } if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) From dd28033b34dd94fd5209636497c0a34f03203991 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Mon, 15 Sep 2014 07:23:22 -0700 Subject: [PATCH 161/168] don't encode alpha channel twice when alpha is different colorspace from other channels --- stb_image_resize.h | 96 ++++++++++++++++++++++++++++++++++++++++--------- tests/resample_test.cpp | 20 ++++++----- 2 files changed, 91 insertions(+), 25 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 9f8268a..4d52b5a 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -3,8 +3,8 @@ http://github.com/nothings/stb Written with emphasis on usability, portability, and efficiency. (No - SIMD or threads, so it will not be the fastest implementation around.) - Only scaling is supported, no rotations or translations. + SIMD or threads, so it be easily outperformed by libs that use those.) + Only scaling and translation is supported, no rotations or shears. COMPILING & LINKING In one C/C++ file that #includes this file, do this: @@ -43,6 +43,11 @@ ASSERT Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + OPTIMIZATION + Define STBIR_SATURATE_INT to compute clamp values in-range using + integer operations instead of float operations. This may be faster + on some platforms. + DEFAULT FILTERS For functions which don't provide explicit control over what filters to use, you can change the compile-time defaults with @@ -78,6 +83,10 @@ printf("Progress: %f%%\n", progress*100); } + MAX CHANNELS + If your image has more than 64 channels, define STBIR_MAX_CHANNELS + to the max you'll have. + ALPHA CHANNEL Most of the resizing functions provide the ability to control how the alpha channel of an image is processed. The important things @@ -419,6 +428,15 @@ typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1] #define STBIR_PROGRESS_REPORT(float_0_to_1) #endif +#ifndef STBIR_MAX_CHANNELS +#define STBIR_MAX_CHANNELS 64 +#endif + +#if STBIR_MAX_CHANNELS > 65536 +#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." +// because we store the indices in 16-bit variables +#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 @@ -551,6 +569,30 @@ static stbir__inline float stbir__saturate(float x) return x; } +#ifdef STBIR_SATURATE_INT +static stbir__inline stbir_uint8 stbir__saturate8(int x) +{ + if ((unsigned int) x <= 255) + return x; + + if (x < 0) + return 0; + + return 255; +} + +static stbir__inline stbir_uint16 stbir__saturate16(int x) +{ + if ((unsigned int) x <= 65535) + return x; + + if (x < 0) + return 0; + + return 65535; +} +#endif + static float stbir__srgb_uchar_to_linear_float[256] = { 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, @@ -1594,6 +1636,8 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void { int x; int n; + int num_nonalpha; + stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) { @@ -1603,19 +1647,36 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void float alpha = encode_buffer[pixel_index + alpha_channel]; float reciprocal_alpha = alpha ? 1.0f / alpha : 0; + + // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb 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. // Because we only add it for integer types, it will automatically be discarded on integer - // conversion. + // conversion, so we don't need to subtract it back out (which would be problematic for + // numeric precision reasons). } } + // build a table of all channels that need colorspace correction, so + // we don't perform colorspace correction on channels that don't need it. + for (x=0, num_nonalpha=0; x < channels; ++x) + if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + nonalpha[num_nonalpha++] = x; + #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) + #ifdef STBIR__SATURATE_INT + #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * 255 )) + #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * 65535)) + #else + #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * 255 ) + #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * 65535) + #endif + switch (decode) { case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): @@ -1626,7 +1687,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) STBIR__ROUND_INT(stbir__saturate(encode_buffer[index]) * 255); + ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); } } break; @@ -1636,14 +1697,14 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void { int pixel_index = x*channels; - for (n = 0; n < channels; n++) + for (n = 0; n < num_nonalpha; n++) { - int index = pixel_index + n; + int index = pixel_index + nonalpha[n]; ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); } - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned char*)output_buffer)[pixel_index + alpha_channel] = (unsigned char) STBIR__ROUND_INT(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 255); + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); } break; @@ -1655,7 +1716,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)STBIR__ROUND_INT(stbir__saturate(encode_buffer[index]) * 65535); + ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); } } break; @@ -1665,14 +1726,14 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void { int pixel_index = x*channels; - for (n = 0; n < channels; n++) + for (n = 0; n < num_nonalpha; n++) { - int index = pixel_index + n; + int index = pixel_index + nonalpha[n]; ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535); } if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = (unsigned short)STBIR__ROUND_INT(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 65535); + ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); } break; @@ -1695,9 +1756,9 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void { int pixel_index = x*channels; - for (n = 0; n < channels; n++) + for (n = 0; n < num_nonalpha; n++) { - int index = pixel_index + n; + int index = pixel_index + nonalpha[n]; ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295); } @@ -1724,9 +1785,9 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void { int pixel_index = x*channels; - for (n = 0; n < channels; n++) + for (n = 0; n < num_nonalpha; n++) { - int index = pixel_index + n; + int index = pixel_index + nonalpha[n]; ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); } @@ -2186,8 +2247,9 @@ static int stbir__resize_allocated(stbir__info *info, #endif STBIR_ASSERT(info->channels >= 0); + STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); - if (info->channels < 0) + if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) 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 db38e40..f6f7d0b 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -148,25 +148,26 @@ static void performance(int argc, char **argv) int w, h, count; int n, i; int out_w, out_h, srgb=1; - input_pixels = stbi_load(argv[1], &w, &h, &n, 0); - #if 1 + input_pixels = stbi_load(argv[1], &w, &h, &n, 4); + n=4; + #if 0 out_w = w/4; out_h = h/4; count=100; // 1 #elif 0 out_w = w*2; out_h = h/4; count=20; // 2 // note this is structured pessimily, would be much faster to downsample vertically first #elif 0 out_w = w/4; out_h = h*2; count=50; // 3 #elif 0 - out_w = w*3; out_h = h*3; count=5; srgb=0; // 4 + out_w = w*3; out_h = h*3; count=2; srgb=0; // 4 #else - out_w = w*3; out_h = h*3; count=3; // 5 // this is dominated by linear->sRGB conversion + out_w = w*3; out_h = h*3; count=1; // 5 // this is dominated by linear->sRGB conversion #endif output_pixels = (unsigned char*) malloc(out_w*out_h*n); for (i=0; i < count; ++i) if (srgb) - stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, -1,0); + stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, 3,0); else - stbir_resize_uint8(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n); + stbir_resize(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, STBIR_TYPE_UINT8, n, 3, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_COLORSPACE_LINEAR, NULL); exit(0); } @@ -228,6 +229,7 @@ void test_format(const char* file, float width_percent, float height_percent, st int new_h = (int)(h * height_percent); T* T_data = (T*)malloc(w * h * n * sizeof(T)); + memset(T_data, 0, w*h*n*sizeof(T)); convert_image(input_data, T_data, w * h * n); T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); @@ -792,13 +794,15 @@ void test_suite(int argc, char **argv) _mkdir("test-output"); - test_32(); - if (argc > 1) barbara = argv[1]; else barbara = "barbara.png"; + test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB); + test_32(); + + // check what cases we need normalization for #if 1 { From 7602c99e779fd4bbed9e108d531308e9917427da Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Mon, 15 Sep 2014 11:29:40 -0700 Subject: [PATCH 162/168] Quick formatting fix. --- stb_image_resize.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 4d52b5a..5197129 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -17,12 +17,12 @@ stbir_resize_float(...) stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, output_pixels, out_w, out_h, 0, - num_channels , alpha_ chan , 0) + num_channels , alpha_chan , 0) stbir_resize_uint8_srgb_edgemode( input_pixels , in_w , in_h , 0, output_pixels, out_w, out_h, 0, - num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) - WRAP/REFLECT/ZERO + num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) + WRAP/REFLECT/ZERO FULL API See the "header file" section of the source for API documentation. From 600d80387e47840f91f42787603575dd4d1978db Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Mon, 15 Sep 2014 14:01:11 -0700 Subject: [PATCH 163/168] Make a guess at the value before doing the binary search, cuts the number of conditionals by half. Not as much gain as I hoped but something. --- stb_image_resize.h | 30 +++++++++++++++++++++--------- tests/resample_test.cpp | 6 ++---- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 5197129..7260479 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -677,20 +677,32 @@ static float stbir__linear_to_srgb(float f) return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; } +// Used as a starting point to save time in the binary search in stbir__linear_to_srgb_uchar. +static unsigned char stbr__linear_uchar_to_srgb_uchar[] = { + 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, + 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, + 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, + 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, + 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, + 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, + 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, + 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, + 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, + 251, 251, 252, 252, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 +}; + static unsigned char stbir__linear_to_srgb_uchar(float f) { int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp - int v = 0; + int v = stbr__linear_uchar_to_srgb_uchar[(int)(f * 255)]; // Make a guess at the value with a table. int i; - 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; + // Refine the guess with a short binary search. + 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; } diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index f6f7d0b..9353f4c 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -799,10 +799,6 @@ void test_suite(int argc, char **argv) else barbara = "barbara.png"; - test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB); - test_32(); - - // check what cases we need normalization for #if 1 { @@ -877,6 +873,8 @@ void test_suite(int argc, char **argv) test_premul(); + test_32(); + // Some tests to make sure errors don't pop up with strange filter/dimension combinations. stbir_resize(image88, 8, 8, 0, output88, 4, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); stbir_resize(image88, 8, 8, 0, output88, 4, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_BOX, STBIR_COLORSPACE_SRGB, &g_context); From cbf5ebbd3591789cccd0be4c037e43c28e9af5ea Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 16 Sep 2014 10:36:19 -0700 Subject: [PATCH 164/168] Install ryg's float -> uint8 sRGB conversion code, which is much faster. --- stb_image_resize.h | 81 +++++++++++++++++++++++++++++++++++++++++++++++-- tests/resample_test.cpp | 4 +-- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 7260479..d080721 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -146,6 +146,13 @@ (For example, graphics hardware does not apply sRGB conversion to the alpha channel.) + IEEE FLOAT OPTIMIZATIONS + Some optimizations in this library make use of IEEE floating point + numbers. If you are on a system that uses non-IEEE floats then you can + disable these optimizations and use a somewhat slower fallback with + + #define STBIR_NON_IEEE_FLOAT + ADDITIONAL CONTRIBUTORS Sean Barrett: API design, optimizations @@ -677,8 +684,74 @@ static float stbir__linear_to_srgb(float f) return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; } +#ifndef STBIR_NON_IEEE_FLOAT + +// From https://gist.github.com/rygorous/2203834 + +typedef union +{ + stbir_uint32 u; + float f; + struct + { + stbir_uint32 Mantissa : 23; + stbir_uint32 Exponent : 8; + stbir_uint32 Sign : 1; + }; +} stbir__FP32; + +static const stbir_uint32 fp32_to_srgb8_tab3[64] = { + 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, + 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, + 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, + 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, + 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, + 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, + 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, + 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float in) +{ + static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps + static const stbir__FP32 lutthresh = { 0x3b800000 }; // 2^(-8) + static const stbir__FP32 linearsc = { 0x454c5d00 }; + static const stbir__FP32 float2int = { (127 + 23) << 23 }; + stbir__FP32 f; + + // Clamp to [0, 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > 0.0f)) // written this way to catch NaNs + in = 0.0f; + if (in > almostone.f) + in = almostone.f; + + // Check which region this value falls into + f.f = in; + if (f.f < lutthresh.f) // linear region + { + f.f *= linearsc.f; + f.f += float2int.f; // use "magic value" to get float->int with rounding. + return (stbir_uint8)(f.u & 255); + } + else // non-linear region + { + // Unpack bias, scale from table + stbir_uint32 tab = fp32_to_srgb8_tab3[(f.u >> 20) & 63]; + stbir_uint32 bias = (tab >> 16) << 9; + stbir_uint32 scale = tab & 0xffff; + + // Grab next-highest mantissa bits and perform linear interpolation + stbir_uint32 t = (f.u >> 12) & 0xff; + return (stbir_uint8)((bias + scale*t) >> 16); + } +} + +#else + // Used as a starting point to save time in the binary search in stbir__linear_to_srgb_uchar. -static unsigned char stbr__linear_uchar_to_srgb_uchar[] = { +static stbir_uint8 stbr__linear_uchar_to_srgb_uchar[] = { 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, @@ -692,7 +765,7 @@ static unsigned char stbr__linear_uchar_to_srgb_uchar[] = { 251, 251, 252, 252, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; -static unsigned char stbir__linear_to_srgb_uchar(float f) +static stbir_uint8 stbir__linear_to_srgb_uchar(float f) { int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp int v = stbr__linear_uchar_to_srgb_uchar[(int)(f * 255)]; // Make a guess at the value with a table. @@ -704,9 +777,11 @@ static unsigned char stbir__linear_to_srgb_uchar(float f) 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; + return (stbir_uint8) v; } +#endif + static float stbir__filter_trapezoid(float x, float scale) { float halfscale = scale / 2; diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 9353f4c..0d47530 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -259,7 +259,7 @@ void convert_image_float(const unsigned char* input, float* output, int length) void convert_image_float(const float* input, unsigned char* output, int length) { for (int i = 0; i < length; i++) - output[i] = (unsigned char)(input[i] * 255); + output[i] = (unsigned char)(stbir__saturate(input[i]) * 255); } void test_float(const char* file, float width_percent, float height_percent, stbir_datatype type, stbir_colorspace colorspace) @@ -857,7 +857,7 @@ void test_suite(int argc, char **argv) // 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) { + for (float y = -0.42f; y <= 0.42f; 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); From a12d3dedf02d2505a1138ec34c4931641e8caef6 Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Tue, 16 Sep 2014 10:50:41 -0700 Subject: [PATCH 165/168] Some todo notes. --- stb_image_resize.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stb_image_resize.h b/stb_image_resize.h index d080721..bfed013 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -166,6 +166,8 @@ TODO Don't decode all of the image data when only processing a partial tile + Don't use full-width decode buffers when only processing a partial tile + When doing huge upscaling, break scanlines into smaller blocks that fit in L1 cache Installable filters? Resize that respects alpha test coverage (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: From 80246734611209bfd4334f079c01fc2b0e3f20ac Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Wed, 17 Sep 2014 06:07:16 -0700 Subject: [PATCH 166/168] switch srgb convert to second ryg method, which is a bit faster (8% total speedup on upsampling test); remove extra table in slowpath --- stb_image_resize.h | 192 +++++++++++++++++++++++------------------------------ 1 file changed, 84 insertions(+), 108 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index bfed013..203f13b 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -22,7 +22,7 @@ input_pixels , in_w , in_h , 0, output_pixels, out_w, out_h, 0, num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) - WRAP/REFLECT/ZERO + // WRAP/REFLECT/ZERO FULL API See the "header file" section of the source for API documentation. @@ -629,11 +629,81 @@ static float stbir__srgb_uchar_to_linear_float[256] = { 0.982251f, 0.991102f, 1.0f }; +static float stbir__srgb_to_linear(float f) +{ + if (f <= 0.04045f) + return f / 12.92f; + else + return (float)pow((f + 0.055f) / 1.055f, 2.4f); +} + +static float stbir__linear_to_srgb(float f) +{ + if (f <= 0.0031308f) + return f * 12.92f; + else + return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; +} + +#ifndef STBIR_NON_IEEE_FLOAT +// From https://gist.github.com/rygorous/2203834 + +typedef union +{ + stbir_uint32 u; + float f; + struct + { + stbir_uint32 Mantissa : 23; + stbir_uint32 Exponent : 8; + stbir_uint32 Sign : 1; + }; +} stbir__FP32; + +static const stbir_uint32 fp32_to_srgb8_tab4[104] = { + 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, + 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, + 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, + 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, + 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, + 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, + 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, + 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, + 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, + 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, + 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, + 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, + 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float in) +{ + static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps + static const stbir__FP32 minval = { (127-13) << 23 }; + stbir_uint32 tab,bias,scale,t; + stbir__FP32 f; + + // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > minval.f)) // written this way to catch NaNs + in = minval.f; + if (in > almostone.f) + in = almostone.f; + + // Do the table lookup and unpack bias, scale + f.f = in; + tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; + bias = (tab >> 16) << 9; + scale = tab & 0xffff; + + // Grab next-highest mantissa bits and perform linear interpolation + t = (f.u >> 12) & 0xff; + return (unsigned char) ((bias + scale*t) >> 16); +} + +#else // sRGB transition values, scaled by 1<<28 -// note that if you only scaled by 1<<16, all the values would be 4K smaller, -// so [1] would be ~10, and so that would have around 5% error (10 +- 0.5) -// at the boundary between uint8 0 and 1. This also means that a 64K-entry table -// would have the same 5% error there. static int stbir__srgb_offset_to_linear_scaled[256] = { 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, @@ -670,118 +740,24 @@ static int stbir__srgb_offset_to_linear_scaled[256] = 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, }; -static float stbir__srgb_to_linear(float f) -{ - if (f <= 0.04045f) - return f / 12.92f; - else - return (float)pow((f + 0.055f) / 1.055f, 2.4f); -} - -static float stbir__linear_to_srgb(float f) -{ - if (f <= 0.0031308f) - return f * 12.92f; - else - return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; -} - -#ifndef STBIR_NON_IEEE_FLOAT - -// From https://gist.github.com/rygorous/2203834 - -typedef union -{ - stbir_uint32 u; - float f; - struct - { - stbir_uint32 Mantissa : 23; - stbir_uint32 Exponent : 8; - stbir_uint32 Sign : 1; - }; -} stbir__FP32; - -static const stbir_uint32 fp32_to_srgb8_tab3[64] = { - 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, - 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, - 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, - 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, - 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, - 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, - 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, - 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, -}; - -static stbir_uint8 stbir__linear_to_srgb_uchar(float in) -{ - static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps - static const stbir__FP32 lutthresh = { 0x3b800000 }; // 2^(-8) - static const stbir__FP32 linearsc = { 0x454c5d00 }; - static const stbir__FP32 float2int = { (127 + 23) << 23 }; - stbir__FP32 f; - - // Clamp to [0, 1-eps]; these two values map to 0 and 1, respectively. - // The tests are carefully written so that NaNs map to 0, same as in the reference - // implementation. - if (!(in > 0.0f)) // written this way to catch NaNs - in = 0.0f; - if (in > almostone.f) - in = almostone.f; - - // Check which region this value falls into - f.f = in; - if (f.f < lutthresh.f) // linear region - { - f.f *= linearsc.f; - f.f += float2int.f; // use "magic value" to get float->int with rounding. - return (stbir_uint8)(f.u & 255); - } - else // non-linear region - { - // Unpack bias, scale from table - stbir_uint32 tab = fp32_to_srgb8_tab3[(f.u >> 20) & 63]; - stbir_uint32 bias = (tab >> 16) << 9; - stbir_uint32 scale = tab & 0xffff; - - // Grab next-highest mantissa bits and perform linear interpolation - stbir_uint32 t = (f.u >> 12) & 0xff; - return (stbir_uint8)((bias + scale*t) >> 16); - } -} - -#else - -// Used as a starting point to save time in the binary search in stbir__linear_to_srgb_uchar. -static stbir_uint8 stbr__linear_uchar_to_srgb_uchar[] = { - 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, - 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, - 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, - 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, - 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, - 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, - 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, - 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, - 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, - 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, - 251, 251, 252, 252, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 -}; - static stbir_uint8 stbir__linear_to_srgb_uchar(float f) { int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp - int v = stbr__linear_uchar_to_srgb_uchar[(int)(f * 255)]; // Make a guess at the value with a table. + int v = 0; int i; // Refine the guess with a short binary search. - 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; + 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; return (stbir_uint8) v; } - #endif static float stbir__filter_trapezoid(float x, float scale) From 0fc13e997b5d34da16cb055b4cb18cd4ff527da7 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Wed, 17 Sep 2014 06:24:40 -0700 Subject: [PATCH 167/168] optimization attempts, no meaningful changes --- stb_image_resize.h | 21 ++++++++++++--------- tests/resample_test.cpp | 13 ++++++------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index 203f13b..ea03fc6 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -1903,6 +1903,9 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in memset(encode_buffer, 0, output_w * sizeof(float) * channels); + // I tried reblocking this for better cache usage of encode_buffer + // (using x_outer, k, x_inner), but it lost speed. -- stb + coefficient_counter = 0; switch (channels) { case 1: @@ -1911,9 +1914,9 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int coefficient_index = coefficient_counter++; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; x++) + for (x = 0; x < output_w; ++x) { - int in_pixel_index = x * channels; + int in_pixel_index = x * 1; encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; } } @@ -1924,9 +1927,9 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int coefficient_index = coefficient_counter++; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; x++) + for (x = 0; x < output_w; ++x) { - int in_pixel_index = x * channels; + int in_pixel_index = x * 2; encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; } @@ -1938,9 +1941,9 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int coefficient_index = coefficient_counter++; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; x++) + for (x = 0; x < output_w; ++x) { - int in_pixel_index = x * channels; + int in_pixel_index = x * 3; encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; @@ -1953,9 +1956,9 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int coefficient_index = coefficient_counter++; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; x++) + for (x = 0; x < output_w; ++x) { - int in_pixel_index = x * channels; + int in_pixel_index = x * 4; encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; @@ -1969,7 +1972,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in int coefficient_index = coefficient_counter++; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; x++) + for (x = 0; x < output_w; ++x) { int in_pixel_index = x * channels; int c; diff --git a/tests/resample_test.cpp b/tests/resample_test.cpp index 0d47530..c42df7b 100644 --- a/tests/resample_test.cpp +++ b/tests/resample_test.cpp @@ -132,8 +132,8 @@ static void resizer(int argc, char **argv) int n; int out_w, out_h; input_pixels = stbi_load(argv[1], &w, &h, &n, 0); - out_w = w/4; - out_h = h/4; + out_w = w*3; + out_h = h*3; output_pixels = (unsigned char*) malloc(out_w*out_h*n); //stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, -1,0); stbir_resize_uint8(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n); @@ -148,8 +148,7 @@ static void performance(int argc, char **argv) int w, h, count; int n, i; int out_w, out_h, srgb=1; - input_pixels = stbi_load(argv[1], &w, &h, &n, 4); - n=4; + input_pixels = stbi_load(argv[1], &w, &h, &n, 0); #if 0 out_w = w/4; out_h = h/4; count=100; // 1 #elif 0 @@ -159,15 +158,15 @@ static void performance(int argc, char **argv) #elif 0 out_w = w*3; out_h = h*3; count=2; srgb=0; // 4 #else - out_w = w*3; out_h = h*3; count=1; // 5 // this is dominated by linear->sRGB conversion + out_w = w*3; out_h = h*3; count=2; // 5 // this is dominated by linear->sRGB conversion #endif output_pixels = (unsigned char*) malloc(out_w*out_h*n); for (i=0; i < count; ++i) if (srgb) - stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, 3,0); + stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n,-1,0); else - stbir_resize(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, STBIR_TYPE_UINT8, n, 3, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_COLORSPACE_LINEAR, NULL); + stbir_resize(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, STBIR_TYPE_UINT8, n,-1, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_COLORSPACE_LINEAR, NULL); exit(0); } From 7c0ae19a42527e4011b6a59a03e7443f99d3f909 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Wed, 17 Sep 2014 07:10:21 -0700 Subject: [PATCH 168/168] documentation, release data --- stb_image_resize.h | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/stb_image_resize.h b/stb_image_resize.h index ea03fc6..ff057b7 100644 --- a/stb_image_resize.h +++ b/stb_image_resize.h @@ -5,6 +5,7 @@ Written with emphasis on usability, portability, and efficiency. (No SIMD or threads, so it be easily outperformed by libs that use those.) Only scaling and translation is supported, no rotations or shears. + Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation. COMPILING & LINKING In one C/C++ file that #includes this file, do this: @@ -29,6 +30,15 @@ ADDITIONAL DOCUMENTATION + SRGB & FLOATING POINT REPRESENTATION + Some srgb-related code in this library relies on floats being 32-bit + IEEE floating point, and relies on a specific bitpacking order of C + bitfields. If you are on a system that uses non-IEEE floats or packs + C bitfields in the opposite order, then you can use a slower fallback + codepath by defining STBIR_NON_IEEE_FLOAT. (We didn't make this choice + idly; using mostly-but-not-100%-portable-code for this is a massive + speedup, especially upsampling where colorspace conversion dominates.) + MEMORY ALLOCATION The resize functions here perform a single memory allocation using malloc. To control the memory allocation, before the #include that @@ -146,18 +156,11 @@ (For example, graphics hardware does not apply sRGB conversion to the alpha channel.) - IEEE FLOAT OPTIMIZATIONS - Some optimizations in this library make use of IEEE floating point - numbers. If you are on a system that uses non-IEEE floats then you can - disable these optimizations and use a somewhat slower fallback with - - #define STBIR_NON_IEEE_FLOAT - ADDITIONAL CONTRIBUTORS Sean Barrett: API design, optimizations REVISIONS - 0.90 (2014-??-??) first released version + 0.90 (2014-09-17) first released version LICENSE This software is in the public domain. Where that dedication is not @@ -167,7 +170,7 @@ TODO Don't decode all of the image data when only processing a partial tile Don't use full-width decode buffers when only processing a partial tile - When doing huge upscaling, break scanlines into smaller blocks that fit in L1 cache + When processing wide images, break processing into tiles so data fits in L1 cache Installable filters? Resize that respects alpha test coverage (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: