From d54e74092ed5c2d43d161b5bda88b9b4784b624e Mon Sep 17 00:00:00 2001 From: Jorge Rodriguez Date: Mon, 21 Jul 2014 00:16:03 -0700 Subject: [PATCH 01/64] 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 02/64] 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 03/64] 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 04/64] 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 05/64] 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 06/64] 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 07/64] 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 08/64] 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 09/64] 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 10/64] 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 11/64] 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 12/64] 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 13/64] 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 14/64] 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 15/64] 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 16/64] 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 17/64] 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 18/64] 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 19/64] 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 20/64] 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 21/64] 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 22/64] 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 23/64] 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 24/64] 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 25/64] 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 26/64] 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 27/64] 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 28/64] 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 29/64] 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 30/64] 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 31/64] 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 32/64] 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 33/64] 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 34/64] 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 35/64] 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 36/64] 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 37/64] 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 38/64] 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 39/64] 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 40/64] 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 41/64] 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 42/64] 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 43/64] 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 44/64] 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 45/64] 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 46/64] 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 47/64] 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 48/64] 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 49/64] 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 50/64] 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 51/64] 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 52/64] 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 53/64] 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 54/64] 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 55/64] 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 56/64] 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 57/64] 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 58/64] 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 59/64] 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 60/64] 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 61/64] 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 62/64] 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 63/64] 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 64/64] 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);