sharp_yuv: use 14b fixed-point precision for gamma
Output is <.1% difference in size, randomly. Speed is 30-50% faster (-m 0 -sharp_yuv). It also gives the exact same output on ARM and x86, because floats are no longer used. Change-Id: Id0f0aa748cc4fc0b82bac1fc5ca954775a0a1b7c
This commit is contained in:
parent
b2db361ca6
commit
e03f0ec319
@ -170,29 +170,33 @@ typedef uint16_t fixed_y_t; // unsigned type with extra SFIX precision for W
|
||||
|
||||
#if defined(USE_GAMMA_COMPRESSION)
|
||||
|
||||
// float variant of gamma-correction
|
||||
// We use tables of different size and precision for the Rec709 / BT2020
|
||||
// transfer function.
|
||||
#define kGammaF (1./0.45)
|
||||
static float kGammaToLinearTabF[MAX_Y_T + 1]; // size scales with Y_FIX
|
||||
static float kLinearToGammaTabF[kGammaTabSize + 2];
|
||||
static volatile int kGammaTablesFOk = 0;
|
||||
static uint32_t kLinearToGammaTabS[kGammaTabSize + 2];
|
||||
#define GAMMA_TO_LINEAR_BITS 14
|
||||
static uint32_t kGammaToLinearTabS[MAX_Y_T + 1]; // size scales with Y_FIX
|
||||
static volatile int kGammaTablesSOk = 0;
|
||||
|
||||
static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) {
|
||||
if (!kGammaTablesFOk) {
|
||||
static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesS(void) {
|
||||
assert(2 * GAMMA_TO_LINEAR_BITS < 32); // we use uint32_t intermediate values
|
||||
if (!kGammaTablesSOk) {
|
||||
int v;
|
||||
const double norm = 1. / MAX_Y_T;
|
||||
const double scale = 1. / kGammaTabSize;
|
||||
const double a = 0.09929682680944;
|
||||
const double thresh = 0.018053968510807;
|
||||
const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;
|
||||
for (v = 0; v <= MAX_Y_T; ++v) {
|
||||
const double g = norm * v;
|
||||
double value;
|
||||
if (g <= thresh * 4.5) {
|
||||
kGammaToLinearTabF[v] = (float)(g / 4.5);
|
||||
value = g / 4.5;
|
||||
} else {
|
||||
const double a_rec = 1. / (1. + a);
|
||||
kGammaToLinearTabF[v] = (float)pow(a_rec * (g + a), kGammaF);
|
||||
value = pow(a_rec * (g + a), kGammaF);
|
||||
}
|
||||
kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);
|
||||
}
|
||||
for (v = 0; v <= kGammaTabSize; ++v) {
|
||||
const double g = scale * v;
|
||||
@ -202,37 +206,44 @@ static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) {
|
||||
} else {
|
||||
value = (1. + a) * pow(g, 1. / kGammaF) - a;
|
||||
}
|
||||
kLinearToGammaTabF[v] = (float)(MAX_Y_T * value);
|
||||
// we already incorporate the 1/2 rounding constant here
|
||||
kLinearToGammaTabS[v] =
|
||||
(uint32_t)(MAX_Y_T * value) + (1 << GAMMA_TO_LINEAR_BITS >> 1);
|
||||
}
|
||||
// to prevent small rounding errors to cause read-overflow:
|
||||
kLinearToGammaTabF[kGammaTabSize + 1] = kLinearToGammaTabF[kGammaTabSize];
|
||||
kGammaTablesFOk = 1;
|
||||
kLinearToGammaTabS[kGammaTabSize + 1] = kLinearToGammaTabS[kGammaTabSize];
|
||||
kGammaTablesSOk = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE float GammaToLinearF(int v) {
|
||||
return kGammaToLinearTabF[v];
|
||||
// return value has a fixed-point precision of GAMMA_TO_LINEAR_BITS
|
||||
static WEBP_INLINE uint32_t GammaToLinearS(int v) {
|
||||
return kGammaToLinearTabS[v];
|
||||
}
|
||||
|
||||
static WEBP_INLINE int LinearToGammaF(float value) {
|
||||
const float v = value * kGammaTabSize;
|
||||
const int tab_pos = (int)v;
|
||||
const float x = v - (float)tab_pos; // fractional part
|
||||
const float v0 = kLinearToGammaTabF[tab_pos + 0];
|
||||
const float v1 = kLinearToGammaTabF[tab_pos + 1];
|
||||
const float y = v1 * x + v0 * (1.f - x); // interpolate
|
||||
return (int)(y + .5);
|
||||
static WEBP_INLINE uint32_t LinearToGammaS(uint32_t value) {
|
||||
// 'value' is in GAMMA_TO_LINEAR_BITS fractional precision
|
||||
const uint32_t v = value * kGammaTabSize;
|
||||
const uint32_t tab_pos = v >> GAMMA_TO_LINEAR_BITS;
|
||||
// fractional part, in GAMMA_TO_LINEAR_BITS fixed-point precision
|
||||
const uint32_t x = v - (tab_pos << GAMMA_TO_LINEAR_BITS); // fractional part
|
||||
// v0 / v1 are in GAMMA_TO_LINEAR_BITS fixed-point precision (range [0..1])
|
||||
const uint32_t v0 = kLinearToGammaTabS[tab_pos + 0];
|
||||
const uint32_t v1 = kLinearToGammaTabS[tab_pos + 1];
|
||||
// Final interpolation. Note that rounding is already included.
|
||||
const uint32_t v2 = (v1 - v0) * x; // note: v1 >= v0.
|
||||
const uint32_t result = v0 + (v2 >> GAMMA_TO_LINEAR_BITS);
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) {}
|
||||
static WEBP_INLINE float GammaToLinearF(int v) {
|
||||
const float norm = 1.f / MAX_Y_T;
|
||||
return norm * v;
|
||||
static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesS(void) {}
|
||||
static WEBP_INLINE uint32_t GammaToLinearS(int v) {
|
||||
return (v << GAMMA_TO_LINEAR_BITS) / MAX_Y_T;
|
||||
}
|
||||
static WEBP_INLINE int LinearToGammaF(float value) {
|
||||
return (int)(MAX_Y_T * value + .5);
|
||||
static WEBP_INLINE uint32_t LinearToGammaS(uint32_t value) {
|
||||
return (MAX_Y_T * value) >> GAMMA_TO_LINEAR_BITS;
|
||||
}
|
||||
|
||||
#endif // USE_GAMMA_COMPRESSION
|
||||
@ -254,26 +265,22 @@ static int RGBToGray(int r, int g, int b) {
|
||||
return (luma >> YUV_FIX);
|
||||
}
|
||||
|
||||
static float RGBToGrayF(float r, float g, float b) {
|
||||
return (float)(0.2126 * r + 0.7152 * g + 0.0722 * b);
|
||||
}
|
||||
|
||||
static int ScaleDown(int a, int b, int c, int d) {
|
||||
const float A = GammaToLinearF(a);
|
||||
const float B = GammaToLinearF(b);
|
||||
const float C = GammaToLinearF(c);
|
||||
const float D = GammaToLinearF(d);
|
||||
return LinearToGammaF(0.25f * (A + B + C + D));
|
||||
static uint32_t ScaleDown(int a, int b, int c, int d) {
|
||||
const uint32_t A = GammaToLinearS(a);
|
||||
const uint32_t B = GammaToLinearS(b);
|
||||
const uint32_t C = GammaToLinearS(c);
|
||||
const uint32_t D = GammaToLinearS(d);
|
||||
return LinearToGammaS((A + B + C + D + 2) >> 2);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w) {
|
||||
int i;
|
||||
for (i = 0; i < w; ++i) {
|
||||
const float R = GammaToLinearF(src[0 * w + i]);
|
||||
const float G = GammaToLinearF(src[1 * w + i]);
|
||||
const float B = GammaToLinearF(src[2 * w + i]);
|
||||
const float Y = RGBToGrayF(R, G, B);
|
||||
dst[i] = (fixed_y_t)LinearToGammaF(Y);
|
||||
const uint32_t R = GammaToLinearS(src[0 * w + i]);
|
||||
const uint32_t G = GammaToLinearS(src[1 * w + i]);
|
||||
const uint32_t B = GammaToLinearS(src[2 * w + i]);
|
||||
const uint32_t Y = RGBToGray(R, G, B);
|
||||
dst[i] = (fixed_y_t)LinearToGammaS(Y);
|
||||
}
|
||||
}
|
||||
|
||||
@ -863,7 +870,7 @@ static int ImportYUVAFromRGBA(const uint8_t* r_ptr,
|
||||
}
|
||||
|
||||
if (use_iterative_conversion) {
|
||||
InitGammaTablesF();
|
||||
InitGammaTablesS();
|
||||
if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) {
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user