Support for filters with larger support. Initial support for edge behavior.

pull/32/head
Jorge Rodriguez 2014-07-26 11:51:02 -07:00
parent 62ff271c7a
commit 7abd4ccf34
3 changed files with 154 additions and 114 deletions

View File

@ -196,5 +196,6 @@ Wish list:
s0, t0, s1, t1 vs scale_x, scale_y, offset_x, offset_y - What's the best interface? 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 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) 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

View File

@ -28,6 +28,7 @@ Initial implementation by Jorge L Rodriguez
typedef enum typedef enum
{ {
STBR_FILTER_NEAREST = 1, STBR_FILTER_NEAREST = 1,
STBR_FILTER_LINEAR = 2,
} stbr_filter; } stbr_filter;
typedef enum 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, 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, 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, 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); void* tempmem, stbr_size_t tempmem_size_in_bytes);
@ -175,22 +176,20 @@ typedef struct
int channels; int channels;
stbr_type type; stbr_type type;
stbr_filter filter; stbr_filter filter;
stbr_edge edge;
int total_coefficients;
int kernel_texel_width;
int total_horizontal_contributors;
stbr__contributors* horizontal_contributors; stbr__contributors* horizontal_contributors;
float* horizontal_coefficients; float* horizontal_coefficients;
stbr__contributors vertical_contributors; stbr__contributors vertical_contributors;
float* vertical_coefficients; float* vertical_coefficients;
int decode_buffer_texels;
float* decode_buffer; float* decode_buffer;
float* horizontal_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_first_scanline;
int ring_buffer_last_scanline; int ring_buffer_last_scanline;
int ring_buffer_begin_index; int ring_buffer_begin_index;
@ -219,9 +218,20 @@ static float stbr__filter_nearest(float x)
return 0; 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[] = { static stbr__filter_info stbr__filter_info_table[] = {
{ NULL, 0.0f }, { NULL, 0.0f },
{ stbr__filter_nearest, 0.5f }, { 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) 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); 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) 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_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]; return &stbr_info->horizontal_contributors[n];
} }
stbr_inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, int c) stbr_inline static float* stbr__get_coefficient(stbr__info* stbr_info, int n, int c)
{ {
return &stbr_info->horizontal_coefficients[stbr_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? // 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; float out_texel_influence_upperbound = in_texel_influence_upperbound * scale_ratio;
*out_center_of_in = in_texel_center * 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_lowerbound + 0.5));
*out_first_texel = (int)(floor(out_texel_influence_upperbound - 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; int i;
float total_filter = 0; float total_filter = 0;
float filter_scale; float filter_scale;
stbr_filter filter = stbr_info->filter; stbr_filter filter = stbr_info->filter;
STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr_info->kernel_texel_width); STBR_DEBUG_ASSERT(in_last_texel - in_first_texel <= stbr__get_filter_texel_width(filter));
STBR_DEBUG_ASSERT(in_first_texel >= 0); 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 < stbr_info->total_horizontal_contributors);
contributor->n0 = in_first_texel; contributor->n0 = in_first_texel;
contributor->n1 = in_last_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; 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; int i;
stbr_filter filter = stbr_info->filter; stbr_filter filter = stbr_info->filter;
STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr_info->kernel_texel_width); STBR_DEBUG_ASSERT(out_last_texel - out_first_texel <= stbr__get_filter_texel_width(filter));
STBR_DEBUG_ASSERT(out_first_texel >= 0); 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 < stbr_info->total_horizontal_contributors);
contributor->n0 = out_first_texel; contributor->n0 = out_first_texel;
contributor->n1 = out_last_texel; contributor->n1 = out_last_texel;
@ -356,7 +395,7 @@ static void stbr__calculate_horizontal_filters(stbr__info* stbr_info)
int n; int n;
float scale_ratio = (float)stbr_info->output_w / stbr_info->input_w; 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)) 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_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 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 float out_center_of_in; // Center of the current out texel in the in texel space
int out_first_texel, out_last_texel; 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); // The 0 index of the decode buffer starts after the margin. This makes
STBR_DEBUG_ASSERT(c >= 0 && c < stbr_info->channels); // 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[x * stbr_info->channels + c];
} }
static void stbr__decode_scanline(stbr__info* stbr_info, int n) 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_w = stbr_info->input_w;
int input_stride_bytes = stbr_info->input_stride_bytes; int input_stride_bytes = stbr_info->input_stride_bytes;
const void* input_data = stbr_info->input_data; const void* input_data = stbr_info->input_data;
float* decode_buffer = stbr_info->decode_buffer; float* decode_buffer = stbr__get_decode_buffer(stbr_info);
int in_buffer_row_index = n * input_stride_bytes; 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); 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++) for (c = 0; c < channels; c++)
{ decode_buffer[decode_texel_index + c] = ((float)((const unsigned char*)input_data)[input_texel_index + c]) / 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;
}
} }
} }
@ -440,12 +478,12 @@ static float* stbr__add_empty_ring_buffer_entry(stbr__info* stbr_info, int n)
} }
else 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); 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); 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); memset(ring_buffer, 0, stbr_info->ring_buffer_length_bytes);
stbr_info->ring_buffer_last_scanline = n; 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 x, k, c;
int output_w = stbr_info->output_w; 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; 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; stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors;
float* horizontal_coefficients = stbr_info->horizontal_coefficients; 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; int coefficient_counter = 0;
STBR_DEBUG_ASSERT(n1 >= n0); 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++) 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; int in_texel_index = k * channels;
float coefficient = horizontal_coefficients[coefficient_index]; float coefficient = horizontal_coefficients[coefficient_index];
if (!coefficient)
continue;
for (c = 0; c < channels; c++) for (c = 0; c < channels; c++)
{ {
output_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;
@ -496,34 +535,36 @@ static void stbr__resample_horizontal_downsample(stbr__info* stbr_info, int n, f
{ {
int x, k, c; int x, k, c;
int input_w = stbr_info->input_w; 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; 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; stbr__contributors* horizontal_contributors = stbr_info->horizontal_contributors;
float* horizontal_coefficients = stbr_info->horizontal_coefficients; 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)); 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 n0 = horizontal_contributors[x].n0;
int n1 = horizontal_contributors[x].n1; int n1 = horizontal_contributors[x].n1;
int in_texel_index = x * channels; int in_x = x - filter_texel_margin;
int coefficient_group_index = x * kernel_texel_width; int in_texel_index = in_x * channels;
int coefficient_counter = 0; int max_n = stbr__min(n1, output_w-1);
int coefficient_group = x*kernel_texel_width;
STBR_DEBUG_ASSERT(n1 >= n0); 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; int out_texel_index = k * channels;
float coefficient = horizontal_coefficients[coefficient_index]; float coefficient = horizontal_coefficients[coefficient_index];
if (!coefficient)
continue;
for (c = 0; c < channels; c++) for (c = 0; c < channels; c++)
{ {
output_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;
@ -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) 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. // Decode the nth scanline from the source image into the decode buffer.
stbr__decode_scanline(stbr_info, n); 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) 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. // Decode the nth scanline from the source image into the decode buffer.
stbr__decode_scanline(stbr_info, n); 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; stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors;
float* vertical_coefficients = stbr_info->vertical_coefficients; float* vertical_coefficients = stbr_info->vertical_coefficients;
int channels = stbr_info->channels; 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; void* output_data = stbr_info->output_data;
float* encode_buffer = stbr_info->encode_buffer; 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_begin_index = stbr_info->ring_buffer_begin_index;
int ring_buffer_first_scanline = stbr_info->ring_buffer_first_scanline; 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_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_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 n0 = vertical_contributors->n0;
int n1 = vertical_contributors->n1; 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* 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]; float coefficient = vertical_coefficients[coefficient_index];
if (!coefficient)
continue;
for (c = 0; c < channels; c++) for (c = 0; c < channels; c++)
encode_buffer[c] += ring_buffer_entry[in_texel_index + c] * coefficient; encode_buffer[c] += ring_buffer_entry[in_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 x, k, c;
int output_w = stbr_info->output_w; int output_w = stbr_info->output_w;
int output_h = stbr_info->output_h;
stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors; stbr__contributors* vertical_contributors = &stbr_info->vertical_contributors;
float* vertical_coefficients = stbr_info->vertical_coefficients; float* vertical_coefficients = stbr_info->vertical_coefficients;
int channels = stbr_info->channels; 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; void* output_data = stbr_info->output_data;
float* horizontal_buffer = stbr_info->horizontal_buffer; 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_begin_index = stbr_info->ring_buffer_begin_index;
int ring_buffer_first_scanline = stbr_info->ring_buffer_first_scanline; 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_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 n0 = vertical_contributors->n0;
int n1 = vertical_contributors->n1; 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++) for (x = 0; x < output_w; x++)
{ {
int in_texel_index = x * channels; int in_texel_index = x * channels;
int coefficient_counter = 0; int max_n = stbr__min(n1, output_h-1);
STBR_DEBUG_ASSERT(n1 >= n0); 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* 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]; float coefficient = vertical_coefficients[coefficient_index];
if (!coefficient)
continue;
for (c = 0; c < channels; c++) for (c = 0; c < channels; c++)
{ {
int index = in_texel_index + 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__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_last_scanline - in_first_scanline <= stbr__get_filter_texel_width(stbr_info->filter));
STBR_DEBUG_ASSERT(in_first_scanline >= 0); 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_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) if (stbr_info->ring_buffer_begin_index >= 0)
{ {
@ -728,7 +753,10 @@ static void stbr__buffer_loop_upsample(stbr__info* stbr_info)
break; break;
} }
else else
{
stbr_info->ring_buffer_first_scanline++; 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) if (stbr_info->ring_buffer_begin_index < 0)
stbr__decode_and_resample_upsample(stbr_info, in_first_scanline); 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); 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. // 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; void* output_data = stbr_info->output_data;
float* ring_buffer = stbr_info->ring_buffer; 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) 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); 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; for (x = 0; x < output_w; x++)
int ring_texel_index = texel_index; {
int output_texel_index = output_row + texel_index; int texel_index = x * channels;
for (c = 0; c < channels; c++) int ring_texel_index = texel_index;
((unsigned char*)output_data)[output_texel_index + c] = (unsigned char)(ring_buffer_entry[ring_texel_index + c] * 255); 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) 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; break;
} }
else else
{
stbr_info->ring_buffer_first_scanline++; 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__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_last_scanline - out_first_scanline <= stbr__get_filter_texel_width(stbr_info->filter));
STBR_DEBUG_ASSERT(out_first_scanline >= 0); STBR_DEBUG_ASSERT(out_first_scanline >= -stbr__get_filter_texel_margin(stbr_info->filter));
STBR_DEBUG_ASSERT(out_last_scanline < stbr_info->output_h); 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); 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. // Load in new ones.
if (stbr_info->ring_buffer_begin_index < 0) 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); 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. // 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, 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, 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) 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_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->channels = channels;
stbr_info->type = type; stbr_info->type = type;
stbr_info->filter = filter; stbr_info->filter = filter;
stbr_info->edge = edge;
stbr_info->total_coefficients = stbr__get_total_coefficients(filter, input_w, output_w); stbr_info->ring_buffer_length_bytes = output_w * channels * sizeof(float);
stbr_info->kernel_texel_width = stbr__get_filter_texel_width(filter); stbr_info->decode_buffer_texels = input_w + stbr__get_filter_texel_margin(filter) * 2;
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) #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_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->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_info->total_coefficients * sizeof(float), 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_info->kernel_texel_width * 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)) if (stbr__use_height_upsampling(stbr_info))
{ {
stbr_info->horizontal_buffer = NULL; stbr_info->horizontal_buffer = NULL;
stbr_info->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->decode_buffer, input_w * channels * sizeof(float), 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, output_w * channels * sizeof(float) * stbr_info->kernel_texel_width, 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 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->ring_buffer = STBR__NEXT_MEMPTR(stbr_info->horizontal_buffer, output_w * channels * sizeof(float), float);
stbr_info->encode_buffer = NULL; 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 #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 != 0);
STBR_ASSERT(filter < STBR_ARRAY_SIZE(stbr__filter_info_table)); 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 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 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) * 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 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);
int encode_buffer_size = channels * sizeof(float); int encode_buffer_size = channels * sizeof(float);

View File

@ -48,7 +48,7 @@ int main(int argc, char** argv)
int in_w = 512; int in_w = 512;
int in_h = 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); void* extra_memory = malloc(memory_required);
// Cut out the outside 64 pixels all around to test the stride. // 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_w + border <= w);
STBR_ASSERT(in_h + border <= h); 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); free(extra_memory);