Install ryg's float -> uint8 sRGB conversion code, which is much faster.

pull/44/head
Jorge Rodriguez 2014-09-16 10:36:19 -07:00
parent 600d80387e
commit cbf5ebbd35
2 changed files with 80 additions and 5 deletions

View File

@ -146,6 +146,13 @@
(For example, graphics hardware does not apply sRGB conversion
to the alpha channel.)
IEEE FLOAT OPTIMIZATIONS
Some optimizations in this library make use of IEEE floating point
numbers. If you are on a system that uses non-IEEE floats then you can
disable these optimizations and use a somewhat slower fallback with
#define STBIR_NON_IEEE_FLOAT
ADDITIONAL CONTRIBUTORS
Sean Barrett: API design, optimizations
@ -677,8 +684,74 @@ static float stbir__linear_to_srgb(float f)
return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f;
}
#ifndef STBIR_NON_IEEE_FLOAT
// From https://gist.github.com/rygorous/2203834
typedef union
{
stbir_uint32 u;
float f;
struct
{
stbir_uint32 Mantissa : 23;
stbir_uint32 Exponent : 8;
stbir_uint32 Sign : 1;
};
} stbir__FP32;
static const stbir_uint32 fp32_to_srgb8_tab3[64] = {
0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
};
static stbir_uint8 stbir__linear_to_srgb_uchar(float in)
{
static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps
static const stbir__FP32 lutthresh = { 0x3b800000 }; // 2^(-8)
static const stbir__FP32 linearsc = { 0x454c5d00 };
static const stbir__FP32 float2int = { (127 + 23) << 23 };
stbir__FP32 f;
// Clamp to [0, 1-eps]; these two values map to 0 and 1, respectively.
// The tests are carefully written so that NaNs map to 0, same as in the reference
// implementation.
if (!(in > 0.0f)) // written this way to catch NaNs
in = 0.0f;
if (in > almostone.f)
in = almostone.f;
// Check which region this value falls into
f.f = in;
if (f.f < lutthresh.f) // linear region
{
f.f *= linearsc.f;
f.f += float2int.f; // use "magic value" to get float->int with rounding.
return (stbir_uint8)(f.u & 255);
}
else // non-linear region
{
// Unpack bias, scale from table
stbir_uint32 tab = fp32_to_srgb8_tab3[(f.u >> 20) & 63];
stbir_uint32 bias = (tab >> 16) << 9;
stbir_uint32 scale = tab & 0xffff;
// Grab next-highest mantissa bits and perform linear interpolation
stbir_uint32 t = (f.u >> 12) & 0xff;
return (stbir_uint8)((bias + scale*t) >> 16);
}
}
#else
// Used as a starting point to save time in the binary search in stbir__linear_to_srgb_uchar.
static unsigned char stbr__linear_uchar_to_srgb_uchar[] = {
static stbir_uint8 stbr__linear_uchar_to_srgb_uchar[] = {
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,
@ -692,7 +765,7 @@ static unsigned char stbr__linear_uchar_to_srgb_uchar[] = {
251, 251, 252, 252, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
};
static unsigned char stbir__linear_to_srgb_uchar(float f)
static stbir_uint8 stbir__linear_to_srgb_uchar(float f)
{
int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp
int v = stbr__linear_uchar_to_srgb_uchar[(int)(f * 255)]; // Make a guess at the value with a table.
@ -704,9 +777,11 @@ static unsigned char stbir__linear_to_srgb_uchar(float f)
i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i;
return (unsigned char) v;
return (stbir_uint8) v;
}
#endif
static float stbir__filter_trapezoid(float x, float scale)
{
float halfscale = scale / 2;

View File

@ -259,7 +259,7 @@ void convert_image_float(const unsigned char* input, float* output, int length)
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);
output[i] = (unsigned char)(stbir__saturate(input[i]) * 255);
}
void test_float(const char* file, float width_percent, float height_percent, stbir_datatype type, stbir_colorspace colorspace)
@ -857,7 +857,7 @@ void test_suite(int argc, char **argv)
// new tests that hacky fix failed for - test that
// values adjacent to uint8 round to nearest uint8
for (i = 0; i < 256; i++) {
for (float y = -0.49f; y <= 0.491f; y += 0.01f) {
for (float y = -0.42f; y <= 0.42f; y += 0.01f) {
float f = stbir__srgb_to_linear((i+y) / 255.0f);
int n = stbir__linear_to_srgb_uchar(f);
STBIR_ASSERT(n == i);