FFmpeg
Loading...
Searching...
No Matches
lut3d.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2024 Niklas Haas
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include <assert.h>
22#include <string.h>
23
25#include "libavutil/avassert.h"
26#include "libavutil/mem.h"
27#include "libavutil/refstruct.h"
28
29#include "cms.h"
30#include "csputils.h"
31#include "lut3d.h"
32
34{
36 SwsLut3D *lut3d = av_refstruct_alloc_ext(sizeof(*lut3d), flags, NULL, NULL);
37 if (!lut3d)
38 return NULL;
39
40 lut3d->map = (SwsColorMap) {0};
41 lut3d->dynamic = false;
42 return lut3d;
43}
44
45/**
46 * v0 and v1 are 'black' and 'white'
47 * v2 and v3 are closest RGB/CMY vertices
48 * x >= y >= z are relative weights
49 */
51v3u16_t barycentric(int shift, int x, int y, int z,
52 v3u16_t v0, v3u16_t v1, v3u16_t v2, v3u16_t v3)
53{
54 const int a = (1 << shift) - x;
55 const int b = x - y;
56 const int c = y - z;
57 const int d = z;
58 av_assert2(x >= y);
59 av_assert2(y >= z);
60
61 return (v3u16_t) {
62 (a * v0.x + b * v1.x + c * v2.x + d * v3.x) >> shift,
63 (a * v0.y + b * v1.y + c * v2.y + d * v3.y) >> shift,
64 (a * v0.z + b * v1.z + c * v2.z + d * v3.z) >> shift,
65 };
66}
67
69v3u16_t tetrahedral(const SwsLut3D *lut3d, int Rx, int Gx, int Bx,
70 int Rf, int Gf, int Bf)
71{
72 const int shift = 16 - INPUT_LUT_BITS;
73 const int Rn = FFMIN(Rx + 1, INPUT_LUT_SIZE - 1);
74 const int Gn = FFMIN(Gx + 1, INPUT_LUT_SIZE - 1);
75 const int Bn = FFMIN(Bx + 1, INPUT_LUT_SIZE - 1);
76
77 const v3u16_t c000 = lut3d->input[Bx][Gx][Rx];
78 const v3u16_t c111 = lut3d->input[Bn][Gn][Rn];
79 if (Rf > Gf) {
80 if (Gf > Bf) {
81 const v3u16_t c100 = lut3d->input[Bx][Gx][Rn];
82 const v3u16_t c110 = lut3d->input[Bx][Gn][Rn];
83 return barycentric(shift, Rf, Gf, Bf, c000, c100, c110, c111);
84 } else if (Rf > Bf) {
85 const v3u16_t c100 = lut3d->input[Bx][Gx][Rn];
86 const v3u16_t c101 = lut3d->input[Bn][Gx][Rn];
87 return barycentric(shift, Rf, Bf, Gf, c000, c100, c101, c111);
88 } else {
89 const v3u16_t c001 = lut3d->input[Bn][Gx][Rx];
90 const v3u16_t c101 = lut3d->input[Bn][Gx][Rn];
91 return barycentric(shift, Bf, Rf, Gf, c000, c001, c101, c111);
92 }
93 } else {
94 if (Bf > Gf) {
95 const v3u16_t c001 = lut3d->input[Bn][Gx][Rx];
96 const v3u16_t c011 = lut3d->input[Bn][Gn][Rx];
97 return barycentric(shift, Bf, Gf, Rf, c000, c001, c011, c111);
98 } else if (Bf > Rf) {
99 const v3u16_t c010 = lut3d->input[Bx][Gn][Rx];
100 const v3u16_t c011 = lut3d->input[Bn][Gn][Rx];
101 return barycentric(shift, Gf, Bf, Rf, c000, c010, c011, c111);
102 } else {
103 const v3u16_t c010 = lut3d->input[Bx][Gn][Rx];
104 const v3u16_t c110 = lut3d->input[Bx][Gn][Rn];
105 return barycentric(shift, Gf, Rf, Bf, c000, c010, c110, c111);
106 }
107 }
108}
109
111{
112 const int shift = 16 - INPUT_LUT_BITS;
113 const int Rx = rgb.x >> shift;
114 const int Gx = rgb.y >> shift;
115 const int Bx = rgb.z >> shift;
116 const int Rf = rgb.x & ((1 << shift) - 1);
117 const int Gf = rgb.y & ((1 << shift) - 1);
118 const int Bf = rgb.z & ((1 << shift) - 1);
119 return tetrahedral(lut3d, Rx, Gx, Bx, Rf, Gf, Bf);
120}
121
122/**
123 * Note: These functions are scaled such that x == (1 << shift) corresponds to
124 * a value of 1.0. This makes them suitable for use when interpolation LUT
125 * entries with a fractional part that is just masked away from the index,
126 * since a fractional coordinate of e.g. 0xFFFF corresponds to a mix weight of
127 * just slightly *less* than 1.0.
128 */
130{
131 const int xi = (1 << shift) - x;
132 return (v2u16_t) {
133 (a.x * xi + b.x * x) >> shift,
134 (a.y * xi + b.y * x) >> shift,
135 };
136}
137
139{
140 const int xi = (1 << shift) - x;
141 return (v3u16_t) {
142 (a.x * xi + b.x * x) >> shift,
143 (a.y * xi + b.y * x) >> shift,
144 (a.z * xi + b.z * x) >> shift,
145 };
146}
147
149{
150 const int Ishift = 16 - OUTPUT_LUT_BITS_I;
151 const int Cshift = 16 - OUTPUT_LUT_BITS_PT;
152 const int Ix = ipt.x >> Ishift;
153 const int Px = ipt.y >> Cshift;
154 const int Tx = ipt.z >> Cshift;
155 const int If = ipt.x & ((1 << Ishift) - 1);
156 const int Pf = ipt.y & ((1 << Cshift) - 1);
157 const int Tf = ipt.z & ((1 << Cshift) - 1);
158 const int In = FFMIN(Ix + 1, OUTPUT_LUT_SIZE_I - 1);
159 const int Pn = FFMIN(Px + 1, OUTPUT_LUT_SIZE_PT - 1);
160 const int Tn = FFMIN(Tx + 1, OUTPUT_LUT_SIZE_PT - 1);
161
162 /* Trilinear interpolation */
163 const v3u16_t c000 = lut3d->output[Tx][Px][Ix];
164 const v3u16_t c001 = lut3d->output[Tx][Px][In];
165 const v3u16_t c010 = lut3d->output[Tx][Pn][Ix];
166 const v3u16_t c011 = lut3d->output[Tx][Pn][In];
167 const v3u16_t c100 = lut3d->output[Tn][Px][Ix];
168 const v3u16_t c101 = lut3d->output[Tn][Px][In];
169 const v3u16_t c110 = lut3d->output[Tn][Pn][Ix];
170 const v3u16_t c111 = lut3d->output[Tn][Pn][In];
171 const v3u16_t c00 = lerp3u16(c000, c100, Tf, Cshift);
172 const v3u16_t c10 = lerp3u16(c010, c110, Tf, Cshift);
173 const v3u16_t c01 = lerp3u16(c001, c101, Tf, Cshift);
174 const v3u16_t c11 = lerp3u16(c011, c111, Tf, Cshift);
175 const v3u16_t c0 = lerp3u16(c00, c10, Pf, Cshift);
176 const v3u16_t c1 = lerp3u16(c01, c11, Pf, Cshift);
177 const v3u16_t c = lerp3u16(c0, c1, If, Ishift);
178 return c;
179}
180
182{
183 const int shift = 16 - TONE_LUT_BITS;
184 const int Ix = ipt.x >> shift;
185 const int If = ipt.x & ((1 << shift) - 1);
186
187 const v2u16_t w0 = lut3d->tone_map[Ix];
188 const v2u16_t w1 = lut3d->tone_map[Ix + 1];
189 const v2u16_t w = lerp2u16(w0, w1, If, shift);
190 const int base = (1 << 15) - w.y;
191
192 ipt.x = w.x;
193 ipt.y = base + (ipt.y * w.y >> 15);
194 ipt.z = base + (ipt.z * w.y >> 15);
195 return ipt;
196}
197
199{
200 int ret;
201
202 lut3d->dynamic = map->src.frame_peak.num > 0;
203 lut3d->map = *map;
204
205 if (lut3d->dynamic) {
206 ret = ff_sws_color_map_generate_dynamic(&lut3d->input[0][0][0],
207 &lut3d->output[0][0][0],
210 if (ret < 0)
211 return ret;
212
213 /* Make sure initial state is valid */
214 ff_sws_lut3d_update(lut3d, &map->src);
215 return 0;
216 } else {
217 return ff_sws_color_map_generate_static(&lut3d->input[0][0][0],
219 }
220}
221
222void ff_sws_lut3d_update(SwsLut3D *lut3d, const SwsColor *new_src)
223{
224 if (!new_src || !lut3d->dynamic)
225 return;
226
227 lut3d->map.src.frame_peak = new_src->frame_peak;
228 lut3d->map.src.frame_avg = new_src->frame_avg;
229
231 lut3d->tone_map[TONE_LUT_SIZE] = lut3d->tone_map[TONE_LUT_SIZE - 1];
232}
233
234void ff_sws_lut3d_apply_rgba64(const SwsLut3D *lut3d, const uint8_t *in,
235 int in_stride, uint8_t *out, int out_stride,
236 int w, int h)
237{
238 while (h--) {
239 const uint16_t *in16 = (const uint16_t *) in;
240 uint16_t *out16 = (uint16_t *) out;
241
242 for (int x = 0; x < w; x++) {
243 v3u16_t c = { in16[0], in16[1], in16[2] };
244 c = lookup_input16(lut3d, c);
245
246 if (lut3d->dynamic) {
247 c = apply_tone_map(lut3d, c);
248 c = lookup_output(lut3d, c);
249 }
250
251 out16[0] = c.x;
252 out16[1] = c.y;
253 out16[2] = c.z;
254 out16[3] = in16[3];
255 in16 += 4;
256 out16 += 4;
257 }
258
259 in += in_stride;
260 out += out_stride;
261 }
262}
static FILE * out
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert2(cond)
assert() equivalent, that does lie in speed critical code.
Definition avassert.h:68
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define xi(width, name, var, range_min, range_max, subs,...)
Definition cbs_h264.c:115
int ff_sws_color_map_generate_static(v3u16_t *lut, int size, const SwsColorMap *map)
Generates a single end-to-end color mapping 3DLUT embedding a static tone mapping curve.
Definition cms.c:685
void ff_sws_tone_map_generate(v2u16_t *lut, int size, const SwsColorMap *map)
Generate a 1D LUT of size size adapting intensity (I) levels from the source to the destination color...
Definition cms.c:749
int ff_sws_color_map_generate_dynamic(v3u16_t *input, v3u16_t *output, int size_input, int size_I, int size_PT, const SwsColorMap *map)
Generates a split pair of 3DLUTS, going to IPT and back, allowing an arbitrary dynamic EETF to be nes...
Definition cms.c:690
#define NULL
Definition coverity.c:32
int a
const VDPAUPixFmtMap * map
#define b
Definition input.c:43
static int shift(int a, int b)
Definition bonk.c:261
Macro definitions for various function/variable attributes.
#define av_always_inline
Definition attributes.h:72
@ TONE_LUT_SIZE
Definition lut3d.h:40
@ OUTPUT_LUT_SIZE_I
Definition lut3d.h:46
@ OUTPUT_LUT_BITS_PT
Definition lut3d.h:44
@ INPUT_LUT_BITS
Definition lut3d.h:35
@ INPUT_LUT_SIZE
Definition lut3d.h:36
@ OUTPUT_LUT_BITS_I
Definition lut3d.h:43
@ OUTPUT_LUT_SIZE_PT
Definition lut3d.h:47
@ TONE_LUT_BITS
Definition lut3d.h:39
uint8_t w
Definition llvidencdsp.c:39
static av_always_inline v3u16_t lookup_output(const SwsLut3D *lut3d, v3u16_t ipt)
Definition lut3d.c:148
static av_always_inline v3u16_t lerp3u16(v3u16_t a, v3u16_t b, int x, int shift)
Definition lut3d.c:138
SwsLut3D * ff_sws_lut3d_alloc(void)
Allocates a refstruct.
Definition lut3d.c:33
static av_always_inline v3u16_t lookup_input16(const SwsLut3D *lut3d, v3u16_t rgb)
Definition lut3d.c:110
static av_always_inline v3u16_t apply_tone_map(const SwsLut3D *lut3d, v3u16_t ipt)
Definition lut3d.c:181
static av_always_inline v3u16_t barycentric(int shift, int x, int y, int z, v3u16_t v0, v3u16_t v1, v3u16_t v2, v3u16_t v3)
v0 and v1 are 'black' and 'white' v2 and v3 are closest RGB/CMY vertices x >= y >= z are relative wei...
Definition lut3d.c:51
void ff_sws_lut3d_update(SwsLut3D *lut3d, const SwsColor *new_src)
Update the tone mapping state.
Definition lut3d.c:222
static av_always_inline v2u16_t lerp2u16(v2u16_t a, v2u16_t b, int x, int shift)
Note: These functions are scaled such that x == (1 << shift) corresponds to a value of 1....
Definition lut3d.c:129
void ff_sws_lut3d_apply_rgba64(const SwsLut3D *lut3d, const uint8_t *in, int in_stride, uint8_t *out, int out_stride, int w, int h)
Applies a color transformation to a plane in RGBA64 format.
Definition lut3d.c:234
int ff_sws_lut3d_generate(SwsLut3D *lut3d, const SwsColorMap *map)
Recalculate the (static) 3DLUT state with new settings.
Definition lut3d.c:198
static av_always_inline v3u16_t tetrahedral(const SwsLut3D *lut3d, int Rx, int Gx, int Bx, int Rf, int Gf, int Bf)
Definition lut3d.c:69
#define FFMIN(a, b)
Definition macros.h:49
Memory handling functions.
static const uint64_t c1
Definition murmur3.c:52
static void * av_refstruct_alloc_ext(size_t size, unsigned flags, void *opaque, void(*free_cb)(AVRefStructOpaque opaque, void *obj))
A wrapper around av_refstruct_alloc_ext_c() for the common case of a non-const qualified opaque.
Definition refstruct.h:94
#define AV_REFSTRUCT_FLAG_NO_ZEROING
If this flag is set in av_refstruct_alloc_ext_c(), the object will not be initially zeroed.
Definition refstruct.h:67
SwsColor src
Definition cms.h:61
AVRational frame_peak
Definition format.h:66
AVRational frame_avg
Definition format.h:67
Append a set of operations for applying a gamut/tone mapping 3D LUT to the pixels.
Definition lut3d.h:50
v3u16_t output[OUTPUT_LUT_SIZE_PT][OUTPUT_LUT_SIZE_PT][OUTPUT_LUT_SIZE_I]
Definition lut3d.h:56
SwsColorMap map
Definition lut3d.h:51
v3u16_t input[INPUT_LUT_SIZE][INPUT_LUT_SIZE][INPUT_LUT_SIZE]
Definition lut3d.h:55
bool dynamic
Definition lut3d.h:52
v2u16_t tone_map[TONE_LUT_SIZE+1]
Definition lut3d.h:59
Definition rpzaenc.c:60
uint16_t z
Definition csputils.h:73
uint16_t x
Definition csputils.h:73
uint16_t y
Definition csputils.h:73
uint8_t base
Definition vp3data.h:128
static double c[64]