Path: blob/master/thirdparty/libwebp/sharpyuv/sharpyuv_gamma.c
9898 views
// Copyright 2022 Google Inc. All Rights Reserved.1//2// Use of this source code is governed by a BSD-style license3// that can be found in the COPYING file in the root of the source4// tree. An additional intellectual property rights grant can be found5// in the file PATENTS. All contributing project authors may6// be found in the AUTHORS file in the root of the source tree.7// -----------------------------------------------------------------------------8//9// Gamma correction utilities.1011#include "sharpyuv/sharpyuv_gamma.h"1213#include <assert.h>14#include <float.h>15#include <math.h>1617#include "src/webp/types.h"1819// Gamma correction compensates loss of resolution during chroma subsampling.20// Size of pre-computed table for converting from gamma to linear.21#define GAMMA_TO_LINEAR_TAB_BITS 1022#define GAMMA_TO_LINEAR_TAB_SIZE (1 << GAMMA_TO_LINEAR_TAB_BITS)23static uint32_t kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 2];24#define LINEAR_TO_GAMMA_TAB_BITS 925#define LINEAR_TO_GAMMA_TAB_SIZE (1 << LINEAR_TO_GAMMA_TAB_BITS)26static uint32_t kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 2];2728#if defined(_MSC_VER)29static const double kGammaF = 2.222222222222222;30#else31static const double kGammaF = 1. / 0.45;32#endif33#define GAMMA_TO_LINEAR_BITS 163435static volatile int kGammaTablesSOk = 0;36void SharpYuvInitGammaTables(void) {37assert(GAMMA_TO_LINEAR_BITS <= 16);38if (!kGammaTablesSOk) {39int v;40const double a = 0.09929682680944;41const double thresh = 0.018053968510807;42const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;43// Precompute gamma to linear table.44{45const double norm = 1. / GAMMA_TO_LINEAR_TAB_SIZE;46const double a_rec = 1. / (1. + a);47for (v = 0; v <= GAMMA_TO_LINEAR_TAB_SIZE; ++v) {48const double g = norm * v;49double value;50if (g <= thresh * 4.5) {51value = g / 4.5;52} else {53value = pow(a_rec * (g + a), kGammaF);54}55kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);56}57// to prevent small rounding errors to cause read-overflow:58kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 1] =59kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE];60}61// Precompute linear to gamma table.62{63const double scale = 1. / LINEAR_TO_GAMMA_TAB_SIZE;64for (v = 0; v <= LINEAR_TO_GAMMA_TAB_SIZE; ++v) {65const double g = scale * v;66double value;67if (g <= thresh) {68value = 4.5 * g;69} else {70value = (1. + a) * pow(g, 1. / kGammaF) - a;71}72kLinearToGammaTabS[v] =73(uint32_t)(final_scale * value + 0.5);74}75// to prevent small rounding errors to cause read-overflow:76kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 1] =77kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE];78}79kGammaTablesSOk = 1;80}81}8283static WEBP_INLINE int Shift(int v, int shift) {84return (shift >= 0) ? (v << shift) : (v >> -shift);85}8687static WEBP_INLINE uint32_t FixedPointInterpolation(int v, uint32_t* tab,88int tab_pos_shift_right,89int tab_value_shift) {90const uint32_t tab_pos = Shift(v, -tab_pos_shift_right);91// fractional part, in 'tab_pos_shift' fixed-point precision92const uint32_t x = v - (tab_pos << tab_pos_shift_right); // fractional part93// v0 / v1 are in kGammaToLinearBits fixed-point precision (range [0..1])94const uint32_t v0 = Shift(tab[tab_pos + 0], tab_value_shift);95const uint32_t v1 = Shift(tab[tab_pos + 1], tab_value_shift);96// Final interpolation.97const uint32_t v2 = (v1 - v0) * x; // note: v1 >= v0.98const int half =99(tab_pos_shift_right > 0) ? 1 << (tab_pos_shift_right - 1) : 0;100const uint32_t result = v0 + ((v2 + half) >> tab_pos_shift_right);101return result;102}103104static uint32_t ToLinearSrgb(uint16_t v, int bit_depth) {105const int shift = GAMMA_TO_LINEAR_TAB_BITS - bit_depth;106if (shift > 0) {107return kGammaToLinearTabS[v << shift];108}109return FixedPointInterpolation(v, kGammaToLinearTabS, -shift, 0);110}111112static uint16_t FromLinearSrgb(uint32_t value, int bit_depth) {113return FixedPointInterpolation(114value, kLinearToGammaTabS,115(GAMMA_TO_LINEAR_BITS - LINEAR_TO_GAMMA_TAB_BITS),116bit_depth - GAMMA_TO_LINEAR_BITS);117}118119////////////////////////////////////////////////////////////////////////////////120121#define CLAMP(x, low, high) \122(((x) < (low)) ? (low) : (((high) < (x)) ? (high) : (x)))123#define MIN(a, b) (((a) < (b)) ? (a) : (b))124#define MAX(a, b) (((a) > (b)) ? (a) : (b))125126static WEBP_INLINE float Roundf(float x) {127if (x < 0)128return (float)ceil((double)(x - 0.5f));129else130return (float)floor((double)(x + 0.5f));131}132133static WEBP_INLINE float Powf(float base, float exp) {134return (float)pow((double)base, (double)exp);135}136137static WEBP_INLINE float Log10f(float x) { return (float)log10((double)x); }138139static float ToLinear709(float gamma) {140if (gamma < 0.f) {141return 0.f;142} else if (gamma < 4.5f * 0.018053968510807f) {143return gamma / 4.5f;144} else if (gamma < 1.f) {145return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);146}147return 1.f;148}149150static float FromLinear709(float linear) {151if (linear < 0.f) {152return 0.f;153} else if (linear < 0.018053968510807f) {154return linear * 4.5f;155} else if (linear < 1.f) {156return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;157}158return 1.f;159}160161static float ToLinear470M(float gamma) {162return Powf(CLAMP(gamma, 0.f, 1.f), 2.2f);163}164165static float FromLinear470M(float linear) {166return Powf(CLAMP(linear, 0.f, 1.f), 1.f / 2.2f);167}168169static float ToLinear470Bg(float gamma) {170return Powf(CLAMP(gamma, 0.f, 1.f), 2.8f);171}172173static float FromLinear470Bg(float linear) {174return Powf(CLAMP(linear, 0.f, 1.f), 1.f / 2.8f);175}176177static float ToLinearSmpte240(float gamma) {178if (gamma < 0.f) {179return 0.f;180} else if (gamma < 4.f * 0.022821585529445f) {181return gamma / 4.f;182} else if (gamma < 1.f) {183return Powf((gamma + 0.111572195921731f) / 1.111572195921731f, 1.f / 0.45f);184}185return 1.f;186}187188static float FromLinearSmpte240(float linear) {189if (linear < 0.f) {190return 0.f;191} else if (linear < 0.022821585529445f) {192return linear * 4.f;193} else if (linear < 1.f) {194return 1.111572195921731f * Powf(linear, 0.45f) - 0.111572195921731f;195}196return 1.f;197}198199static float ToLinearLog100(float gamma) {200// The function is non-bijective so choose the middle of [0, 0.01].201const float mid_interval = 0.01f / 2.f;202return (gamma <= 0.0f) ? mid_interval203: Powf(10.0f, 2.f * (MIN(gamma, 1.f) - 1.0f));204}205206static float FromLinearLog100(float linear) {207return (linear < 0.01f) ? 0.0f : 1.0f + Log10f(MIN(linear, 1.f)) / 2.0f;208}209210static float ToLinearLog100Sqrt10(float gamma) {211// The function is non-bijective so choose the middle of [0, 0.00316227766f[.212const float mid_interval = 0.00316227766f / 2.f;213return (gamma <= 0.0f) ? mid_interval214: Powf(10.0f, 2.5f * (MIN(gamma, 1.f) - 1.0f));215}216217static float FromLinearLog100Sqrt10(float linear) {218return (linear < 0.00316227766f) ? 0.0f219: 1.0f + Log10f(MIN(linear, 1.f)) / 2.5f;220}221222static float ToLinearIec61966(float gamma) {223if (gamma <= -4.5f * 0.018053968510807f) {224return Powf((-gamma + 0.09929682680944f) / -1.09929682680944f, 1.f / 0.45f);225} else if (gamma < 4.5f * 0.018053968510807f) {226return gamma / 4.5f;227}228return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);229}230231static float FromLinearIec61966(float linear) {232if (linear <= -0.018053968510807f) {233return -1.09929682680944f * Powf(-linear, 0.45f) + 0.09929682680944f;234} else if (linear < 0.018053968510807f) {235return linear * 4.5f;236}237return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;238}239240static float ToLinearBt1361(float gamma) {241if (gamma < -0.25f) {242return -0.25f;243} else if (gamma < 0.f) {244return Powf((gamma - 0.02482420670236f) / -0.27482420670236f, 1.f / 0.45f) /245-4.f;246} else if (gamma < 4.5f * 0.018053968510807f) {247return gamma / 4.5f;248} else if (gamma < 1.f) {249return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);250}251return 1.f;252}253254static float FromLinearBt1361(float linear) {255if (linear < -0.25f) {256return -0.25f;257} else if (linear < 0.f) {258return -0.27482420670236f * Powf(-4.f * linear, 0.45f) + 0.02482420670236f;259} else if (linear < 0.018053968510807f) {260return linear * 4.5f;261} else if (linear < 1.f) {262return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;263}264return 1.f;265}266267static float ToLinearPq(float gamma) {268if (gamma > 0.f) {269const float pow_gamma = Powf(gamma, 32.f / 2523.f);270const float num = MAX(pow_gamma - 107.f / 128.f, 0.0f);271const float den = MAX(2413.f / 128.f - 2392.f / 128.f * pow_gamma, FLT_MIN);272return Powf(num / den, 4096.f / 653.f);273}274return 0.f;275}276277static float FromLinearPq(float linear) {278if (linear > 0.f) {279const float pow_linear = Powf(linear, 653.f / 4096.f);280const float num = 107.f / 128.f + 2413.f / 128.f * pow_linear;281const float den = 1.0f + 2392.f / 128.f * pow_linear;282return Powf(num / den, 2523.f / 32.f);283}284return 0.f;285}286287static float ToLinearSmpte428(float gamma) {288return Powf(MAX(gamma, 0.f), 2.6f) / 0.91655527974030934f;289}290291static float FromLinearSmpte428(float linear) {292return Powf(0.91655527974030934f * MAX(linear, 0.f), 1.f / 2.6f);293}294295// Conversion in BT.2100 requires RGB info. Simplify to gamma correction here.296static float ToLinearHlg(float gamma) {297if (gamma < 0.f) {298return 0.f;299} else if (gamma <= 0.5f) {300return Powf((gamma * gamma) * (1.f / 3.f), 1.2f);301}302return Powf((expf((gamma - 0.55991073f) / 0.17883277f) + 0.28466892f) / 12.0f,3031.2f);304}305306static float FromLinearHlg(float linear) {307linear = Powf(linear, 1.f / 1.2f);308if (linear < 0.f) {309return 0.f;310} else if (linear <= (1.f / 12.f)) {311return sqrtf(3.f * linear);312}313return 0.17883277f * logf(12.f * linear - 0.28466892f) + 0.55991073f;314}315316uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth,317SharpYuvTransferFunctionType transfer_type) {318float v_float, linear;319if (transfer_type == kSharpYuvTransferFunctionSrgb) {320return ToLinearSrgb(v, bit_depth);321}322v_float = (float)v / ((1 << bit_depth) - 1);323switch (transfer_type) {324case kSharpYuvTransferFunctionBt709:325case kSharpYuvTransferFunctionBt601:326case kSharpYuvTransferFunctionBt2020_10Bit:327case kSharpYuvTransferFunctionBt2020_12Bit:328linear = ToLinear709(v_float);329break;330case kSharpYuvTransferFunctionBt470M:331linear = ToLinear470M(v_float);332break;333case kSharpYuvTransferFunctionBt470Bg:334linear = ToLinear470Bg(v_float);335break;336case kSharpYuvTransferFunctionSmpte240:337linear = ToLinearSmpte240(v_float);338break;339case kSharpYuvTransferFunctionLinear:340return v;341case kSharpYuvTransferFunctionLog100:342linear = ToLinearLog100(v_float);343break;344case kSharpYuvTransferFunctionLog100_Sqrt10:345linear = ToLinearLog100Sqrt10(v_float);346break;347case kSharpYuvTransferFunctionIec61966:348linear = ToLinearIec61966(v_float);349break;350case kSharpYuvTransferFunctionBt1361:351linear = ToLinearBt1361(v_float);352break;353case kSharpYuvTransferFunctionSmpte2084:354linear = ToLinearPq(v_float);355break;356case kSharpYuvTransferFunctionSmpte428:357linear = ToLinearSmpte428(v_float);358break;359case kSharpYuvTransferFunctionHlg:360linear = ToLinearHlg(v_float);361break;362default:363assert(0);364linear = 0;365break;366}367return (uint32_t)Roundf(linear * ((1 << 16) - 1));368}369370uint16_t SharpYuvLinearToGamma(uint32_t v, int bit_depth,371SharpYuvTransferFunctionType transfer_type) {372float v_float, linear;373if (transfer_type == kSharpYuvTransferFunctionSrgb) {374return FromLinearSrgb(v, bit_depth);375}376v_float = (float)v / ((1 << 16) - 1);377switch (transfer_type) {378case kSharpYuvTransferFunctionBt709:379case kSharpYuvTransferFunctionBt601:380case kSharpYuvTransferFunctionBt2020_10Bit:381case kSharpYuvTransferFunctionBt2020_12Bit:382linear = FromLinear709(v_float);383break;384case kSharpYuvTransferFunctionBt470M:385linear = FromLinear470M(v_float);386break;387case kSharpYuvTransferFunctionBt470Bg:388linear = FromLinear470Bg(v_float);389break;390case kSharpYuvTransferFunctionSmpte240:391linear = FromLinearSmpte240(v_float);392break;393case kSharpYuvTransferFunctionLinear:394return v;395case kSharpYuvTransferFunctionLog100:396linear = FromLinearLog100(v_float);397break;398case kSharpYuvTransferFunctionLog100_Sqrt10:399linear = FromLinearLog100Sqrt10(v_float);400break;401case kSharpYuvTransferFunctionIec61966:402linear = FromLinearIec61966(v_float);403break;404case kSharpYuvTransferFunctionBt1361:405linear = FromLinearBt1361(v_float);406break;407case kSharpYuvTransferFunctionSmpte2084:408linear = FromLinearPq(v_float);409break;410case kSharpYuvTransferFunctionSmpte428:411linear = FromLinearSmpte428(v_float);412break;413case kSharpYuvTransferFunctionHlg:414linear = FromLinearHlg(v_float);415break;416default:417assert(0);418linear = 0;419break;420}421return (uint16_t)Roundf(linear * ((1 << bit_depth) - 1));422}423424425