mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-31 02:15:42 +01:00 
			
		
		
		
	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:
		| @@ -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; | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user