Specify a channel as having premultiplied alpha and use it to un-premultiply all other channels before resampling.

pull/32/head
Jorge Rodriguez 2014-07-31 17:20:00 -07:00
parent a32fa8b4df
commit 8063ea0952
2 changed files with 96 additions and 15 deletions

View File

@ -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);

View File

@ -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);