1
0
Fork 0
forked from wry/wry

cmm: add more transfer functions

This commit is contained in:
Julian Orth 2025-03-01 19:35:04 +01:00
parent 8e65de91f9
commit e92de36f7a
4 changed files with 281 additions and 6 deletions

View file

@ -4,4 +4,13 @@ use linearize::Linearize;
pub enum TransferFunction {
Srgb,
Linear,
St2084Pq,
Bt1886,
Gamma22,
Gamma28,
St240,
ExtSrgb,
Log100,
Log316,
St428,
}

View file

@ -3,8 +3,17 @@
#include "frag_spec_const.glsl"
#define SRGB 0
#define LINEAR 1
#define TF_SRGB 0
#define TF_LINEAR 1
#define TF_ST2084_PQ 2
#define TF_BT1886 3
#define TF_GAMMA22 4
#define TF_GAMMA28 5
#define TF_ST240 6
#define TF_EXT_SRGB 7
#define TF_LOG100 8
#define TF_LOG316 9
#define TF_ST428 10
vec3 eotf_srgb(vec3 c) {
return mix(
@ -23,18 +32,141 @@ vec3 oetf_srgb(vec3 c) {
);
}
vec3 eotf_ext_srgb(vec3 c) {
return mix(
-pow((c - vec3(0.055)) / vec3(-1.055), vec3(2.4)),
mix(
c * vec3(1.0 / 12.92),
pow((c + vec3(0.055)) / vec3(1.055), vec3(2.4)),
greaterThan(c, vec3(0.04045))
),
greaterThan(c, vec3(-0.04045))
);
}
vec3 oetf_ext_srgb(vec3 c) {
c = clamp(c, -0.6038, 7.5913);
return mix(
vec3(-1.055) * pow(-c, vec3(1/2.4)) + vec3(0.055),
mix(
c * vec3(12.92),
vec3(1.055) * pow(c, vec3(1/2.4)) - vec3(0.055),
greaterThan(c, vec3(0.0031308))
),
greaterThan(c, vec3(-0.0031308))
);
}
vec3 eotf_st2084_pq(vec3 c) {
vec3 cp = pow(c, vec3(1.0 / 78.84375));
vec3 num = max(cp - vec3(0.8359375), 0.0);
vec3 den = vec3(18.8515625) - vec3(18.6875) * cp;
return pow(num / den, vec3(1.0 / 0.1593017578125));
}
vec3 oetf_st2084_pq(vec3 c) {
c = clamp(c, 0.0, 1.0);
vec3 num = vec3(0.8359375) + vec3(18.8515625) * pow(c, vec3(0.1593017578125));
vec3 den = vec3(1.0) + vec3(18.6875) * pow(c, vec3(0.1593017578125));
return pow(num / den, vec3(78.84375));
}
vec3 eotf_bt1886(vec3 c) {
return mix(
c * vec3(1.0 / 4.5),
pow((c + vec3(0.099)) * vec3(1.0 / 1.099), vec3(1.0 / 0.45)),
greaterThanEqual(c, vec3(0.081))
);
}
vec3 oetf_bt1886(vec3 c) {
return mix(
vec3(4.5) * c,
vec3(1.099) * pow(c, vec3(0.45)) - vec3(0.099),
greaterThanEqual(c, vec3(0.018))
);
}
vec3 eotf_st240(vec3 c) {
return mix(
c * vec3(1.0 / 4.0),
pow((c + vec3(0.1115)) * vec3(1.0 / 1.1115), vec3(1.0 / 0.45)),
greaterThanEqual(c, vec3(0.0913))
);
}
vec3 oetf_st240(vec3 c) {
return mix(
vec3(4.0) * c,
vec3(1.1115) * pow(c, vec3(0.45)) - vec3(0.1115),
greaterThanEqual(c, vec3(0.0228))
);
}
vec3 eotf_log100(vec3 c) {
return pow(vec3(10), vec3(2.0) * (c - vec3(1.0)));
}
vec3 oetf_log100(vec3 c) {
c = clamp(c, 0.0, 1.0);
return mix(
vec3(0.0),
vec3(1.0) + log2(c) / vec3(log2(10)) / vec3(2.0),
greaterThanEqual(c, vec3(0.01))
);
}
vec3 eotf_log316(vec3 c) {
return pow(vec3(10), vec3(2.5) * (c - vec3(1.0)));
}
vec3 oetf_log316(vec3 c) {
c = clamp(c, 0.0, 1.0);
return mix(
vec3(0.0),
vec3(1.0) + log2(c) / vec3(log2(10)) / vec3(2.5),
greaterThanEqual(c, vec3(sqrt(10) / 1000.0))
);
}
vec3 eotf_st428(vec3 c) {
return pow(c, vec3(2.6)) * vec3(52.37 / 48.0);
}
vec3 oetf_st428(vec3 c) {
return pow(vec3(48.0) * c / vec3(52.37), vec3(1.0 / 2.6));
}
vec3 apply_eotf(vec3 c) {
switch (eotf) {
case SRGB: return eotf_srgb(c);
case LINEAR: return c;
case TF_SRGB: return eotf_srgb(c);
case TF_LINEAR: return c;
case TF_ST2084_PQ: return eotf_st2084_pq(c);
case TF_BT1886: return eotf_bt1886(c);
case TF_GAMMA22: return pow(c, vec3(2.2));
case TF_GAMMA28: return pow(c, vec3(2.8));
case TF_ST240: return eotf_st240(c);
case TF_EXT_SRGB: return eotf_ext_srgb(c);
case TF_LOG100: return eotf_log100(c);
case TF_LOG316: return eotf_log316(c);
case TF_ST428: return eotf_st428(c);
default: return c;
}
}
vec3 apply_oetf(vec3 c) {
switch (oetf) {
case SRGB: return oetf_srgb(c);
case LINEAR: return c;
case TF_SRGB: return oetf_srgb(c);
case TF_LINEAR: return c;
case TF_ST2084_PQ: return oetf_st2084_pq(c);
case TF_BT1886: return oetf_bt1886(c);
case TF_GAMMA22: return pow(c, vec3(1.0 / 2.2));
case TF_GAMMA28: return pow(c, vec3(1.0 / 2.8));
case TF_ST240: return oetf_st240(c);
case TF_EXT_SRGB: return oetf_ext_srgb(c);
case TF_LOG100: return oetf_log100(c);
case TF_LOG316: return oetf_log316(c);
case TF_ST428: return oetf_st428(c);
default: return c;
}
}

View file

@ -2,6 +2,15 @@ use crate::cmm::cmm_transfer_function::TransferFunction;
pub const TF_SRGB: u32 = 0;
pub const TF_LINEAR: u32 = 1;
pub const TF_ST2084_PQ: u32 = 2;
pub const TF_BT1887: u32 = 3;
pub const TF_GAMMA22: u32 = 4;
pub const TF_GAMMA28: u32 = 5;
pub const TF_ST240: u32 = 6;
pub const TF_EXT_SRGB: u32 = 7;
pub const TF_LOG100: u32 = 8;
pub const TF_LOG316: u32 = 9;
pub const TF_ST428: u32 = 10;
pub trait TransferFunctionExt: Sized {
fn to_vulkan(self) -> u32;
@ -12,6 +21,15 @@ impl TransferFunctionExt for TransferFunction {
match self {
TransferFunction::Srgb => TF_SRGB,
TransferFunction::Linear => TF_LINEAR,
TransferFunction::St2084Pq => TF_ST2084_PQ,
TransferFunction::Bt1886 => TF_BT1887,
TransferFunction::Gamma22 => TF_GAMMA22,
TransferFunction::Gamma28 => TF_GAMMA28,
TransferFunction::St240 => TF_ST240,
TransferFunction::ExtSrgb => TF_EXT_SRGB,
TransferFunction::Log100 => TF_LOG100,
TransferFunction::Log316 => TF_LOG316,
TransferFunction::St428 => TF_ST428,
}
}
}

View file

@ -1,5 +1,8 @@
#![expect(clippy::excessive_precision)]
use {
crate::{cmm::cmm_transfer_function::TransferFunction, utils::clonecell::CloneCell},
num_traits::Float,
std::{cell::Cell, cmp::Ordering, ops::Mul, sync::Arc},
};
@ -77,6 +80,51 @@ impl Color {
fn linear(c: f32) -> f32 {
c
}
fn st2084_pq(c: f32) -> f32 {
let cp = c.powf(1.0 / 78.84375);
let num = (cp - 0.8359375).max(0.0);
let den = 18.8515625 - 18.6875 * cp;
(num / den).powf(1.0 / 0.1593017578125)
}
fn ext_srgb(c: f32) -> f32 {
let c = c.clamp(-0.6038, 7.5913);
if c <= -0.0031308 {
-1.055 * (-c).powf(1.0 / 2.4) + 0.055
} else if c <= 0.0031308 {
c * 12.92
} else {
1.055 * c.powf(1.0 / 2.4) - 0.055
}
}
fn bt1886(c: f32) -> f32 {
if c < 0.081 {
c / 4.5
} else {
((c + 0.099) / 1.099).powf(1.0 / 0.45)
}
}
fn st240(c: f32) -> f32 {
if c < 0.0913 {
c / 4.0
} else {
((c + 0.1115) / 1.1115).powf(1.0 / 0.45)
}
}
fn log100(c: f32) -> f32 {
10.0.powf(2.0 * (c - 1.0))
}
fn log316(c: f32) -> f32 {
10.0.powf(2.5 * (c - 1.0))
}
fn st428(c: f32) -> f32 {
c.powf(2.6) * 52.37 / 48.0
}
fn gamma22(c: f32) -> f32 {
c.powf(2.2)
}
fn gamma28(c: f32) -> f32 {
c.powf(2.8)
}
macro_rules! convert {
($tf:ident) => {{
r = $tf(r);
@ -87,6 +135,15 @@ impl Color {
match transfer_function {
TransferFunction::Srgb => convert!(srgb),
TransferFunction::Linear => convert!(linear),
TransferFunction::St2084Pq => convert!(st2084_pq),
TransferFunction::Bt1886 => convert!(bt1886),
TransferFunction::Gamma22 => convert!(gamma22),
TransferFunction::Gamma28 => convert!(gamma28),
TransferFunction::St240 => convert!(st240),
TransferFunction::ExtSrgb => convert!(ext_srgb),
TransferFunction::Log100 => convert!(log100),
TransferFunction::Log316 => convert!(log316),
TransferFunction::St428 => convert!(st428),
}
Self { r, g, b, a: 1.0 }
}
@ -185,6 +242,56 @@ impl Color {
fn linear(c: f32) -> f32 {
c
}
fn st2084_pq(c: f32) -> f32 {
let c = c.clamp(0.0, 1.0);
let num = 0.8359375 + 18.8515625 * c.powf(0.1593017578125);
let den = 1.0 + 18.6875 * c.powf(0.1593017578125);
(num / den).powf(78.84375)
}
fn ext_srgb(c: f32) -> f32 {
if c < -0.04045 {
-((c - 0.055) / -1.055).powf(2.4)
} else if c < 0.04045 {
c / 12.92
} else {
((c + 0.055) / 1.055).powf(2.4)
}
}
fn bt1886(c: f32) -> f32 {
if c < 0.018 {
4.5 * c
} else {
1.099 * c.powf(0.45) - 0.099
}
}
fn st240(c: f32) -> f32 {
if c < 0.0228 {
4.0 * c
} else {
1.1115 * c.powf(0.45) - 0.1115
}
}
fn log100(c: f32) -> f32 {
let c = c.clamp(0.0, 1.0);
if c < 0.01 { 0.0 } else { 1.0 + c.log10() / 2.0 }
}
fn log316(c: f32) -> f32 {
let c = c.clamp(0.0, 1.0);
if c < 10.0.sqrt() / 1000.0 {
0.0
} else {
1.0 + c.log10() / 2.5
}
}
fn st428(c: f32) -> f32 {
(48.0 * c / 52.37).powf(1.0 / 2.6)
}
fn gamma22(c: f32) -> f32 {
c.powf(1.0 / 2.2)
}
fn gamma28(c: f32) -> f32 {
c.powf(1.0 / 2.8)
}
macro_rules! convert {
($tf:ident) => {{
for c in &mut res[..3] {
@ -201,6 +308,15 @@ impl Color {
match transfer_function {
TransferFunction::Srgb => convert!(srgb),
TransferFunction::Linear => convert!(linear),
TransferFunction::St2084Pq => convert!(st2084_pq),
TransferFunction::Bt1886 => convert!(bt1886),
TransferFunction::Gamma22 => convert!(gamma22),
TransferFunction::Gamma28 => convert!(gamma28),
TransferFunction::St240 => convert!(st240),
TransferFunction::ExtSrgb => convert!(ext_srgb),
TransferFunction::Log100 => convert!(log100),
TransferFunction::Log316 => convert!(log316),
TransferFunction::St428 => convert!(st428),
}
if self.a < 1.0 {
for c in &mut res[..3] {