Skip to content

Commit ec723d6

Browse files
committed
Improved tone mapping
1 parent 782ead1 commit ec723d6

File tree

11 files changed

+220
-46
lines changed

11 files changed

+220
-46
lines changed

app/src/main/java/com/radzivon/bartoshyk/avif/MainActivity.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class MainActivity : AppCompatActivity() {
122122
var allFiles = mutableListOf<String>()
123123
allFiles.addAll(allFiles2)
124124
allFiles.addAll(allFiles1)
125-
// allFiles = allFiles.filter { it.contains("club.avif") }.toMutableList()
125+
allFiles = allFiles.filter { it.contains("wide_gamut.avif") || it.contains("IMG_0199_rr.avif") || it.contains("bt_2020_pq.avif") }.toMutableList()
126126
// allFiles = allFiles.filter { it.contains("bbb_alpha_inverted.avif") }.toMutableList()
127127
for (file in allFiles) {
128128
try {

avif-coder/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ afterEvaluate {
4242
create<MavenPublication>("mavenJava") {
4343
groupId = "com.github.awxkee"
4444
artifactId = "avif-coder"
45-
version = "2.0.6"
45+
version = "2.0.8"
4646
from(components["release"])
4747
// artifact("androidSourcesJar")
4848
}

avif-coder/src/main/cpp/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ add_library(coder SHARED JniEncoder.cpp JniException.cpp SizeScaler.cpp
1919
colorspace/Trc.cpp YuvConversion.cpp
2020
colorspace/Rec2408ToneMapper.cpp colorspace/LogarithmicToneMapper.cpp
2121
colorspace/ColorMatrix.cpp imagebits/ScanAlpha.cpp imagebits/Rgba16.cpp
22-
AvifDecoderController.cpp HeifImageDecoder.cpp JniAnimatedController.cpp)
22+
AvifDecoderController.cpp HeifImageDecoder.cpp JniAnimatedController.cpp
23+
colorspace/FilmicToneMapper.cpp)
2324

2425
add_library(libheif SHARED IMPORTED)
2526
add_library(libyuv STATIC IMPORTED)

avif-coder/src/main/cpp/colorspace/ColorMatrix.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "definitions.h"
3232
#include "Rec2408ToneMapper.h"
3333
#include "LogarithmicToneMapper.h"
34+
#include "FilmicToneMapper.h"
3435
#include "ITUR.h"
3536

3637
void applyColorMatrix(uint8_t *inPlace, uint32_t stride, uint32_t width, uint32_t height,
@@ -80,6 +81,9 @@ void applyColorMatrix(uint8_t *inPlace, uint32_t stride, uint32_t width, uint32_
8081
} else if (toneMapper == CurveToneMapper::LOGARITHMIC) {
8182
LogarithmicToneMapper mToneMapper(mCoeffs);
8283
mToneMapper.transferTone(rowVector.data(), width);
84+
} else if (toneMapper == CurveToneMapper::FILMIC) {
85+
FilmicToneMapper mToneMapper;
86+
mToneMapper.transferTone(rowVector.data(), width);
8387
}
8488

8589
float *iter = rowVector.data();
@@ -162,7 +166,8 @@ void applyColorMatrix16Bit(uint16_t *inPlace,
162166

163167
concurrency::parallel_for(6, height, [&](uint32_t y) {
164168
aligned_float_vector rowVector(width * 3);
165-
auto sourceRow = reinterpret_cast<uint16_t *>(reinterpret_cast<uint8_t * >(inPlace) + y * stride);
169+
auto sourceRow =
170+
reinterpret_cast<uint16_t *>(reinterpret_cast<uint8_t * >(inPlace) + y * stride);
166171

167172
float *rowData = rowVector.data();
168173

@@ -181,6 +186,9 @@ void applyColorMatrix16Bit(uint16_t *inPlace,
181186
} else if (toneMapper == CurveToneMapper::LOGARITHMIC) {
182187
LogarithmicToneMapper mToneMapper(mCoeffs);
183188
mToneMapper.transferTone(rowVector.data(), width);
189+
} else if (toneMapper == CurveToneMapper::FILMIC) {
190+
FilmicToneMapper mToneMapper;
191+
mToneMapper.transferTone(rowVector.data(), width);
184192
}
185193

186194
float *iter = rowVector.data();
@@ -214,7 +222,6 @@ void applyColorMatrix16Bit(uint16_t *inPlace,
214222
static_cast<uint16_t >(std::clamp(iter[2], 0.f, 1.0f) * cutOffColors),
215223
static_cast<uint16_t>(cutOffColors));
216224

217-
218225
sourceRow[0] = gammaMap[scaledValue0];
219226
sourceRow[1] = gammaMap[scaledValue1];
220227
sourceRow[2] = gammaMap[scaledValue2];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2024 Radzivon Bartoshyk
5+
* avif-coder [https://github.com/awxkee/avif-coder]
6+
*
7+
* Created by Radzivon Bartoshyk on 10/11/2024
8+
*
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy
10+
* of this software and associated documentation files (the "Software"), to deal
11+
* in the Software without restriction, including without limitation the rights
12+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
* copies of the Software, and to permit persons to whom the Software is
14+
* furnished to do so, subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included in all
17+
* copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
* SOFTWARE.
26+
*
27+
*/
28+
29+
30+
#include "FilmicToneMapper.h"
31+
#include "Oklab.hpp"
32+
33+
void FilmicToneMapper::transferTone(float *inPlace, uint32_t width) {
34+
float *targetPlace = inPlace;
35+
36+
for (uint32_t x = 0; x < width; ++x) {
37+
float r = targetPlace[0];
38+
float g = targetPlace[1];
39+
float b = targetPlace[2];
40+
coder::Oklab oklab = coder::Oklab::fromLinearRGB(r, g, b);
41+
if (oklab.L == 0) {
42+
continue;
43+
}
44+
float shScale = this->uncharted2_filmic(oklab.L) / oklab.L;
45+
oklab.L = oklab.L * shScale;
46+
coder::Rgb linearRgb = oklab.toLinearRGB();
47+
targetPlace[0] = std::min(linearRgb.r, 1.f);
48+
targetPlace[1] = std::min(linearRgb.g, 1.f);
49+
targetPlace[2] = std::min(linearRgb.b, 1.f);
50+
targetPlace += 3;
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2024 Radzivon Bartoshyk
5+
* avif-coder [https://github.com/awxkee/avif-coder]
6+
*
7+
* Created by Radzivon Bartoshyk on 10/11/2024
8+
*
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy
10+
* of this software and associated documentation files (the "Software"), to deal
11+
* in the Software without restriction, including without limitation the rights
12+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
* copies of the Software, and to permit persons to whom the Software is
14+
* furnished to do so, subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included in all
17+
* copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
* SOFTWARE.
26+
*
27+
*/
28+
29+
#ifndef AVIF_FILMIC_TONEMAPPER_H_
30+
#define AVIF_FILMIC_TONEMAPPER_H_
31+
32+
#include <vector>
33+
34+
class FilmicToneMapper {
35+
public:
36+
FilmicToneMapper() {
37+
}
38+
39+
void transferTone(float *inPlace, uint32_t width);
40+
41+
private:
42+
43+
float uncharted2_tonemap_partial(float x) {
44+
float A = 0.15f;
45+
float B = 0.50f;
46+
float C = 0.10f;
47+
float D = 0.20f;
48+
float E = 0.02f;
49+
float F = 0.30f;
50+
return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F;
51+
}
52+
53+
float uncharted2_filmic(float v) {
54+
float exposure_bias = 2.0f;
55+
float curr = uncharted2_tonemap_partial(v * exposure_bias);
56+
57+
float W = 11.2f;
58+
float white_scale = 1.0f / uncharted2_tonemap_partial(W);
59+
return curr * white_scale;
60+
}
61+
62+
};
63+
64+
#endif //AVIF_FILMIC_TONEMAPPER_H_

avif-coder/src/main/cpp/colorspace/LogarithmicToneMapper.cpp

+19-24
Original file line numberDiff line numberDiff line change
@@ -27,33 +27,28 @@
2727
*/
2828

2929
#include "LogarithmicToneMapper.h"
30+
#include "Oklab.hpp"
3031

3132
void LogarithmicToneMapper::transferTone(float *inPlace, uint32_t width) {
32-
float *targetPlace = inPlace;
33+
float *targetPlace = inPlace;
3334

34-
const float primaryR = this->lumaPrimaries[0];
35-
const float primaryG = this->lumaPrimaries[1];
36-
const float primaryB = this->lumaPrimaries[2];
35+
const float vDen = this->den;
3736

38-
const float vDen = this->den;
39-
40-
for (uint32_t x = 0; x < width; ++x) {
41-
float r = targetPlace[0];
42-
float g = targetPlace[1];
43-
float b = targetPlace[2];
44-
float Lin =
45-
r * primaryR + g * primaryG + b * primaryB;
46-
if (Lin == 0) {
47-
continue;
48-
}
49-
float Lout = std::logf(std::abs(1 + Lin)) * vDen;
50-
float shScale = Lout / Lin;
51-
r = r * shScale;
52-
g = g * shScale;
53-
b = b * shScale;
54-
targetPlace[0] = std::min(r, 1.f);
55-
targetPlace[1] = std::min(g, 1.f);
56-
targetPlace[2] = std::min(b, 1.f);
57-
targetPlace += 3;
37+
for (uint32_t x = 0; x < width; ++x) {
38+
float r = targetPlace[0];
39+
float g = targetPlace[1];
40+
float b = targetPlace[2];
41+
coder::Oklab oklab = coder::Oklab::fromLinearRGB(r, g, b);
42+
if (oklab.L == 0) {
43+
continue;
5844
}
45+
float Lout = std::logf(std::abs(1.f + oklab.L)) * vDen;
46+
float shScale = Lout / oklab.L;
47+
oklab.L = oklab.L * shScale;
48+
coder::Rgb linearRgb = oklab.toLinearRGB();
49+
targetPlace[0] = std::min(linearRgb.r, 1.f);
50+
targetPlace[1] = std::min(linearRgb.g, 1.f);
51+
targetPlace[2] = std::min(linearRgb.b, 1.f);
52+
targetPlace += 3;
53+
}
5954
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//
2+
// Created by Radzivon Bartoshyk on 10/11/2024.
3+
//
4+
5+
#ifndef AVIF_OKLAB
6+
#define AVIF_OKLAB
7+
8+
#include <cmath>
9+
10+
namespace coder {
11+
class Rgb {
12+
public:
13+
float r, g, b;
14+
Rgb(float r_, float g_, float b_) : r(r_), g(g_), b(b_) {}
15+
};
16+
17+
class Oklab {
18+
public:
19+
// Public member variables for Oklab components
20+
float L, a, b;
21+
22+
// Constructor to initialize Oklab color components
23+
constexpr Oklab(float l = 0.0f, float a_ = 0.0f, float b_ = 0.0f)
24+
: L(l), a(a_), b(b_) {}
25+
26+
[[nodiscard]] static constexpr Oklab fromLinearRGB(float r, float g, float rgb_b) {
27+
float l = 0.4122214708f * r + 0.5363325363f * g + 0.0514459929f * rgb_b;
28+
float m = 0.2119034982f * r + 0.6806995451f * g + 0.1073969566f * rgb_b;
29+
float s = 0.0883024619f * r + 0.2817188376f * g + 0.6299787005f * rgb_b;
30+
31+
float l_ = std::cbrtf(l);
32+
float m_ = std::cbrtf(m);
33+
float s_ = std::cbrtf(s);
34+
35+
float oklab_l = 0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_;
36+
float oklab_a = 1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_;
37+
float oklab_b = 0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_;
38+
39+
return {oklab_l, oklab_a, oklab_b};
40+
}
41+
42+
[[nodiscard]] Rgb toLinearRGB() const {
43+
float l_ = this->L + 0.3963377774f * this->a + 0.2158037573f * this->b;
44+
float m_ = this->L - 0.1055613458f * this->a - 0.0638541728f * this->b;
45+
float s_ = this->L - 0.0894841775f * this->a - 1.2914855480f * this->b;
46+
47+
float l = l_ * l_ * l_;
48+
float m = m_ * m_ * m_;
49+
float s = s_ * s_ * s_;
50+
51+
float linear_r = 4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s;
52+
float linear_g = -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s;
53+
float linear_b = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s;
54+
55+
return {linear_r, linear_g, linear_b};
56+
}
57+
};
58+
}
59+
60+
#endif //AVIF_OKLAB

avif-coder/src/main/cpp/colorspace/Rec2408ToneMapper.cpp

+10-15
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "Rec2408ToneMapper.h"
3030
#include "Trc.h"
31+
#include "Oklab.hpp"
3132

3233
float rec2408_pq(float intensity, const float intensity_target) {
3334
// Lb, Lw, Lmin, Lmax
@@ -38,7 +39,7 @@ float rec2408_pq(float intensity, const float intensity_target) {
3839
0.f / intensity_target,
3940
};
4041

41-
for (float & luminance : luminances) {
42+
for (float &luminance : luminances) {
4243
luminance = avifToGammaPQ(luminance);
4344
}
4445

@@ -80,29 +81,23 @@ float rec2408_pq(float intensity, const float intensity_target) {
8081
void Rec2408ToneMapper::transferTone(float *inPlace, uint32_t width) {
8182
float *targetPlace = inPlace;
8283

83-
const float primaryR = this->lumaPrimaries[0];
84-
const float primaryG = this->lumaPrimaries[1];
85-
const float primaryB = this->lumaPrimaries[2];
86-
8784
const float vWeightA = this->weightA;
8885
const float vWeightB = this->weightB;
8986

9087
for (uint32_t x = 0; x < width; ++x) {
9188
float r = targetPlace[0];
9289
float g = targetPlace[1];
9390
float b = targetPlace[2];
94-
float Lin =
95-
r * primaryR + g * primaryG + b * primaryB;
96-
if (Lin == 0) {
91+
coder::Oklab oklab = coder::Oklab::fromLinearRGB(r, g, b);
92+
if (oklab.L == 0) {
9793
continue;
9894
}
99-
float shScale = (1.f + vWeightA * Lin) / (1.f + vWeightB * Lin);
100-
r = r * shScale;
101-
g = g * shScale;
102-
b = b * shScale;
103-
targetPlace[0] = std::min(r, 1.f);
104-
targetPlace[1] = std::min(g, 1.f);
105-
targetPlace[2] = std::min(b, 1.f);
95+
float shScale = (1.f + vWeightA * oklab.L) / (1.f + vWeightB * oklab.L);
96+
oklab.L = oklab.L * shScale;
97+
coder::Rgb linearRgb = oklab.toLinearRGB();
98+
targetPlace[0] = std::min(linearRgb.r, 1.f);
99+
targetPlace[1] = std::min(linearRgb.g, 1.f);
100+
targetPlace[2] = std::min(linearRgb.b, 1.f);
106101
targetPlace += 3;
107102
}
108103
}

avif-coder/src/main/cpp/colorspace/ToneMapper.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
#define AVIF_TONEMAPPER_H
3131

3232
enum CurveToneMapper {
33-
REC2408 = 1, LOGARITHMIC = 2, TONE_SKIP = 3
33+
REC2408 = 1, LOGARITHMIC = 2, FILMIC = 3, TONE_SKIP = 4
3434
};
3535

3636
#endif //AVIF_TONEMAPPER_H

avif-coder/src/main/java/com/radzivon/bartoshyk/avif/coder/ToneMapper.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@ import androidx.annotation.Keep
3232

3333
@Keep
3434
enum class ToneMapper(val value: Int) {
35-
REC2408(1), LOGARITHMIC(2)
35+
REC2408(1), LOGARITHMIC(2), FILMIC(3)
3636
}

0 commit comments

Comments
 (0)