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