CaptureMoment 0.1.0
Free and open-source non-destructive photo editor
Loading...
Searching...
No Matches
halide_color_space.h
Go to the documentation of this file.
1
9#pragma once
10
11#include <Halide.h>
12
14
15namespace ColorSpace {
16
28inline Halide::Func rgbToOklab(const Halide::Func& input, const Halide::Var& x, const Halide::Var& y, const Halide::Var& c)
29{
30 Halide::Func oklab_func("rgb_to_oklab");
31
32 // 1. Read linear RGB (assumes input is already linear, if sRGB, add gamma removal here)
33 Halide::Expr r { input(x, y, 0) };
34 Halide::Expr g { input(x, y, 1) };
35 Halide::Expr b { input(x, y, 2) };
36
37 // 2. Linear RGB to LMS (Cone responses)
38 Halide::Expr l { 0.4122214708f * r + 0.5363325363f * g + 0.0514459929f * b };
39 Halide::Expr m { 0.2119034982f * r + 0.6806995451f * g + 0.1073969566f * b };
40 Halide::Expr s { 0.0883024619f * r + 0.2817188376f * g + 0.6299787005f * b };
41
42 // 3. Cube root of LMS (Sign-aware to prevent NaN on negative values)
43 // Halide::select is evaluated per-pixel at compile time to generate branching-less SIMD code.
44 Halide::Expr l_ { select(l >= 0.0f, pow(l, 1.0f / 3.0f), -pow(-l, 1.0f / 3.0f)) };
45 Halide::Expr m_ { select(m >= 0.0f, pow(m, 1.0f / 3.0f), -pow(-m, 1.0f / 3.0f)) };
46 Halide::Expr s_ { select(s >= 0.0f, pow(s, 1.0f / 3.0f), -pow(-s, 1.0f / 3.0f)) };
47
48 // 4. LMS' to Oklab using M2 matrix
49 Halide::Expr L { 0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_ };
50 Halide::Expr a { 1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_ };
51 Halide::Expr b_val { 0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_ };
52
53 // 5. Define the output, preserving Alpha on channel 3
54 oklab_func(x, y, c) = select(
55 c == 0, L,
56 c == 1, a,
57 c == 2, b_val,
58 input(x, y, c) // c == 3 (Alpha)
59 );
60
61 return oklab_func;
62}
63
75inline Halide::Func oklabToRgb(const Halide::Func& input, const Halide::Var& x, const Halide::Var& y, const Halide::Var& c)
76{
77
78 Halide::Func rgb_func("oklab_to_rgb");
79
80 // 1. Read Oklab
81 Halide::Expr L { input(x, y, 0) };
82 Halide::Expr a { input(x, y, 1) };
83 Halide::Expr b_val { input(x, y, 2) };
84
85 // 2. Oklab to LMS' (Inverse M2 matrix)
86 Halide::Expr l_ { L + 0.3963377774f * a + 0.2158037573f * b_val };
87 Halide::Expr m_ { L - 0.1055613458f * a - 0.0638541728f * b_val };
88 Halide::Expr s_ { L - 0.0894841775f * a - 1.2914855480f * b_val };
89
90 // 3. Cube LMS' (Inverse perceptual non-linearity)
91 // No need for select() here, multiplying a negative number by itself stays safe.
92 Halide::Expr l { l_ * l_ * l_ };
93 Halide::Expr m { m_ * m_ * m_ };
94 Halide::Expr s { s_ * s_ * s_ };
95
96 // 4. LMS to Linear RGB (Inverse M1 matrix)
97 Halide::Expr r { +4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s };
98 Halide::Expr g { -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s };
99 Halide::Expr b_out { -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s };
100
101 // 5. Define the output, preserving Alpha
102 rgb_func(x, y, c) = select(
103 c == 0, r,
104 c == 1, g,
105 c == 2, b_out,
106 input(x, y, c) // c == 3 (Alpha)
107 );
108
109 return rgb_func;
110}
111
112} // namespace ColorSpace
113
114} // namespace CaptureMoment::Core::Pipeline
Halide::Func oklabToRgb(const Halide::Func &input, const Halide::Var &x, const Halide::Var &y, const Halide::Var &c)
Converts a Chunky Oklab Halide::Func back to linear RGB.
Halide::Func rgbToOklab(const Halide::Func &input, const Halide::Var &x, const Halide::Var &y, const Halide::Var &c)
Converts RGB Halide::Func to Oklab.
CaptureMoment — footer