From c50ee401d0d8f02ad33b4d235c608f70c76873e1 Mon Sep 17 00:00:00 2001 From: awxkee Date: Mon, 9 Oct 2023 21:14:58 +1100 Subject: [PATCH] fix alpha detection --- Sources/avifc/AVIFImageXForm.mm | 2 +- Sources/avifc/Colorspace.h | 112 ------------------ Sources/avifc/NEMath.h | 11 +- Sources/avifc/PerceptualQuantinizer.mm | 40 +++---- Sources/avifc/ToneMap/AcesHillToneMapper.cpp | 101 ++++++++++++++++ Sources/avifc/ToneMap/AcesHillToneMapper.hpp | 37 ++++++ .../avifc/ToneMap/HableFilmicToneMapper.cpp | 46 +++++++ .../avifc/ToneMap/HableFilmicToneMapper.hpp | 44 +++++++ Sources/avifc/ToneMap/HableToneMapper.cpp | 39 ++++++ Sources/avifc/ToneMap/HableToneMapper.hpp | 41 +++++++ .../avifc/ToneMap/LogarithmicToneMapper.cpp | 46 +++++++ .../avifc/ToneMap/LogarithmicToneMapper.hpp | 44 +++++++ Sources/avifc/ToneMap/MobiusToneMapper.cpp | 52 ++++++++ Sources/avifc/ToneMap/MobiusToneMapper.hpp | 45 +++++++ .../avifc/{ => ToneMap}/Rec2408ToneMapper.cpp | 21 +--- .../avifc/{ => ToneMap}/Rec2408ToneMapper.hpp | 19 ++- Sources/avifc/ToneMap/ReinhardToneMapper.cpp | 60 ++++++++++ Sources/avifc/ToneMap/ReinhardToneMapper.hpp | 43 +++++++ Sources/avifc/ToneMap/ToneMapper.cpp | 8 ++ Sources/avifc/ToneMap/ToneMapper.hpp | 32 +++++ 20 files changed, 681 insertions(+), 162 deletions(-) create mode 100644 Sources/avifc/ToneMap/AcesHillToneMapper.cpp create mode 100644 Sources/avifc/ToneMap/AcesHillToneMapper.hpp create mode 100644 Sources/avifc/ToneMap/HableFilmicToneMapper.cpp create mode 100644 Sources/avifc/ToneMap/HableFilmicToneMapper.hpp create mode 100644 Sources/avifc/ToneMap/HableToneMapper.cpp create mode 100644 Sources/avifc/ToneMap/HableToneMapper.hpp create mode 100644 Sources/avifc/ToneMap/LogarithmicToneMapper.cpp create mode 100644 Sources/avifc/ToneMap/LogarithmicToneMapper.hpp create mode 100644 Sources/avifc/ToneMap/MobiusToneMapper.cpp create mode 100644 Sources/avifc/ToneMap/MobiusToneMapper.hpp rename Sources/avifc/{ => ToneMap}/Rec2408ToneMapper.cpp (72%) rename Sources/avifc/{ => ToneMap}/Rec2408ToneMapper.hpp (62%) create mode 100644 Sources/avifc/ToneMap/ReinhardToneMapper.cpp create mode 100644 Sources/avifc/ToneMap/ReinhardToneMapper.hpp create mode 100644 Sources/avifc/ToneMap/ToneMapper.cpp create mode 100644 Sources/avifc/ToneMap/ToneMapper.hpp diff --git a/Sources/avifc/AVIFImageXForm.mm b/Sources/avifc/AVIFImageXForm.mm index 35e5458..e89e0a8 100644 --- a/Sources/avifc/AVIFImageXForm.mm +++ b/Sources/avifc/AVIFImageXForm.mm @@ -62,7 +62,7 @@ - (_Nullable CGImageRef)formCGImage:(nonnull avifDecoder*)decoder scale:(CGFloat avifRGBImage rgbImage; avifRGBImageSetDefaults(&rgbImage, decoder->image); - auto imageUsesAlpha = decoder->image->imageOwnsAlphaPlane; + auto imageUsesAlpha = decoder->image->imageOwnsAlphaPlane || decoder->image->alphaPlane != nullptr; int components = imageUsesAlpha ? 4 : 3; diff --git a/Sources/avifc/Colorspace.h b/Sources/avifc/Colorspace.h index 9e49b57..dd30e88 100644 --- a/Sources/avifc/Colorspace.h +++ b/Sources/avifc/Colorspace.h @@ -328,48 +328,6 @@ inline vector Colorspace_Gamut_Conversion_DCIP3_to_709(const vector> aces_input_matrix = -{ - {0.59719f, 0.35458f, 0.04823f}, - {0.07600f, 0.90834f, 0.01566f}, - {0.02840f, 0.13383f, 0.83777f} -}; - -static const std::vector> aces_output_matrix = -{ - { 1.60475f, -0.53108f, -0.07367f}, - {-0.10208f, 1.10813f, -0.00605f}, - {-0.00327f, -0.07276f, 1.07602f} -}; - -vector rtt_and_odt_fit(vector v) -{ - vector a = add(mul(v, add(v, 0.0245786f)), - 0.000090537f); - vector b = mul(mul(v, add(mul(v, 0.983729f), 0.4329510f)), 0.238081f); - return div(a, b); -} - -vector aces_approx(const vector& v) -{ - vector r = mul(v, 0.6f); - float a = 2.51f; - float b = 0.03f; - float c = 2.43f; - float d = 0.59f; - float e = 0.14f; - auto num = mul(r,add(mul(r,a),b)); - auto den = add(mul(r, add(mul(r, c), d)), e); - auto ret = clamp(div(num, den), 0.0f, 1.0f); - return ret; -} - -vector aces_fitted(vector& v) -{ - v = mul(aces_input_matrix, v); - v = rtt_and_odt_fit(v); - return mul(aces_output_matrix, v); -} - std::vector acesFilmicToneMapping(const std::vector& rgb) { std::vector result(3); for (int i = 0; i < 3; ++i) { @@ -419,33 +377,10 @@ std::vector filmicUncharted3ToneMapping(const std::vector& rgb) { return result; } -std::vector reinhard(const std::vector& rgb) { - return div(rgb, add(rgb, 1.0f)); -} - -float reinhard(const float v) { - return v / (1.0f + v); -} - inline float Luma(const vector& v, const float* primaries) { return v[0] * primaries[0] + v[1] * primaries[1] + v[2] * primaries[2]; } -vector change_luminance(const vector& c_in, float l_out, const float* primaries) -{ - float l_in = Luma(c_in, primaries); - const float scale = l_out / l_in; - return { c_in[0]*scale, c_in[1]*scale, c_in[2]*scale }; -} - -vector reinhard_extended_luminance(const vector& v, float max_white_l, const float* primaries) -{ - float l_old = Luma(v, primaries); - float numerator = l_old * (1.0f + (l_old / (max_white_l * max_white_l))); - float l_new = numerator / (1.0f + l_old); - return change_luminance(v, l_new, primaries); -} - template T lerp(const T& a, const T& b, float t) { return a + t * (b - a); @@ -458,51 +393,4 @@ vector reinhard_jodie(const vector& v, const float* primaries) vector res = { lerp(v[0] / (1.0f + l), tv[0], tv[0]), lerp(v[1] / (1.0f + l), tv[1], tv[1]), lerp(v[2] / (1.0f + l), tv[2], tv[2]) }; return res; } - -float uncharted2_tonemap_partial(float x) -{ - float A = 0.15f; - float B = 0.50f; - float C = 0.10f; - float D = 0.20f; - float E = 0.02f; - float F = 0.30f; - return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; -} - -float uncharted2_filmic(float v) -{ - float exposure_bias = 2.0f; - float curr = uncharted2_tonemap_partial(v * exposure_bias); - - float W = 11.2f; - float white_scale = 1.0f / uncharted2_tonemap_partial(W); - return curr * white_scale; -} - - -// Apply the Hable tone mapping operator to a single HDR pixel -inline float hableToneMapping(float x) { - const float A = 0.15f; // Shoulder Strength - const float B = 0.50f; // Linear Strength - const float C = 0.10f; // Linear Angle - const float D = 0.20f; // Toe Strength - const float E = 0.02f; // Toe Numerator - const float F = 0.30f; // Toe Denominator - return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; -} - -inline float hable(float x) -{ - const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30; - - return ((x * (A * x + (C * B)) + (D * E)) / (x * (A * x + B) + (D * F))) - E / F; -} - -inline float ToneMappingHable(const float rgb) -{ - static const float HABLE_DIV = hable(4.8); - return hable(rgb) / HABLE_DIV; -} - #endif /* Colorspace_h */ diff --git a/Sources/avifc/NEMath.h b/Sources/avifc/NEMath.h index f299e69..c3b4021 100644 --- a/Sources/avifc/NEMath.h +++ b/Sources/avifc/NEMath.h @@ -13,9 +13,11 @@ #if __arm64__ #include +#include +#include /* Logarithm polynomial coefficients */ -const std::array log_tab = +static const std::array log_tab = { { vdupq_n_f32(-2.29561495781f), @@ -141,6 +143,13 @@ static inline float32x4_t vlogq_f32(float32x4_t x) return poly; } +static inline float32x4_t vlog10q_f32(float32x4_t x) +{ + static const float32x4_t CONST_LN10 = vdupq_n_f32(2.30258509299); // ln(2) + const float32x4_t v = vlogq_f32(x); + return vdivq_f32(v, CONST_LN10); +} + __attribute__((always_inline)) static inline float32x4_t vpowq_f32(float32x4_t val, float32x4_t n) { diff --git a/Sources/avifc/PerceptualQuantinizer.mm b/Sources/avifc/PerceptualQuantinizer.mm index 29de743..fd01ecf 100644 --- a/Sources/avifc/PerceptualQuantinizer.mm +++ b/Sources/avifc/PerceptualQuantinizer.mm @@ -44,7 +44,7 @@ #import "NEMath.h" #import "Colorspace.h" -#import "Rec2408ToneMapper.hpp" +#import "ToneMap/Rec2408ToneMapper.hpp" #import "half.hpp" using namespace std; @@ -96,10 +96,6 @@ inline TriStim ClipToWhite(TriStim* c, const float* primaries) { return *c; } -float Luma(float r, float g, float b, const float* primaries) { - return r * primaries[0] + g * primaries[1] + b * primaries[2]; -} - float clampf(float value, float min, float max) { return fmin(fmax(value, min), max); } @@ -128,7 +124,7 @@ inline half loadHalf(uint16_t t) { return f; } -void TransferROW_U16HFloats(uint16_t *data, PQGammaCorrection gammaCorrection, const float* primaries, Rec2408ToneMapper* toneMapper) { +void TransferROW_U16HFloats(uint16_t *data, PQGammaCorrection gammaCorrection, const float* primaries, ToneMapper* toneMapper) { auto r = (float) loadHalf(data[0]); auto g = (float) loadHalf(data[1]); auto b = (float) loadHalf(data[2]); @@ -138,7 +134,7 @@ void TransferROW_U16HFloats(uint16_t *data, PQGammaCorrection gammaCorrection, c g = smpte.g; b = smpte.b; - toneMapper->toneMap(r, g, b); + toneMapper->Execute(r, g, b); if (gammaCorrection == Rec2020) { data[0] = half(clamp(LinearRec2020ToRec2020(r), 0.0f, 1.0f)).data_; @@ -209,7 +205,7 @@ inline float32x4_t GetPixelsRGBU8(const float32x4_t rgb, const float32x4_t maxCo inline float32x4x4_t Transfer(float32x4_t rChan, float32x4_t gChan, float32x4_t bChan, PQGammaCorrection gammaCorrection, - Rec2408ToneMapper* toneMapper) { + ToneMapper* toneMapper) { float32x4_t pqR = ToLinearPQ(rChan); float32x4_t pqG = ToLinearPQ(gChan); float32x4_t pqB = ToLinearPQ(bChan); @@ -219,7 +215,7 @@ inline float32x4x4_t Transfer(float32x4_t rChan, float32x4_t gChan, }; m = MatTransponseQF32(m); - float32x4x4_t r = toneMapper->toneMap(m); + float32x4x4_t r = toneMapper->Execute(m); if (gammaCorrection == Rec2020) { r.val[0] = vclampq_n_f32(LinearRec2020ToRec2020(r.val[0]), 0.0f, 1.0f); @@ -256,7 +252,7 @@ void TransferROW_U16(uint16_t *data, float maxColors, PQGammaCorrection gammaCor // data[2] = float_to_half((float) smpte.b * scale); } -void TransferROW_U8(uint8_t *data, float maxColors, PQGammaCorrection gammaCorrection, Rec2408ToneMapper* toneMapper) { +void TransferROW_U8(uint8_t *data, float maxColors, PQGammaCorrection gammaCorrection, ToneMapper* toneMapper) { auto r = (float) data[0] / (float) maxColors; auto g = (float) data[1] / (float) maxColors; auto b = (float) data[2] / (float) maxColors; @@ -266,7 +262,7 @@ void TransferROW_U8(uint8_t *data, float maxColors, PQGammaCorrection gammaCorre g = smpte.g; b = smpte.b; - toneMapper->toneMap(r, g, b); + toneMapper->Execute(r, g, b); if (gammaCorrection == Rec2020) { r = LinearRec2020ToRec2020(r); @@ -288,11 +284,9 @@ @implementation PerceptualQuantinizer : NSObject #if __arm64__ +(void)transferNEONF16:(nonnull uint8_t*)data stride:(int)stride width:(int)width height:(int)height depth:(int)depth primaries:(float*)primaries - space:(PQGammaCorrection)space components:(int)components { + space:(PQGammaCorrection)space components:(int)components toneMapper:(ToneMapper*)toneMapper { auto ptr = reinterpret_cast(data); - Rec2408ToneMapper* toneMapper = new Rec2408ToneMapper(1000.0f, 1.0f, sdrReferencePoint); - dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(height, concurrentQueue, ^(size_t y) { @@ -368,13 +362,12 @@ +(void)transferNEONF16:(nonnull uint8_t*)data stride:(int)stride width:(int)widt ptr16 += components; } }); - - delete toneMapper; } +(void)transferNEONU8:(nonnull uint8_t*)data stride:(int)stride width:(int)width height:(int)height depth:(int)depth - primaries:(float*)primaries space:(PQGammaCorrection)space components:(int)components { + primaries:(float*)primaries space:(PQGammaCorrection)space components:(int)components + toneMapper:(ToneMapper*)toneMapper { auto ptr = reinterpret_cast(data); const float32x4_t mask = {1.0f, 1.0f, 1.0f, 0.0}; @@ -386,8 +379,6 @@ +(void)transferNEONU8:(nonnull uint8_t*)data const float32x4_t vMaxColors = vdupq_n_f32(maxColors); - Rec2408ToneMapper* toneMapper = new Rec2408ToneMapper(1000.0f, 1.0f, sdrReferencePoint); - dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(height, concurrentQueue, ^(size_t y) { auto ptr16 = reinterpret_cast(ptr + y * stride); @@ -588,8 +579,6 @@ +(void)transferNEONU8:(nonnull uint8_t*)data ptr16 += components; } }); - - delete toneMapper; } #endif @@ -688,22 +677,23 @@ +(void)transfer:(nonnull uint8_t*)data stride:(int)stride width:(int)width heigh U16:(bool)U16 depth:(int)depth half:(bool)half primaries:(float*)primaries components:(int)components gammaCorrection:(PQGammaCorrection)gammaCorrection { auto ptr = reinterpret_cast(data); + ToneMapper* toneMapper = new Rec2408ToneMapper(1000.0f, 1.0f, sdrReferencePoint); #if __arm64__ if (U16 && half) { [self transferNEONF16:reinterpret_cast(data) stride:stride width:width height:height - depth:depth primaries:primaries space:gammaCorrection components:components]; + depth:depth primaries:primaries space:gammaCorrection components:components toneMapper:toneMapper]; + delete toneMapper; return; } if (!U16) { [self transferNEONU8:reinterpret_cast(data) stride:stride width:width height:height - depth:depth primaries:primaries space:gammaCorrection components:components]; + depth:depth primaries:primaries space:gammaCorrection components:components toneMapper:toneMapper]; + delete toneMapper; return; } #endif auto maxColors = powf(2, (float) depth) - 1; - Rec2408ToneMapper* toneMapper = new Rec2408ToneMapper(1000.0f, 1.0f, sdrReferencePoint); - dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(height, concurrentQueue, ^(size_t y) { if (U16) { diff --git a/Sources/avifc/ToneMap/AcesHillToneMapper.cpp b/Sources/avifc/ToneMap/AcesHillToneMapper.cpp new file mode 100644 index 0000000..aef7f5b --- /dev/null +++ b/Sources/avifc/ToneMap/AcesHillToneMapper.cpp @@ -0,0 +1,101 @@ +// +// AcesHillToneMapper.cpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#include "AcesHillToneMapper.hpp" + +float AcesCurve(const float Cin) { + const float a = Cin * (Cin + 0.0245786f) - 0.000090537f; + const float b = Cin * (0.983729f * Cin + 0.4329510f) + 0.238081f; + const float Cout = a / b; + return Cout; +} + +void AcesHillToneMapper::Execute(float& r, float& g, float& b) { + auto mulInput = [](float& r, float& g, float& b) { + float a1 = 0.59719f * r + 0.35458f * g + 0.04823f * b, + b1 = 0.07600f * r + 0.90834f * g + 0.01566f * b, + c1 = 0.02840f * r + 0.13383f * g + 0.83777f * b; + r = a1; + g = b1; + b = c1; + }; + + auto mulOutput = [](float& r, float& g, float& b) { + float a1 = 1.60475f * r - 0.53108f * g - 0.07367f * b, + b1 = -0.10208f * r + 1.10813f * g - 0.00605f * b, + c1 = -0.00327f * r - 0.07276f * g + 1.07602f * b; + r = a1; + g = b1; + b = c1; + }; + + // Fetch color + r = r*exposure; + g = g*exposure; + b = b*exposure; + + // Apply curve directly on color input + mulInput(r,g, b); + r = AcesCurve(r); + g = AcesCurve(g); + b = AcesCurve(b); + mulOutput(r, g, b); +} + +#if __arm64__ + +__attribute__((always_inline)) +static inline float vsumq_f32A(const float32x4_t v) { + float32x2_t r = vadd_f32(vget_high_f32(v), vget_low_f32(v)); + return vget_lane_f32(vpadd_f32(r, r), 0); +} + +inline float32x4_t AcesInputMul(const float32x4_t m) { + const float32x4_t row1 = { 0.59719f, 0.35458f, 0.04823f, 0.0f }; + const float32x4_t row2 = { 0.07600f, 0.90834f, 0.01566f, 0.0f }; + const float32x4_t row3 = { 0.02840f, 0.13383f, 0.83777f, 0.0f }; + const float rNew = vsumq_f32A(vmulq_f32(m, row1)); + const float gNew = vsumq_f32A(vmulq_f32(m, row2)); + const float bNew = vsumq_f32A(vmulq_f32(m, row3)); + float32x4_t v = { rNew, gNew, bNew }; + return v; +} + +inline float32x4_t AcesOutputMul(const float32x4_t m) { + const float32x4_t row1 = { 1.60475f, -0.53108f, -0.07367f, 0.0f }; + const float32x4_t row2 = { -0.10208f, 1.10813f, -0.00605f, 0.0f }; + const float32x4_t row3 = { -0.00327f, -0.07276f, 1.07602f, 0.0f }; + const float rNew = vsumq_f32A(vmulq_f32(m, row1)); + const float gNew = vsumq_f32A(vmulq_f32(m, row2)); + const float bNew = vsumq_f32A(vmulq_f32(m, row3)); + float32x4_t v = { rNew, gNew, bNew }; + return v; +} + +inline float32x4_t AcesCurve(const float32x4_t Cin) { + const float32x4_t a = vsubq_f32(vmulq_f32(vaddq_f32(Cin, vdupq_n_f32(0.0245786f)), Cin), vdupq_n_f32(0.000090537f)); + const float32x4_t b = vmlaq_f32(vdupq_n_f32(0.238081f), vmlaq_f32(vdupq_n_f32(0.4329510f), Cin, vdupq_n_f32(0.983729f)), Cin); + float32x4_t Cout = vdivq_f32(a, b); + return Cout; +} + +float32x4_t AcesHillToneMapper::Execute(const float32x4_t m) { + float32x4_t vm = vmulq_n_f32(AcesInputMul(m), exposure); + vm = AcesCurve(vm); + return AcesOutputMul(vm); +} + +float32x4x4_t AcesHillToneMapper::Execute(const float32x4x4_t m) { + float32x4x4_t r = { + this->Execute(m.val[0]), + this->Execute(m.val[1]), + this->Execute(m.val[2]), + this->Execute(m.val[3]), + }; + return r; +} +#endif diff --git a/Sources/avifc/ToneMap/AcesHillToneMapper.hpp b/Sources/avifc/ToneMap/AcesHillToneMapper.hpp new file mode 100644 index 0000000..adce6c8 --- /dev/null +++ b/Sources/avifc/ToneMap/AcesHillToneMapper.hpp @@ -0,0 +1,37 @@ +// +// AcesHillToneMapper.hpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#ifndef AcesHillToneMapper_hpp +#define AcesHillToneMapper_hpp + +#include +#include "ToneMapper.hpp" + +#if __arm64__ +#include +#endif + +class AcesHillToneMapper: public ToneMapper { +public: + AcesHillToneMapper(): exposure(1.5f) { + + } + + ~AcesHillToneMapper() { + + } + + void Execute(float &r, float &g, float &b) override; +#if __arm64__ + float32x4_t Execute(const float32x4_t m) override; + float32x4x4_t Execute(const float32x4x4_t m) override; +#endif +private: + const float exposure; +}; + +#endif /* AcesHillToneMapper_hpp */ diff --git a/Sources/avifc/ToneMap/HableFilmicToneMapper.cpp b/Sources/avifc/ToneMap/HableFilmicToneMapper.cpp new file mode 100644 index 0000000..a46ab00 --- /dev/null +++ b/Sources/avifc/ToneMap/HableFilmicToneMapper.cpp @@ -0,0 +1,46 @@ +// +// HableFilmicToneMapper.cpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#include "HableFilmicToneMapper.hpp" + +float HableFilmicToneMapper::hable(const float x) +{ + const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30; + return ((x * (A * x + (C * B)) + (D * E)) / (x * (A * x + B) + (D * F))) - E / F; +} + +float HableFilmicToneMapper::hableFilmic(float v) +{ + float curr = hable(v * exposure); + return curr * whiteScale; +} + +void HableFilmicToneMapper::Execute(float& r, float& g, float &b) { + r = hableFilmic(r); + g = hableFilmic(g); + b = hableFilmic(b); +} + +#if __arm64__ +float32x4_t HableFilmicToneMapper::Execute(const float32x4_t m) { + const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30; + const float32x4_t v = vmulq_n_f32(m, exposure); + const float32x4_t den = vaddq_f32(vmulq_f32(v, vaddq_f32(vmulq_n_f32(v, A), vdupq_n_f32(C*B))), vdupq_n_f32(D*E)); + const float32x4_t num = vaddq_f32(vmulq_f32(vmlaq_f32(vdupq_n_f32(B), v, vdupq_n_f32(A)), v), vdupq_n_f32(D*F)); + return vmulq_n_f32(vsubq_f32(vdivq_f32(den, num), vdupq_n_f32(E/F)), whiteScale); +} + +float32x4x4_t HableFilmicToneMapper::Execute(const float32x4x4_t m) { + float32x4x4_t r = { + this->Execute(m.val[0]), + this->Execute(m.val[1]), + this->Execute(m.val[2]), + this->Execute(m.val[3]), + }; + return r; +} +#endif diff --git a/Sources/avifc/ToneMap/HableFilmicToneMapper.hpp b/Sources/avifc/ToneMap/HableFilmicToneMapper.hpp new file mode 100644 index 0000000..fc91cab --- /dev/null +++ b/Sources/avifc/ToneMap/HableFilmicToneMapper.hpp @@ -0,0 +1,44 @@ +// +// HableFilmicToneMapper.hpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#ifndef HableFilmicToneMapper_hpp +#define HableFilmicToneMapper_hpp + +#include +#include "ToneMapper.hpp" + +#if __arm64__ +#include +#endif + +class HableFilmicToneMapper: public ToneMapper { +public: + HableFilmicToneMapper(): exposure(2.0f) { + + } + + ~HableFilmicToneMapper() { + + } + + void Execute(float &r, float &g, float &b) override; +#if __arm64__ + float32x4_t Execute(const float32x4_t m) override; + float32x4x4_t Execute(const float32x4x4_t m) override; +#endif +private: + float hableFilmic(float v); + float hable(const float x); + const float sig = hable(4.8f); + const float whiteScale = 1.0f / hable(11.2f); + float exposure; +#if __arm64__ + const float32x4_t sigVec = vdupq_n_f32(hable(4.8f)); +#endif +}; + +#endif /* HableFilmicToneMapper_hpp */ diff --git a/Sources/avifc/ToneMap/HableToneMapper.cpp b/Sources/avifc/ToneMap/HableToneMapper.cpp new file mode 100644 index 0000000..d95c3c2 --- /dev/null +++ b/Sources/avifc/ToneMap/HableToneMapper.cpp @@ -0,0 +1,39 @@ +// +// HableToneMapper.cpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#include "HableToneMapper.hpp" + +float HableToneMapper::hable(const float x) +{ + const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30; + return ((x * (A * x + (C * B)) + (D * E)) / (x * (A * x + B) + (D * F))) - E / F; +} + +void HableToneMapper::Execute(float& r, float& g, float &b) { + r = hable(r) / sig; + g = hable(g) / sig; + b = hable(b) / sig; +} + +#if __arm64__ +float32x4_t HableToneMapper::Execute(const float32x4_t m) { + const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30; + const float32x4_t den = vaddq_f32(vmulq_f32(m, vaddq_f32(vmulq_n_f32(m, A), vdupq_n_f32(C*B))), vdupq_n_f32(D*E)); + const float32x4_t num = vaddq_f32(vmulq_f32(vmlaq_f32(vdupq_n_f32(B), m, vdupq_n_f32(A)), m), vdupq_n_f32(D*F)); + return vdivq_f32(vsubq_f32(vdivq_f32(den, num), vdupq_n_f32(E/F)), sigVec); +} + +float32x4x4_t HableToneMapper::Execute(const float32x4x4_t m) { + float32x4x4_t r = { + this->Execute(m.val[0]), + this->Execute(m.val[1]), + this->Execute(m.val[2]), + this->Execute(m.val[3]), + }; + return r; +} +#endif diff --git a/Sources/avifc/ToneMap/HableToneMapper.hpp b/Sources/avifc/ToneMap/HableToneMapper.hpp new file mode 100644 index 0000000..569d14a --- /dev/null +++ b/Sources/avifc/ToneMap/HableToneMapper.hpp @@ -0,0 +1,41 @@ +// +// HableToneMapper.hpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#ifndef HableToneMapper_hpp +#define HableToneMapper_hpp + +#include +#include "ToneMapper.hpp" + +#if __arm64__ +#include +#endif + +class HableToneMapper: public ToneMapper { +public: + HableToneMapper() { + + } + + ~HableToneMapper() { + + } + + void Execute(float &r, float &g, float &b) override; +#if __arm64__ + float32x4_t Execute(const float32x4_t m) override; + float32x4x4_t Execute(const float32x4x4_t m) override; +#endif +private: + float hable(const float x); + const float sig = hable(4.8f); +#if __arm64__ + const float32x4_t sigVec = vdupq_n_f32(hable(4.8f)); +#endif +}; + +#endif /* HableToneMapper_hpp */ diff --git a/Sources/avifc/ToneMap/LogarithmicToneMapper.cpp b/Sources/avifc/ToneMap/LogarithmicToneMapper.cpp new file mode 100644 index 0000000..3b73fb3 --- /dev/null +++ b/Sources/avifc/ToneMap/LogarithmicToneMapper.cpp @@ -0,0 +1,46 @@ +// +// LogarithmicToneMapper.cpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#include "LogarithmicToneMapper.hpp" +#include + +float LogarithmicToneMapper::Luma(const float r, const float g, const float b) { + return r * lumaVec[0] + g * lumaVec[1] + b * lumaVec[2]; +} + +void LogarithmicToneMapper::Execute(float& r, float& g, float &b) { + const float Lmax_ = exposure * LMax; + const float Lin = Luma(r, g, b); + float Lout = log10(1.0 + curve * Lin) / log10(1.0 + curve * Lmax_); +} + +#if __arm64__ + +__attribute__((always_inline)) +static inline float vsumq_f32LG(const float32x4_t v) { + float32x2_t r = vadd_f32(vget_high_f32(v), vget_low_f32(v)); + return vget_lane_f32(vpadd_f32(r, r), 0); +} + +float32x4_t LogarithmicToneMapper::Execute(const float32x4_t m) { + const float Lmax_ = exposure * LMax; + const float Lin = vsumq_f32LG(vmulq_f32(m, vLumaVec)); + const float Lout = log10(1.0 + curve * Lin) / log10(1.0 + curve * Lmax_); + const float scale = Lout / Lin; + return vmulq_n_f32(m, scale); +} + +float32x4x4_t LogarithmicToneMapper::Execute(const float32x4x4_t m) { + float32x4x4_t r = { + this->Execute(m.val[0]), + this->Execute(m.val[1]), + this->Execute(m.val[2]), + this->Execute(m.val[3]), + }; + return r; +} +#endif diff --git a/Sources/avifc/ToneMap/LogarithmicToneMapper.hpp b/Sources/avifc/ToneMap/LogarithmicToneMapper.hpp new file mode 100644 index 0000000..c5888ab --- /dev/null +++ b/Sources/avifc/ToneMap/LogarithmicToneMapper.hpp @@ -0,0 +1,44 @@ +// +// LogarithmicToneMapper.hpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#ifndef LogarithmicToneMapper_hpp +#define LogarithmicToneMapper_hpp + +#include +#include "ToneMapper.hpp" + +#if __arm64__ +#include +#endif + +class LogarithmicToneMapper: public ToneMapper { +public: + LogarithmicToneMapper(): lumaVec { 0.2126, 0.7152, 0.0722 }, curve(1.0f), exposure(1.5f), LMax(1.0f) { + + } + + ~LogarithmicToneMapper() { + + } + + void Execute(float &r, float &g, float &b) override; +#if __arm64__ + float32x4_t Execute(const float32x4_t m) override; + float32x4x4_t Execute(const float32x4x4_t m) override; +#endif +private: + const float exposure; + const float LMax; + const float lumaVec[3] = { 0.2126, 0.7152, 0.0722 }; + const float curve; + float Luma(const float r, const float g, const float b); +#if __arm64__ + const float32x4_t vLumaVec = { lumaVec[0], lumaVec[1], lumaVec[2], 0.0f }; +#endif +}; + +#endif /* LogarithmicToneMapper_hpp */ diff --git a/Sources/avifc/ToneMap/MobiusToneMapper.cpp b/Sources/avifc/ToneMap/MobiusToneMapper.cpp new file mode 100644 index 0000000..e656222 --- /dev/null +++ b/Sources/avifc/ToneMap/MobiusToneMapper.cpp @@ -0,0 +1,52 @@ +// +// MobiusToneMapper.cpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#include "MobiusToneMapper.hpp" +#include + +using namespace std; + +float MobiusToneMapper::mobius(const float x) +{ + const float in = x * exposure; + const float j = transition; + if( in <= j ) + return in; + const float a = -j * j * ( peak - 1.0f ) / ( j * j - 2.0f * j + peak ); + const float b = ( j * j - 2.0f * j * peak + peak ) / max( peak - 1.0f, 1e-6f ); + return ( b * b + 2.0f * b * j + j * j ) / ( b - a ) * ( in + a ) / ( in + b ); +} + +#if __arm64__ +float32x4_t MobiusToneMapper::Execute(const float32x4_t m) { + const float32x4_t in = vmulq_n_f32(m, exposure); + uint32x4_t maskHigh = vcltq_f32(m, vdupq_n_f32(transition)); + float j = transition; + const float a = -j * j * ( peak - 1.0f ) / ( j * j - 2.0f * j + peak ); + const float b = ( j * j - 2.0f * j * peak + peak ) / max( peak - 1.0f, 1e-6f ); + const float32x4_t av = vdupq_n_f32(a); + const float32x4_t bv = vdupq_n_f32(b); + return vdivq_f32(vmulq_f32(vaddq_f32(in, av), vdupq_n_f32(( b * b + 2.0f * b * j + j * j ) / ( b - a ))), vaddq_f32(in, bv)); +} + +float32x4x4_t MobiusToneMapper::Execute(const float32x4x4_t m) { + float32x4x4_t r = { + Execute(m.val[0]), + Execute(m.val[1]), + Execute(m.val[2]), + Execute(m.val[3]) + }; + return r; +} + +#endif + +void MobiusToneMapper::Execute(float &r, float &g, float &b) { + r = mobius(r); + g = mobius(g); + b = mobius(b); +} diff --git a/Sources/avifc/ToneMap/MobiusToneMapper.hpp b/Sources/avifc/ToneMap/MobiusToneMapper.hpp new file mode 100644 index 0000000..b3e2c14 --- /dev/null +++ b/Sources/avifc/ToneMap/MobiusToneMapper.hpp @@ -0,0 +1,45 @@ +// +// MobiusToneMapper.hpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#ifndef MobiusToneMapper_hpp +#define MobiusToneMapper_hpp + +#include "ToneMapper.hpp" +#include + +#if __arm64__ +#include +#endif + +class MobiusToneMapper: public ToneMapper { +public: + MobiusToneMapper(const float exposure = 1.2f, const float transition = 0.9f, const float peak = 1.0f): ToneMapper() { + this->exposure = exposure; + this->transition = transition; + this->peak = peak; + } + + ~MobiusToneMapper() { + + } + + void Execute(float &r, float &g, float &b) override; + +#if __arm64__ + float32x4_t Execute(const float32x4_t m) override; + float32x4x4_t Execute(const float32x4x4_t m) override; +#endif + +private: + float exposure; + float transition; + float peak; + + float mobius(const float x); +}; + +#endif /* MobiusToneMapper_hpp */ diff --git a/Sources/avifc/Rec2408ToneMapper.cpp b/Sources/avifc/ToneMap/Rec2408ToneMapper.cpp similarity index 72% rename from Sources/avifc/Rec2408ToneMapper.cpp rename to Sources/avifc/ToneMap/Rec2408ToneMapper.cpp index 1e7c322..27ebabb 100644 --- a/Sources/avifc/Rec2408ToneMapper.cpp +++ b/Sources/avifc/ToneMap/Rec2408ToneMapper.cpp @@ -10,19 +10,6 @@ using namespace std; -inline float hable(float x) -{ - const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30; - - return ((x * (A * x + (C * B)) + (D * E)) / (x * (A * x + B) + (D * F))) - E / F; -} - -inline float ToneMappingHable(const float rgb) -{ - static const float HABLE_DIV = hable(4.8); - return hable(rgb) / HABLE_DIV; -} - #if __arm64__ __attribute__((always_inline)) @@ -31,8 +18,8 @@ inline float vsumq_f32(const float32x4_t v) { return vget_lane_f32(vpadd_f32(r, r), 0); } -float32x4x4_t Rec2408ToneMapper::toneMap(const float32x4x4_t m) { - const float32x4_t maximum = { +float32x4x4_t Rec2408ToneMapper::Execute(const float32x4x4_t m) { + const float32x4_t maximum = { vsumq_f32(vmulq_f32(m.val[0], this->luma)), vsumq_f32(vmulq_f32(m.val[1], this->luma)), vsumq_f32(vmulq_f32(m.val[2], this->luma)), @@ -49,7 +36,7 @@ float32x4x4_t Rec2408ToneMapper::toneMap(const float32x4x4_t m) { return r; } -float32x4_t Rec2408ToneMapper::toneMap(const float32x4_t m) { +float32x4_t Rec2408ToneMapper::Execute(const float32x4_t m) { const float maximum = vsumq_f32(vmulq_f32(m, this->luma)); const float shScale = (1.f + this->a * maximum) / (1.f + this->b * maximum); return vmulq_n_f32(m, shScale); @@ -57,7 +44,7 @@ float32x4_t Rec2408ToneMapper::toneMap(const float32x4_t m) { #endif -void Rec2408ToneMapper::toneMap(float& r, float &g, float& b) { +void Rec2408ToneMapper::Execute(float& r, float &g, float& b) { const float maximum = r*0.2627 + g*0.6780 + b * 0.0593; if (maximum > 0) { const float shScale = (1.f + this->a * maximum) / (1.f + this->b * maximum); diff --git a/Sources/avifc/Rec2408ToneMapper.hpp b/Sources/avifc/ToneMap/Rec2408ToneMapper.hpp similarity index 62% rename from Sources/avifc/Rec2408ToneMapper.hpp rename to Sources/avifc/ToneMap/Rec2408ToneMapper.hpp index 7b8ab8c..98739fa 100644 --- a/Sources/avifc/Rec2408ToneMapper.hpp +++ b/Sources/avifc/ToneMap/Rec2408ToneMapper.hpp @@ -8,15 +8,23 @@ #ifndef Rec2408ToneMapper_hpp #define Rec2408ToneMapper_hpp +#include "ToneMapper.hpp" +#include "HableToneMapper.hpp" +#include "MobiusToneMapper.hpp" +#include "ReinhardToneMapper.hpp" +#include "HableFilmicToneMapper.hpp" +#include "AcesHillToneMapper.hpp" +#include "LogarithmicToneMapper.hpp" + #include #if __arm64__ #include #endif -class Rec2408ToneMapper { +class Rec2408ToneMapper: public ToneMapper { public: - Rec2408ToneMapper(const float contentMaxBrightness, const float displayMaxBrightness, const float whitePoint) { + Rec2408ToneMapper(const float contentMaxBrightness, const float displayMaxBrightness, const float whitePoint): ToneMapper() { this->Ld = contentMaxBrightness / whitePoint; this->a = displayMaxBrightness / (Ld*Ld); this->b = 1.0f / displayMaxBrightness; @@ -27,11 +35,10 @@ class Rec2408ToneMapper { #endif } - void toneMap(float& r, float &g, float& b); - + void Execute(float &r, float &g, float &b) override; #if __arm64__ - float32x4x4_t toneMap(const float32x4x4_t m); - float32x4_t toneMap(const float32x4_t m); + float32x4_t Execute(const float32x4_t m) override; + float32x4x4_t Execute(const float32x4x4_t m) override; #endif private: diff --git a/Sources/avifc/ToneMap/ReinhardToneMapper.cpp b/Sources/avifc/ToneMap/ReinhardToneMapper.cpp new file mode 100644 index 0000000..e175e44 --- /dev/null +++ b/Sources/avifc/ToneMap/ReinhardToneMapper.cpp @@ -0,0 +1,60 @@ +// +// ReinhardToneMapper.cpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#include "ReinhardToneMapper.hpp" + +float ReinhardToneMapper::Luma(const float r, const float g, const float b) { + return r * lumaVec[0] + g * lumaVec[1] + b * lumaVec[2]; +} + +float ReinhardToneMapper::reinhard(const float v) { + float Ld = (exposure * v * (1 + (v / (lumaMaximum * lumaMaximum)))) / (1 + exposure * v); + return Ld; +// return v / (1.0f + v); +} + +void ReinhardToneMapper::Execute(float& r, float& g, float& b) { + const float luma = Luma(r, g, b); + if (luma == 0) { + return; + } + const float reinhardLuma = reinhard(luma); + const float scale = reinhardLuma / luma; + + r = r * scale; + g = g * scale; + b = b * scale; +} + +#if __arm64__ + +__attribute__((always_inline)) +static inline float vsumq_f32R(const float32x4_t v) { + float32x2_t r = vadd_f32(vget_high_f32(v), vget_low_f32(v)); + return vget_lane_f32(vpadd_f32(r, r), 0); +} + +float32x4_t ReinhardToneMapper::Execute(const float32x4_t m) { + const float luma = vsumq_f32R(vmulq_f32(m, vLumaVec)); + if (luma == 0) { + return m; + } + const float reinhardLuma = reinhard(luma); + const float scale = reinhardLuma / luma; + return vmulq_n_f32(m, scale); +} + +float32x4x4_t ReinhardToneMapper::Execute(const float32x4x4_t m) { + float32x4x4_t r = { + this->Execute(m.val[0]), + this->Execute(m.val[1]), + this->Execute(m.val[2]), + this->Execute(m.val[3]), + }; + return r; +} +#endif diff --git a/Sources/avifc/ToneMap/ReinhardToneMapper.hpp b/Sources/avifc/ToneMap/ReinhardToneMapper.hpp new file mode 100644 index 0000000..050b73a --- /dev/null +++ b/Sources/avifc/ToneMap/ReinhardToneMapper.hpp @@ -0,0 +1,43 @@ +// +// ReinhardToneMapper.hpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#ifndef ReinhardToneMapper_hpp +#define ReinhardToneMapper_hpp + +#include "ToneMapper.hpp" +#include +#if __arm64__ +#include +#endif +#include + +class ReinhardToneMapper: public ToneMapper { +public: + ReinhardToneMapper(): lumaVec { 0.2126, 0.7152, 0.0722 }, lumaMaximum(1.0f), exposure(1.2f) { + + } + ~ReinhardToneMapper() { + + } + + void Execute(float &r, float &g, float &b) override; +#if __arm64__ + float32x4_t Execute(const float32x4_t m) override; + float32x4x4_t Execute(const float32x4x4_t m) override; +#endif +private: + float reinhard(const float v); + const float lumaVec[3] = { 0.2126, 0.7152, 0.0722 }; +#if __arm64__ + const float32x4_t vLumaVec = { lumaVec[0], lumaVec[1], lumaVec[2], 0.0f }; +#endif + float Luma(const float r, const float g, const float bs); + const float lumaMaximum; + const float exposure; +}; + +#endif /* ReinhardToneMapper_hpp */ diff --git a/Sources/avifc/ToneMap/ToneMapper.cpp b/Sources/avifc/ToneMap/ToneMapper.cpp new file mode 100644 index 0000000..d81739b --- /dev/null +++ b/Sources/avifc/ToneMap/ToneMapper.cpp @@ -0,0 +1,8 @@ +// +// ToneMapper.cpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#include "ToneMapper.hpp" diff --git a/Sources/avifc/ToneMap/ToneMapper.hpp b/Sources/avifc/ToneMap/ToneMapper.hpp new file mode 100644 index 0000000..06d05f1 --- /dev/null +++ b/Sources/avifc/ToneMap/ToneMapper.hpp @@ -0,0 +1,32 @@ +// +// ToneMapper.hpp +// +// +// Created by Radzivon Bartoshyk on 09/10/2023. +// + +#ifndef ToneMapper_hpp +#define ToneMapper_hpp + +#include + +#if __arm64__ +#include +#endif + +class ToneMapper { + +public: + ToneMapper() { + + } + virtual ~ToneMapper() { }; + virtual void Execute(float& r, float& g, float& b) = 0; + +#if __arm64__ + virtual float32x4x4_t Execute(const float32x4x4_t m) = 0; + virtual float32x4_t Execute(const float32x4_t m) = 0; +#endif +}; + +#endif /* ToneMapper_hpp */