go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

sharpyuv_gamma.c (13621B)


      1 // Copyright 2022 Google Inc. All Rights Reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style license
      4 // that can be found in the COPYING file in the root of the source
      5 // tree. An additional intellectual property rights grant can be found
      6 // in the file PATENTS. All contributing project authors may
      7 // be found in the AUTHORS file in the root of the source tree.
      8 // -----------------------------------------------------------------------------
      9 //
     10 // Gamma correction utilities.
     11 
     12 #include "sharpyuv/sharpyuv_gamma.h"
     13 
     14 #include <assert.h>
     15 #include <float.h>
     16 #include <math.h>
     17 
     18 #include "sharpyuv/sharpyuv.h"
     19 #include "src/webp/types.h"
     20 
     21 // Gamma correction compensates loss of resolution during chroma subsampling.
     22 // Size of pre-computed table for converting from gamma to linear.
     23 #define GAMMA_TO_LINEAR_TAB_BITS 10
     24 #define GAMMA_TO_LINEAR_TAB_SIZE (1 << GAMMA_TO_LINEAR_TAB_BITS)
     25 static uint32_t kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 2];
     26 #define LINEAR_TO_GAMMA_TAB_BITS 9
     27 #define LINEAR_TO_GAMMA_TAB_SIZE (1 << LINEAR_TO_GAMMA_TAB_BITS)
     28 static uint32_t kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 2];
     29 
     30 static const double kGammaF = 1. / 0.45;
     31 #define GAMMA_TO_LINEAR_BITS 16
     32 
     33 static volatile int kGammaTablesSOk = 0;
     34 void SharpYuvInitGammaTables(void) {
     35   assert(GAMMA_TO_LINEAR_BITS <= 16);
     36   if (!kGammaTablesSOk) {
     37     int v;
     38     const double a = 0.09929682680944;
     39     const double thresh = 0.018053968510807;
     40     const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;
     41     // Precompute gamma to linear table.
     42     {
     43       const double norm = 1. / GAMMA_TO_LINEAR_TAB_SIZE;
     44       const double a_rec = 1. / (1. + a);
     45       for (v = 0; v <= GAMMA_TO_LINEAR_TAB_SIZE; ++v) {
     46         const double g = norm * v;
     47         double value;
     48         if (g <= thresh * 4.5) {
     49           value = g / 4.5;
     50         } else {
     51           value = pow(a_rec * (g + a), kGammaF);
     52         }
     53         kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);
     54       }
     55       // to prevent small rounding errors to cause read-overflow:
     56       kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 1] =
     57           kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE];
     58     }
     59     // Precompute linear to gamma table.
     60     {
     61       const double scale = 1. / LINEAR_TO_GAMMA_TAB_SIZE;
     62       for (v = 0; v <= LINEAR_TO_GAMMA_TAB_SIZE; ++v) {
     63         const double g = scale * v;
     64         double value;
     65         if (g <= thresh) {
     66           value = 4.5 * g;
     67         } else {
     68           value = (1. + a) * pow(g, 1. / kGammaF) - a;
     69         }
     70         kLinearToGammaTabS[v] =
     71             (uint32_t)(final_scale * value + 0.5);
     72       }
     73       // to prevent small rounding errors to cause read-overflow:
     74       kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 1] =
     75           kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE];
     76     }
     77     kGammaTablesSOk = 1;
     78   }
     79 }
     80 
     81 static WEBP_INLINE int Shift(int v, int shift) {
     82   return (shift >= 0) ? (v << shift) : (v >> -shift);
     83 }
     84 
     85 static WEBP_INLINE uint32_t FixedPointInterpolation(int v, uint32_t* tab,
     86                                                     int tab_pos_shift_right,
     87                                                     int tab_value_shift) {
     88   const uint32_t tab_pos = Shift(v, -tab_pos_shift_right);
     89   // fractional part, in 'tab_pos_shift' fixed-point precision
     90   const uint32_t x = v - (tab_pos << tab_pos_shift_right);  // fractional part
     91   // v0 / v1 are in kGammaToLinearBits fixed-point precision (range [0..1])
     92   const uint32_t v0 = Shift(tab[tab_pos + 0], tab_value_shift);
     93   const uint32_t v1 = Shift(tab[tab_pos + 1], tab_value_shift);
     94   // Final interpolation.
     95   const uint32_t v2 = (v1 - v0) * x;  // note: v1 >= v0.
     96   const int half =
     97       (tab_pos_shift_right > 0) ? 1 << (tab_pos_shift_right - 1) : 0;
     98   const uint32_t result = v0 + ((v2 + half) >> tab_pos_shift_right);
     99   return result;
    100 }
    101 
    102 static uint32_t ToLinearSrgb(uint16_t v, int bit_depth) {
    103   const int shift = GAMMA_TO_LINEAR_TAB_BITS - bit_depth;
    104   if (shift > 0) {
    105     return kGammaToLinearTabS[v << shift];
    106   }
    107   return FixedPointInterpolation(v, kGammaToLinearTabS, -shift, 0);
    108 }
    109 
    110 static uint16_t FromLinearSrgb(uint32_t value, int bit_depth) {
    111   return FixedPointInterpolation(
    112       value, kLinearToGammaTabS,
    113       (GAMMA_TO_LINEAR_BITS - LINEAR_TO_GAMMA_TAB_BITS),
    114       bit_depth - GAMMA_TO_LINEAR_BITS);
    115 }
    116 
    117 ////////////////////////////////////////////////////////////////////////////////
    118 
    119 #define CLAMP(x, low, high) \
    120   (((x) < (low)) ? (low) : (((high) < (x)) ? (high) : (x)))
    121 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
    122 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
    123 
    124 static WEBP_INLINE float Roundf(float x) {
    125   if (x < 0)
    126     return (float)ceil((double)(x - 0.5f));
    127   else
    128     return (float)floor((double)(x + 0.5f));
    129 }
    130 
    131 static WEBP_INLINE float Powf(float base, float exp) {
    132   return (float)pow((double)base, (double)exp);
    133 }
    134 
    135 static WEBP_INLINE float Log10f(float x) { return (float)log10((double)x); }
    136 
    137 static float ToLinear709(float gamma) {
    138   if (gamma < 0.f) {
    139     return 0.f;
    140   } else if (gamma < 4.5f * 0.018053968510807f) {
    141     return gamma / 4.5f;
    142   } else if (gamma < 1.f) {
    143     return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);
    144   }
    145   return 1.f;
    146 }
    147 
    148 static float FromLinear709(float linear) {
    149   if (linear < 0.f) {
    150     return 0.f;
    151   } else if (linear < 0.018053968510807f) {
    152     return linear * 4.5f;
    153   } else if (linear < 1.f) {
    154     return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;
    155   }
    156   return 1.f;
    157 }
    158 
    159 static float ToLinear470M(float gamma) {
    160   return Powf(CLAMP(gamma, 0.f, 1.f), 2.2f);
    161 }
    162 
    163 static float FromLinear470M(float linear) {
    164   return Powf(CLAMP(linear, 0.f, 1.f), 1.f / 2.2f);
    165 }
    166 
    167 static float ToLinear470Bg(float gamma) {
    168   return Powf(CLAMP(gamma, 0.f, 1.f), 2.8f);
    169 }
    170 
    171 static float FromLinear470Bg(float linear) {
    172   return Powf(CLAMP(linear, 0.f, 1.f), 1.f / 2.8f);
    173 }
    174 
    175 static float ToLinearSmpte240(float gamma) {
    176   if (gamma < 0.f) {
    177     return 0.f;
    178   } else if (gamma < 4.f * 0.022821585529445f) {
    179     return gamma / 4.f;
    180   } else if (gamma < 1.f) {
    181     return Powf((gamma + 0.111572195921731f) / 1.111572195921731f, 1.f / 0.45f);
    182   }
    183   return 1.f;
    184 }
    185 
    186 static float FromLinearSmpte240(float linear) {
    187   if (linear < 0.f) {
    188     return 0.f;
    189   } else if (linear < 0.022821585529445f) {
    190     return linear * 4.f;
    191   } else if (linear < 1.f) {
    192     return 1.111572195921731f * Powf(linear, 0.45f) - 0.111572195921731f;
    193   }
    194   return 1.f;
    195 }
    196 
    197 static float ToLinearLog100(float gamma) {
    198   // The function is non-bijective so choose the middle of [0, 0.01].
    199   const float mid_interval = 0.01f / 2.f;
    200   return (gamma <= 0.0f) ? mid_interval
    201                           : Powf(10.0f, 2.f * (MIN(gamma, 1.f) - 1.0f));
    202 }
    203 
    204 static float FromLinearLog100(float linear) {
    205   return (linear < 0.01f) ? 0.0f : 1.0f + Log10f(MIN(linear, 1.f)) / 2.0f;
    206 }
    207 
    208 static float ToLinearLog100Sqrt10(float gamma) {
    209   // The function is non-bijective so choose the middle of [0, 0.00316227766f[.
    210   const float mid_interval = 0.00316227766f / 2.f;
    211   return (gamma <= 0.0f) ? mid_interval
    212                           : Powf(10.0f, 2.5f * (MIN(gamma, 1.f) - 1.0f));
    213 }
    214 
    215 static float FromLinearLog100Sqrt10(float linear) {
    216   return (linear < 0.00316227766f) ? 0.0f
    217                                   : 1.0f + Log10f(MIN(linear, 1.f)) / 2.5f;
    218 }
    219 
    220 static float ToLinearIec61966(float gamma) {
    221   if (gamma <= -4.5f * 0.018053968510807f) {
    222     return Powf((-gamma + 0.09929682680944f) / -1.09929682680944f, 1.f / 0.45f);
    223   } else if (gamma < 4.5f * 0.018053968510807f) {
    224     return gamma / 4.5f;
    225   }
    226   return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);
    227 }
    228 
    229 static float FromLinearIec61966(float linear) {
    230   if (linear <= -0.018053968510807f) {
    231     return -1.09929682680944f * Powf(-linear, 0.45f) + 0.09929682680944f;
    232   } else if (linear < 0.018053968510807f) {
    233     return linear * 4.5f;
    234   }
    235   return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;
    236 }
    237 
    238 static float ToLinearBt1361(float gamma) {
    239   if (gamma < -0.25f) {
    240     return -0.25f;
    241   } else if (gamma < 0.f) {
    242     return Powf((gamma - 0.02482420670236f) / -0.27482420670236f, 1.f / 0.45f) /
    243            -4.f;
    244   } else if (gamma < 4.5f * 0.018053968510807f) {
    245     return gamma / 4.5f;
    246   } else if (gamma < 1.f) {
    247     return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);
    248   }
    249   return 1.f;
    250 }
    251 
    252 static float FromLinearBt1361(float linear) {
    253   if (linear < -0.25f) {
    254     return -0.25f;
    255   } else if (linear < 0.f) {
    256     return -0.27482420670236f * Powf(-4.f * linear, 0.45f) + 0.02482420670236f;
    257   } else if (linear < 0.018053968510807f) {
    258     return linear * 4.5f;
    259   } else if (linear < 1.f) {
    260     return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;
    261   }
    262   return 1.f;
    263 }
    264 
    265 static float ToLinearPq(float gamma) {
    266   if (gamma > 0.f) {
    267     const float pow_gamma = Powf(gamma, 32.f / 2523.f);
    268     const float num = MAX(pow_gamma - 107.f / 128.f, 0.0f);
    269     const float den = MAX(2413.f / 128.f - 2392.f / 128.f * pow_gamma, FLT_MIN);
    270     return Powf(num / den, 4096.f / 653.f);
    271   }
    272   return 0.f;
    273 }
    274 
    275 static float FromLinearPq(float linear) {
    276   if (linear > 0.f) {
    277     const float pow_linear = Powf(linear, 653.f / 4096.f);
    278     const float num = 107.f / 128.f + 2413.f / 128.f * pow_linear;
    279     const float den = 1.0f + 2392.f / 128.f * pow_linear;
    280     return Powf(num / den, 2523.f / 32.f);
    281   }
    282   return 0.f;
    283 }
    284 
    285 static float ToLinearSmpte428(float gamma) {
    286   return Powf(MAX(gamma, 0.f), 2.6f) / 0.91655527974030934f;
    287 }
    288 
    289 static float FromLinearSmpte428(float linear) {
    290   return Powf(0.91655527974030934f * MAX(linear, 0.f), 1.f / 2.6f);
    291 }
    292 
    293 // Conversion in BT.2100 requires RGB info. Simplify to gamma correction here.
    294 static float ToLinearHlg(float gamma) {
    295   if (gamma < 0.f) {
    296     return 0.f;
    297   } else if (gamma <= 0.5f) {
    298     return Powf((gamma * gamma) * (1.f / 3.f), 1.2f);
    299   }
    300   return Powf((expf((gamma - 0.55991073f) / 0.17883277f) + 0.28466892f) / 12.0f,
    301               1.2f);
    302 }
    303 
    304 static float FromLinearHlg(float linear) {
    305   linear = Powf(linear, 1.f / 1.2f);
    306   if (linear < 0.f) {
    307     return 0.f;
    308   } else if (linear <= (1.f / 12.f)) {
    309     return sqrtf(3.f * linear);
    310   }
    311   return 0.17883277f * logf(12.f * linear - 0.28466892f) + 0.55991073f;
    312 }
    313 
    314 uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth,
    315                                SharpYuvTransferFunctionType transfer_type) {
    316   float v_float, linear;
    317   if (transfer_type == kSharpYuvTransferFunctionSrgb) {
    318     return ToLinearSrgb(v, bit_depth);
    319   }
    320   v_float = (float)v / ((1 << bit_depth) - 1);
    321   switch (transfer_type) {
    322     case kSharpYuvTransferFunctionBt709:
    323     case kSharpYuvTransferFunctionBt601:
    324     case kSharpYuvTransferFunctionBt2020_10Bit:
    325     case kSharpYuvTransferFunctionBt2020_12Bit:
    326       linear = ToLinear709(v_float);
    327       break;
    328     case kSharpYuvTransferFunctionBt470M:
    329       linear = ToLinear470M(v_float);
    330       break;
    331     case kSharpYuvTransferFunctionBt470Bg:
    332       linear = ToLinear470Bg(v_float);
    333       break;
    334     case kSharpYuvTransferFunctionSmpte240:
    335       linear = ToLinearSmpte240(v_float);
    336       break;
    337     case kSharpYuvTransferFunctionLinear:
    338       return v;
    339     case kSharpYuvTransferFunctionLog100:
    340       linear = ToLinearLog100(v_float);
    341       break;
    342     case kSharpYuvTransferFunctionLog100_Sqrt10:
    343       linear = ToLinearLog100Sqrt10(v_float);
    344       break;
    345     case kSharpYuvTransferFunctionIec61966:
    346       linear = ToLinearIec61966(v_float);
    347       break;
    348     case kSharpYuvTransferFunctionBt1361:
    349       linear = ToLinearBt1361(v_float);
    350       break;
    351     case kSharpYuvTransferFunctionSmpte2084:
    352       linear = ToLinearPq(v_float);
    353       break;
    354     case kSharpYuvTransferFunctionSmpte428:
    355       linear = ToLinearSmpte428(v_float);
    356       break;
    357     case kSharpYuvTransferFunctionHlg:
    358       linear = ToLinearHlg(v_float);
    359       break;
    360     default:
    361       assert(0);
    362       linear = 0;
    363       break;
    364   }
    365   return (uint32_t)Roundf(linear * ((1 << 16) - 1));
    366 }
    367 
    368 uint16_t SharpYuvLinearToGamma(uint32_t v, int bit_depth,
    369                                SharpYuvTransferFunctionType transfer_type) {
    370   float v_float, linear;
    371   if (transfer_type == kSharpYuvTransferFunctionSrgb) {
    372     return FromLinearSrgb(v, bit_depth);
    373   }
    374   v_float = (float)v / ((1 << 16) - 1);
    375   switch (transfer_type) {
    376     case kSharpYuvTransferFunctionBt709:
    377     case kSharpYuvTransferFunctionBt601:
    378     case kSharpYuvTransferFunctionBt2020_10Bit:
    379     case kSharpYuvTransferFunctionBt2020_12Bit:
    380       linear = FromLinear709(v_float);
    381       break;
    382     case kSharpYuvTransferFunctionBt470M:
    383       linear = FromLinear470M(v_float);
    384       break;
    385     case kSharpYuvTransferFunctionBt470Bg:
    386       linear = FromLinear470Bg(v_float);
    387       break;
    388     case kSharpYuvTransferFunctionSmpte240:
    389       linear = FromLinearSmpte240(v_float);
    390       break;
    391     case kSharpYuvTransferFunctionLinear:
    392       return v;
    393     case kSharpYuvTransferFunctionLog100:
    394       linear = FromLinearLog100(v_float);
    395       break;
    396     case kSharpYuvTransferFunctionLog100_Sqrt10:
    397       linear = FromLinearLog100Sqrt10(v_float);
    398       break;
    399     case kSharpYuvTransferFunctionIec61966:
    400       linear = FromLinearIec61966(v_float);
    401       break;
    402     case kSharpYuvTransferFunctionBt1361:
    403       linear = FromLinearBt1361(v_float);
    404       break;
    405     case kSharpYuvTransferFunctionSmpte2084:
    406       linear = FromLinearPq(v_float);
    407       break;
    408     case kSharpYuvTransferFunctionSmpte428:
    409       linear = FromLinearSmpte428(v_float);
    410       break;
    411     case kSharpYuvTransferFunctionHlg:
    412       linear = FromLinearHlg(v_float);
    413       break;
    414     default:
    415       assert(0);
    416       linear = 0;
    417       break;
    418   }
    419   return (uint16_t)Roundf(linear * ((1 << bit_depth) - 1));
    420 }