Path: blob/master/thirdparty/libwebp/sharpyuv/sharpyuv_gamma.c
21363 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 "sharpyuv/sharpyuv.h"18#include "src/webp/types.h"1920// Gamma correction compensates loss of resolution during chroma subsampling.21// Size of pre-computed table for converting from gamma to linear.22#define GAMMA_TO_LINEAR_TAB_BITS 1023#define GAMMA_TO_LINEAR_TAB_SIZE (1 << GAMMA_TO_LINEAR_TAB_BITS)24static uint32_t kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 2];25#define LINEAR_TO_GAMMA_TAB_BITS 926#define LINEAR_TO_GAMMA_TAB_SIZE (1 << LINEAR_TO_GAMMA_TAB_BITS)27static uint32_t kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 2];2829#if defined(_MSC_VER)30static const double kGammaF = 2.222222222222222;31#else32static const double kGammaF = 1. / 0.45;33#endif34#define GAMMA_TO_LINEAR_BITS 163536static volatile int kGammaTablesSOk = 0;37void SharpYuvInitGammaTables(void) {38assert(GAMMA_TO_LINEAR_BITS <= 16);39if (!kGammaTablesSOk) {40int v;41const double a = 0.09929682680944;42const double thresh = 0.018053968510807;43const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;44// Precompute gamma to linear table.45{46const double norm = 1. / GAMMA_TO_LINEAR_TAB_SIZE;47const double a_rec = 1. / (1. + a);48for (v = 0; v <= GAMMA_TO_LINEAR_TAB_SIZE; ++v) {49const double g = norm * v;50double value;51if (g <= thresh * 4.5) {52value = g / 4.5;53} else {54value = pow(a_rec * (g + a), kGammaF);55}56kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);57}58// to prevent small rounding errors to cause read-overflow:59kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 1] =60kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE];61}62// Precompute linear to gamma table.63{64const double scale = 1. / LINEAR_TO_GAMMA_TAB_SIZE;65for (v = 0; v <= LINEAR_TO_GAMMA_TAB_SIZE; ++v) {66const double g = scale * v;67double value;68if (g <= thresh) {69value = 4.5 * g;70} else {71value = (1. + a) * pow(g, 1. / kGammaF) - a;72}73kLinearToGammaTabS[v] =74(uint32_t)(final_scale * value + 0.5);75}76// to prevent small rounding errors to cause read-overflow:77kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 1] =78kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE];79}80kGammaTablesSOk = 1;81}82}8384static WEBP_INLINE int Shift(int v, int shift) {85return (shift >= 0) ? (v << shift) : (v >> -shift);86}8788static WEBP_INLINE uint32_t FixedPointInterpolation(int v, uint32_t* tab,89int tab_pos_shift_right,90int tab_value_shift) {91const uint32_t tab_pos = Shift(v, -tab_pos_shift_right);92// fractional part, in 'tab_pos_shift' fixed-point precision93const uint32_t x = v - (tab_pos << tab_pos_shift_right); // fractional part94// v0 / v1 are in kGammaToLinearBits fixed-point precision (range [0..1])95const uint32_t v0 = Shift(tab[tab_pos + 0], tab_value_shift);96const uint32_t v1 = Shift(tab[tab_pos + 1], tab_value_shift);97// Final interpolation.98const uint32_t v2 = (v1 - v0) * x; // note: v1 >= v0.99const int half =100(tab_pos_shift_right > 0) ? 1 << (tab_pos_shift_right - 1) : 0;101const uint32_t result = v0 + ((v2 + half) >> tab_pos_shift_right);102return result;103}104105static uint32_t ToLinearSrgb(uint16_t v, int bit_depth) {106const int shift = GAMMA_TO_LINEAR_TAB_BITS - bit_depth;107if (shift > 0) {108return kGammaToLinearTabS[v << shift];109}110return FixedPointInterpolation(v, kGammaToLinearTabS, -shift, 0);111}112113static uint16_t FromLinearSrgb(uint32_t value, int bit_depth) {114return FixedPointInterpolation(115value, kLinearToGammaTabS,116(GAMMA_TO_LINEAR_BITS - LINEAR_TO_GAMMA_TAB_BITS),117bit_depth - GAMMA_TO_LINEAR_BITS);118}119120////////////////////////////////////////////////////////////////////////////////121122#define CLAMP(x, low, high) \123(((x) < (low)) ? (low) : (((high) < (x)) ? (high) : (x)))124#define MIN(a, b) (((a) < (b)) ? (a) : (b))125#define MAX(a, b) (((a) > (b)) ? (a) : (b))126127static WEBP_INLINE float Roundf(float x) {128if (x < 0)129return (float)ceil((double)(x - 0.5f));130else131return (float)floor((double)(x + 0.5f));132}133134static WEBP_INLINE float Powf(float base, float exp) {135return (float)pow((double)base, (double)exp);136}137138static WEBP_INLINE float Log10f(float x) { return (float)log10((double)x); }139140static float ToLinear709(float gamma) {141if (gamma < 0.f) {142return 0.f;143} else if (gamma < 4.5f * 0.018053968510807f) {144return gamma / 4.5f;145} else if (gamma < 1.f) {146return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);147}148return 1.f;149}150151static float FromLinear709(float linear) {152if (linear < 0.f) {153return 0.f;154} else if (linear < 0.018053968510807f) {155return linear * 4.5f;156} else if (linear < 1.f) {157return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;158}159return 1.f;160}161162static float ToLinear470M(float gamma) {163return Powf(CLAMP(gamma, 0.f, 1.f), 2.2f);164}165166static float FromLinear470M(float linear) {167return Powf(CLAMP(linear, 0.f, 1.f), 1.f / 2.2f);168}169170static float ToLinear470Bg(float gamma) {171return Powf(CLAMP(gamma, 0.f, 1.f), 2.8f);172}173174static float FromLinear470Bg(float linear) {175return Powf(CLAMP(linear, 0.f, 1.f), 1.f / 2.8f);176}177178static float ToLinearSmpte240(float gamma) {179if (gamma < 0.f) {180return 0.f;181} else if (gamma < 4.f * 0.022821585529445f) {182return gamma / 4.f;183} else if (gamma < 1.f) {184return Powf((gamma + 0.111572195921731f) / 1.111572195921731f, 1.f / 0.45f);185}186return 1.f;187}188189static float FromLinearSmpte240(float linear) {190if (linear < 0.f) {191return 0.f;192} else if (linear < 0.022821585529445f) {193return linear * 4.f;194} else if (linear < 1.f) {195return 1.111572195921731f * Powf(linear, 0.45f) - 0.111572195921731f;196}197return 1.f;198}199200static float ToLinearLog100(float gamma) {201// The function is non-bijective so choose the middle of [0, 0.01].202const float mid_interval = 0.01f / 2.f;203return (gamma <= 0.0f) ? mid_interval204: Powf(10.0f, 2.f * (MIN(gamma, 1.f) - 1.0f));205}206207static float FromLinearLog100(float linear) {208return (linear < 0.01f) ? 0.0f : 1.0f + Log10f(MIN(linear, 1.f)) / 2.0f;209}210211static float ToLinearLog100Sqrt10(float gamma) {212// The function is non-bijective so choose the middle of [0, 0.00316227766f[.213const float mid_interval = 0.00316227766f / 2.f;214return (gamma <= 0.0f) ? mid_interval215: Powf(10.0f, 2.5f * (MIN(gamma, 1.f) - 1.0f));216}217218static float FromLinearLog100Sqrt10(float linear) {219return (linear < 0.00316227766f) ? 0.0f220: 1.0f + Log10f(MIN(linear, 1.f)) / 2.5f;221}222223static float ToLinearIec61966(float gamma) {224if (gamma <= -4.5f * 0.018053968510807f) {225return Powf((-gamma + 0.09929682680944f) / -1.09929682680944f, 1.f / 0.45f);226} else if (gamma < 4.5f * 0.018053968510807f) {227return gamma / 4.5f;228}229return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);230}231232static float FromLinearIec61966(float linear) {233if (linear <= -0.018053968510807f) {234return -1.09929682680944f * Powf(-linear, 0.45f) + 0.09929682680944f;235} else if (linear < 0.018053968510807f) {236return linear * 4.5f;237}238return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;239}240241static float ToLinearBt1361(float gamma) {242if (gamma < -0.25f) {243return -0.25f;244} else if (gamma < 0.f) {245return Powf((gamma - 0.02482420670236f) / -0.27482420670236f, 1.f / 0.45f) /246-4.f;247} else if (gamma < 4.5f * 0.018053968510807f) {248return gamma / 4.5f;249} else if (gamma < 1.f) {250return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f);251}252return 1.f;253}254255static float FromLinearBt1361(float linear) {256if (linear < -0.25f) {257return -0.25f;258} else if (linear < 0.f) {259return -0.27482420670236f * Powf(-4.f * linear, 0.45f) + 0.02482420670236f;260} else if (linear < 0.018053968510807f) {261return linear * 4.5f;262} else if (linear < 1.f) {263return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f;264}265return 1.f;266}267268static float ToLinearPq(float gamma) {269if (gamma > 0.f) {270const float pow_gamma = Powf(gamma, 32.f / 2523.f);271const float num = MAX(pow_gamma - 107.f / 128.f, 0.0f);272const float den = MAX(2413.f / 128.f - 2392.f / 128.f * pow_gamma, FLT_MIN);273return Powf(num / den, 4096.f / 653.f);274}275return 0.f;276}277278static float FromLinearPq(float linear) {279if (linear > 0.f) {280const float pow_linear = Powf(linear, 653.f / 4096.f);281const float num = 107.f / 128.f + 2413.f / 128.f * pow_linear;282const float den = 1.0f + 2392.f / 128.f * pow_linear;283return Powf(num / den, 2523.f / 32.f);284}285return 0.f;286}287288static float ToLinearSmpte428(float gamma) {289return Powf(MAX(gamma, 0.f), 2.6f) / 0.91655527974030934f;290}291292static float FromLinearSmpte428(float linear) {293return Powf(0.91655527974030934f * MAX(linear, 0.f), 1.f / 2.6f);294}295296// Conversion in BT.2100 requires RGB info. Simplify to gamma correction here.297static float ToLinearHlg(float gamma) {298if (gamma < 0.f) {299return 0.f;300} else if (gamma <= 0.5f) {301return Powf((gamma * gamma) * (1.f / 3.f), 1.2f);302}303return Powf((expf((gamma - 0.55991073f) / 0.17883277f) + 0.28466892f) / 12.0f,3041.2f);305}306307static float FromLinearHlg(float linear) {308linear = Powf(linear, 1.f / 1.2f);309if (linear < 0.f) {310return 0.f;311} else if (linear <= (1.f / 12.f)) {312return sqrtf(3.f * linear);313}314return 0.17883277f * logf(12.f * linear - 0.28466892f) + 0.55991073f;315}316317uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth,318SharpYuvTransferFunctionType transfer_type) {319float v_float, linear;320if (transfer_type == kSharpYuvTransferFunctionSrgb) {321return ToLinearSrgb(v, bit_depth);322}323v_float = (float)v / ((1 << bit_depth) - 1);324switch (transfer_type) {325case kSharpYuvTransferFunctionBt709:326case kSharpYuvTransferFunctionBt601:327case kSharpYuvTransferFunctionBt2020_10Bit:328case kSharpYuvTransferFunctionBt2020_12Bit:329linear = ToLinear709(v_float);330break;331case kSharpYuvTransferFunctionBt470M:332linear = ToLinear470M(v_float);333break;334case kSharpYuvTransferFunctionBt470Bg:335linear = ToLinear470Bg(v_float);336break;337case kSharpYuvTransferFunctionSmpte240:338linear = ToLinearSmpte240(v_float);339break;340case kSharpYuvTransferFunctionLinear:341return v;342case kSharpYuvTransferFunctionLog100:343linear = ToLinearLog100(v_float);344break;345case kSharpYuvTransferFunctionLog100_Sqrt10:346linear = ToLinearLog100Sqrt10(v_float);347break;348case kSharpYuvTransferFunctionIec61966:349linear = ToLinearIec61966(v_float);350break;351case kSharpYuvTransferFunctionBt1361:352linear = ToLinearBt1361(v_float);353break;354case kSharpYuvTransferFunctionSmpte2084:355linear = ToLinearPq(v_float);356break;357case kSharpYuvTransferFunctionSmpte428:358linear = ToLinearSmpte428(v_float);359break;360case kSharpYuvTransferFunctionHlg:361linear = ToLinearHlg(v_float);362break;363default:364assert(0);365linear = 0;366break;367}368return (uint32_t)Roundf(linear * ((1 << 16) - 1));369}370371uint16_t SharpYuvLinearToGamma(uint32_t v, int bit_depth,372SharpYuvTransferFunctionType transfer_type) {373float v_float, linear;374if (transfer_type == kSharpYuvTransferFunctionSrgb) {375return FromLinearSrgb(v, bit_depth);376}377v_float = (float)v / ((1 << 16) - 1);378switch (transfer_type) {379case kSharpYuvTransferFunctionBt709:380case kSharpYuvTransferFunctionBt601:381case kSharpYuvTransferFunctionBt2020_10Bit:382case kSharpYuvTransferFunctionBt2020_12Bit:383linear = FromLinear709(v_float);384break;385case kSharpYuvTransferFunctionBt470M:386linear = FromLinear470M(v_float);387break;388case kSharpYuvTransferFunctionBt470Bg:389linear = FromLinear470Bg(v_float);390break;391case kSharpYuvTransferFunctionSmpte240:392linear = FromLinearSmpte240(v_float);393break;394case kSharpYuvTransferFunctionLinear:395return v;396case kSharpYuvTransferFunctionLog100:397linear = FromLinearLog100(v_float);398break;399case kSharpYuvTransferFunctionLog100_Sqrt10:400linear = FromLinearLog100Sqrt10(v_float);401break;402case kSharpYuvTransferFunctionIec61966:403linear = FromLinearIec61966(v_float);404break;405case kSharpYuvTransferFunctionBt1361:406linear = FromLinearBt1361(v_float);407break;408case kSharpYuvTransferFunctionSmpte2084:409linear = FromLinearPq(v_float);410break;411case kSharpYuvTransferFunctionSmpte428:412linear = FromLinearSmpte428(v_float);413break;414case kSharpYuvTransferFunctionHlg:415linear = FromLinearHlg(v_float);416break;417default:418assert(0);419linear = 0;420break;421}422return (uint16_t)Roundf(linear * ((1 << bit_depth) - 1));423}424425426