don't encode alpha channel twice when alpha is different colorspace from other channels
This commit is contained in:
parent
bdbf1e0ef4
commit
dd28033b34
@ -3,8 +3,8 @@
|
|||||||
http://github.com/nothings/stb
|
http://github.com/nothings/stb
|
||||||
|
|
||||||
Written with emphasis on usability, portability, and efficiency. (No
|
Written with emphasis on usability, portability, and efficiency. (No
|
||||||
SIMD or threads, so it will not be the fastest implementation around.)
|
SIMD or threads, so it be easily outperformed by libs that use those.)
|
||||||
Only scaling is supported, no rotations or translations.
|
Only scaling and translation is supported, no rotations or shears.
|
||||||
|
|
||||||
COMPILING & LINKING
|
COMPILING & LINKING
|
||||||
In one C/C++ file that #includes this file, do this:
|
In one C/C++ file that #includes this file, do this:
|
||||||
@ -43,6 +43,11 @@
|
|||||||
ASSERT
|
ASSERT
|
||||||
Define STBIR_ASSERT(boolval) to override assert() and not use assert.h
|
Define STBIR_ASSERT(boolval) to override assert() and not use assert.h
|
||||||
|
|
||||||
|
OPTIMIZATION
|
||||||
|
Define STBIR_SATURATE_INT to compute clamp values in-range using
|
||||||
|
integer operations instead of float operations. This may be faster
|
||||||
|
on some platforms.
|
||||||
|
|
||||||
DEFAULT FILTERS
|
DEFAULT FILTERS
|
||||||
For functions which don't provide explicit control over what filters
|
For functions which don't provide explicit control over what filters
|
||||||
to use, you can change the compile-time defaults with
|
to use, you can change the compile-time defaults with
|
||||||
@ -78,6 +83,10 @@
|
|||||||
printf("Progress: %f%%\n", progress*100);
|
printf("Progress: %f%%\n", progress*100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MAX CHANNELS
|
||||||
|
If your image has more than 64 channels, define STBIR_MAX_CHANNELS
|
||||||
|
to the max you'll have.
|
||||||
|
|
||||||
ALPHA CHANNEL
|
ALPHA CHANNEL
|
||||||
Most of the resizing functions provide the ability to control how
|
Most of the resizing functions provide the ability to control how
|
||||||
the alpha channel of an image is processed. The important things
|
the alpha channel of an image is processed. The important things
|
||||||
@ -419,6 +428,15 @@ typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]
|
|||||||
#define STBIR_PROGRESS_REPORT(float_0_to_1)
|
#define STBIR_PROGRESS_REPORT(float_0_to_1)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef STBIR_MAX_CHANNELS
|
||||||
|
#define STBIR_MAX_CHANNELS 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STBIR_MAX_CHANNELS > 65536
|
||||||
|
#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536."
|
||||||
|
// because we store the indices in 16-bit variables
|
||||||
|
#endif
|
||||||
|
|
||||||
// This value is added to alpha just before premultiplication to avoid
|
// This value is added to alpha just before premultiplication to avoid
|
||||||
// zeroing out color values. It is equivalent to 2^-80. If you don't want
|
// zeroing out color values. It is equivalent to 2^-80. If you don't want
|
||||||
// that behavior (it may interfere if you have floating point images with
|
// that behavior (it may interfere if you have floating point images with
|
||||||
@ -551,6 +569,30 @@ static stbir__inline float stbir__saturate(float x)
|
|||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef STBIR_SATURATE_INT
|
||||||
|
static stbir__inline stbir_uint8 stbir__saturate8(int x)
|
||||||
|
{
|
||||||
|
if ((unsigned int) x <= 255)
|
||||||
|
return x;
|
||||||
|
|
||||||
|
if (x < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
static stbir__inline stbir_uint16 stbir__saturate16(int x)
|
||||||
|
{
|
||||||
|
if ((unsigned int) x <= 65535)
|
||||||
|
return x;
|
||||||
|
|
||||||
|
if (x < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 65535;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static float stbir__srgb_uchar_to_linear_float[256] = {
|
static float stbir__srgb_uchar_to_linear_float[256] = {
|
||||||
0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f,
|
0.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.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f,
|
||||||
@ -1594,6 +1636,8 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
|
|||||||
{
|
{
|
||||||
int x;
|
int x;
|
||||||
int n;
|
int n;
|
||||||
|
int num_nonalpha;
|
||||||
|
stbir_uint16 nonalpha[STBIR_MAX_CHANNELS];
|
||||||
|
|
||||||
if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED))
|
if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED))
|
||||||
{
|
{
|
||||||
@ -1603,19 +1647,36 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
|
|||||||
|
|
||||||
float alpha = encode_buffer[pixel_index + alpha_channel];
|
float alpha = encode_buffer[pixel_index + alpha_channel];
|
||||||
float reciprocal_alpha = alpha ? 1.0f / alpha : 0;
|
float reciprocal_alpha = alpha ? 1.0f / alpha : 0;
|
||||||
|
|
||||||
|
// unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb
|
||||||
for (n = 0; n < channels; n++)
|
for (n = 0; n < channels; n++)
|
||||||
if (n != alpha_channel)
|
if (n != alpha_channel)
|
||||||
encode_buffer[pixel_index + n] *= reciprocal_alpha;
|
encode_buffer[pixel_index + n] *= reciprocal_alpha;
|
||||||
|
|
||||||
// We added in a small epsilon to prevent the color channel from being deleted with zero alpha.
|
// We added in a small epsilon to prevent the color channel from being deleted with zero alpha.
|
||||||
// Because we only add it for integer types, it will automatically be discarded on integer
|
// Because we only add it for integer types, it will automatically be discarded on integer
|
||||||
// conversion.
|
// conversion, so we don't need to subtract it back out (which would be problematic for
|
||||||
|
// numeric precision reasons).
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// build a table of all channels that need colorspace correction, so
|
||||||
|
// we don't perform colorspace correction on channels that don't need it.
|
||||||
|
for (x=0, num_nonalpha=0; x < channels; ++x)
|
||||||
|
if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE))
|
||||||
|
nonalpha[num_nonalpha++] = x;
|
||||||
|
|
||||||
#define STBIR__ROUND_INT(f) ((int) ((f)+0.5))
|
#define STBIR__ROUND_INT(f) ((int) ((f)+0.5))
|
||||||
#define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5))
|
#define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5))
|
||||||
|
|
||||||
|
#ifdef STBIR__SATURATE_INT
|
||||||
|
#define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * 255 ))
|
||||||
|
#define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * 65535))
|
||||||
|
#else
|
||||||
|
#define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * 255 )
|
||||||
|
#define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * 65535)
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (decode)
|
switch (decode)
|
||||||
{
|
{
|
||||||
case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR):
|
case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR):
|
||||||
@ -1626,7 +1687,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
|
|||||||
for (n = 0; n < channels; n++)
|
for (n = 0; n < channels; n++)
|
||||||
{
|
{
|
||||||
int index = pixel_index + n;
|
int index = pixel_index + n;
|
||||||
((unsigned char*)output_buffer)[index] = (unsigned char) STBIR__ROUND_INT(stbir__saturate(encode_buffer[index]) * 255);
|
((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1636,14 +1697,14 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
|
|||||||
{
|
{
|
||||||
int pixel_index = x*channels;
|
int pixel_index = x*channels;
|
||||||
|
|
||||||
for (n = 0; n < channels; n++)
|
for (n = 0; n < num_nonalpha; n++)
|
||||||
{
|
{
|
||||||
int index = pixel_index + n;
|
int index = pixel_index + nonalpha[n];
|
||||||
((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]);
|
((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
|
if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE))
|
||||||
((unsigned char*)output_buffer)[pixel_index + alpha_channel] = (unsigned char) STBIR__ROUND_INT(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 255);
|
((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1655,7 +1716,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
|
|||||||
for (n = 0; n < channels; n++)
|
for (n = 0; n < channels; n++)
|
||||||
{
|
{
|
||||||
int index = pixel_index + n;
|
int index = pixel_index + n;
|
||||||
((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__saturate(encode_buffer[index]) * 65535);
|
((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1665,14 +1726,14 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
|
|||||||
{
|
{
|
||||||
int pixel_index = x*channels;
|
int pixel_index = x*channels;
|
||||||
|
|
||||||
for (n = 0; n < channels; n++)
|
for (n = 0; n < num_nonalpha; n++)
|
||||||
{
|
{
|
||||||
int index = pixel_index + n;
|
int index = pixel_index + nonalpha[n];
|
||||||
((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535);
|
((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
|
if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE))
|
||||||
((unsigned short*)output_buffer)[pixel_index + alpha_channel] = (unsigned short)STBIR__ROUND_INT(stbir__saturate(encode_buffer[pixel_index + alpha_channel]) * 65535);
|
((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -1695,9 +1756,9 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
|
|||||||
{
|
{
|
||||||
int pixel_index = x*channels;
|
int pixel_index = x*channels;
|
||||||
|
|
||||||
for (n = 0; n < channels; n++)
|
for (n = 0; n < num_nonalpha; n++)
|
||||||
{
|
{
|
||||||
int index = pixel_index + n;
|
int index = pixel_index + nonalpha[n];
|
||||||
((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295);
|
((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1724,9 +1785,9 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
|
|||||||
{
|
{
|
||||||
int pixel_index = x*channels;
|
int pixel_index = x*channels;
|
||||||
|
|
||||||
for (n = 0; n < channels; n++)
|
for (n = 0; n < num_nonalpha; n++)
|
||||||
{
|
{
|
||||||
int index = pixel_index + n;
|
int index = pixel_index + nonalpha[n];
|
||||||
((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]);
|
((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2186,8 +2247,9 @@ static int stbir__resize_allocated(stbir__info *info,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
STBIR_ASSERT(info->channels >= 0);
|
STBIR_ASSERT(info->channels >= 0);
|
||||||
|
STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS);
|
||||||
|
|
||||||
if (info->channels < 0)
|
if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
|
STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table));
|
||||||
|
@ -148,25 +148,26 @@ static void performance(int argc, char **argv)
|
|||||||
int w, h, count;
|
int w, h, count;
|
||||||
int n, i;
|
int n, i;
|
||||||
int out_w, out_h, srgb=1;
|
int out_w, out_h, srgb=1;
|
||||||
input_pixels = stbi_load(argv[1], &w, &h, &n, 0);
|
input_pixels = stbi_load(argv[1], &w, &h, &n, 4);
|
||||||
#if 1
|
n=4;
|
||||||
|
#if 0
|
||||||
out_w = w/4; out_h = h/4; count=100; // 1
|
out_w = w/4; out_h = h/4; count=100; // 1
|
||||||
#elif 0
|
#elif 0
|
||||||
out_w = w*2; out_h = h/4; count=20; // 2 // note this is structured pessimily, would be much faster to downsample vertically first
|
out_w = w*2; out_h = h/4; count=20; // 2 // note this is structured pessimily, would be much faster to downsample vertically first
|
||||||
#elif 0
|
#elif 0
|
||||||
out_w = w/4; out_h = h*2; count=50; // 3
|
out_w = w/4; out_h = h*2; count=50; // 3
|
||||||
#elif 0
|
#elif 0
|
||||||
out_w = w*3; out_h = h*3; count=5; srgb=0; // 4
|
out_w = w*3; out_h = h*3; count=2; srgb=0; // 4
|
||||||
#else
|
#else
|
||||||
out_w = w*3; out_h = h*3; count=3; // 5 // this is dominated by linear->sRGB conversion
|
out_w = w*3; out_h = h*3; count=1; // 5 // this is dominated by linear->sRGB conversion
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
output_pixels = (unsigned char*) malloc(out_w*out_h*n);
|
output_pixels = (unsigned char*) malloc(out_w*out_h*n);
|
||||||
for (i=0; i < count; ++i)
|
for (i=0; i < count; ++i)
|
||||||
if (srgb)
|
if (srgb)
|
||||||
stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, -1,0);
|
stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, 3,0);
|
||||||
else
|
else
|
||||||
stbir_resize_uint8(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n);
|
stbir_resize(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, STBIR_TYPE_UINT8, n, 3, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_COLORSPACE_LINEAR, NULL);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,6 +229,7 @@ void test_format(const char* file, float width_percent, float height_percent, st
|
|||||||
int new_h = (int)(h * height_percent);
|
int new_h = (int)(h * height_percent);
|
||||||
|
|
||||||
T* T_data = (T*)malloc(w * h * n * sizeof(T));
|
T* T_data = (T*)malloc(w * h * n * sizeof(T));
|
||||||
|
memset(T_data, 0, w*h*n*sizeof(T));
|
||||||
convert_image<unsigned char, T>(input_data, T_data, w * h * n);
|
convert_image<unsigned char, T>(input_data, T_data, w * h * n);
|
||||||
|
|
||||||
T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T));
|
T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T));
|
||||||
@ -792,13 +794,15 @@ void test_suite(int argc, char **argv)
|
|||||||
|
|
||||||
_mkdir("test-output");
|
_mkdir("test-output");
|
||||||
|
|
||||||
test_32();
|
|
||||||
|
|
||||||
if (argc > 1)
|
if (argc > 1)
|
||||||
barbara = argv[1];
|
barbara = argv[1];
|
||||||
else
|
else
|
||||||
barbara = "barbara.png";
|
barbara = "barbara.png";
|
||||||
|
|
||||||
|
test_format<unsigned short>(barbara, 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB);
|
||||||
|
test_32();
|
||||||
|
|
||||||
|
|
||||||
// check what cases we need normalization for
|
// check what cases we need normalization for
|
||||||
#if 1
|
#if 1
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user