2 * Copyright (c) 2013 Clément Bœsch
3 * Copyright (c) 2018 Paul B Mahol
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 * 3D Lookup table filter
29 #include "libavutil/opt.h"
30 #include "libavutil/file.h"
31 #include "libavutil/intreadwrite.h"
32 #include "libavutil/intfloat.h"
33 #include "libavutil/avassert.h"
34 #include "libavutil/pixdesc.h"
35 #include "libavutil/avstring.h"
37 #include "drawutils.h"
39 #include "framesync.h"
50 INTERPOLATE_TRILINEAR,
51 INTERPOLATE_TETRAHEDRAL,
59 /* 3D LUT don't often go up to level 32, but it is common to have a Hald CLUT
60 * of 512x512 (64x64x64) */
62 #define PRELUT_SIZE 65536
64 typedef struct Lut3DPreLut {
72 typedef struct LUT3DContext {
74 int interpolation; ///<interp_mode
78 avfilter_action_func *interp;
84 #if CONFIG_HALDCLUT_FILTER
85 uint8_t clut_rgba_map[4];
95 typedef struct ThreadData {
99 #define OFFSET(x) offsetof(LUT3DContext, x)
100 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
101 #define COMMON_OPTIONS \
102 { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, NB_INTERP_MODE-1, FLAGS, "interp_mode" }, \
103 { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_NEAREST}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \
104 { "trilinear", "interpolate values using the 8 points defining a cube", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TRILINEAR}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \
105 { "tetrahedral", "interpolate values using a tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TETRAHEDRAL}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \
108 #define EXPONENT_MASK 0x7F800000
109 #define MANTISSA_MASK 0x007FFFFF
110 #define SIGN_MASK 0x80000000
112 static inline float sanitizef(float f)
114 union av_intfloat32 t;
117 if ((t.i & EXPONENT_MASK) == EXPONENT_MASK) {
118 if ((t.i & MANTISSA_MASK) != 0) {
121 } else if (t.i & SIGN_MASK) {
132 static inline float lerpf(float v0, float v1, float f)
134 return v0 + (v1 - v0) * f;
137 static inline struct rgbvec lerp(const struct rgbvec *v0, const struct rgbvec *v1, float f)
140 lerpf(v0->r, v1->r, f), lerpf(v0->g, v1->g, f), lerpf(v0->b, v1->b, f)
145 #define NEAR(x) ((int)((x) + .5))
146 #define PREV(x) ((int)(x))
147 #define NEXT(x) (FFMIN((int)(x) + 1, lut3d->lutsize - 1))
150 * Get the nearest defined point
152 static inline struct rgbvec interp_nearest(const LUT3DContext *lut3d,
153 const struct rgbvec *s)
155 return lut3d->lut[NEAR(s->r) * lut3d->lutsize2 + NEAR(s->g) * lut3d->lutsize + NEAR(s->b)];
159 * Interpolate using the 8 vertices of a cube
160 * @see https://en.wikipedia.org/wiki/Trilinear_interpolation
162 static inline struct rgbvec interp_trilinear(const LUT3DContext *lut3d,
163 const struct rgbvec *s)
165 const int lutsize2 = lut3d->lutsize2;
166 const int lutsize = lut3d->lutsize;
167 const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)};
168 const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)};
169 const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]};
170 const struct rgbvec c000 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + prev[2]];
171 const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]];
172 const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]];
173 const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]];
174 const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]];
175 const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]];
176 const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]];
177 const struct rgbvec c111 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + next[2]];
178 const struct rgbvec c00 = lerp(&c000, &c100, d.r);
179 const struct rgbvec c10 = lerp(&c010, &c110, d.r);
180 const struct rgbvec c01 = lerp(&c001, &c101, d.r);
181 const struct rgbvec c11 = lerp(&c011, &c111, d.r);
182 const struct rgbvec c0 = lerp(&c00, &c10, d.g);
183 const struct rgbvec c1 = lerp(&c01, &c11, d.g);
184 const struct rgbvec c = lerp(&c0, &c1, d.b);
189 * Tetrahedral interpolation. Based on code found in Truelight Software Library paper.
190 * @see http://www.filmlight.ltd.uk/pdf/whitepapers/FL-TL-TN-0057-SoftwareLib.pdf
192 static inline struct rgbvec interp_tetrahedral(const LUT3DContext *lut3d,
193 const struct rgbvec *s)
195 const int lutsize2 = lut3d->lutsize2;
196 const int lutsize = lut3d->lutsize;
197 const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)};
198 const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)};
199 const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]};
200 const struct rgbvec c000 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + prev[2]];
201 const struct rgbvec c111 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + next[2]];
205 const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]];
206 const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]];
207 c.r = (1-d.r) * c000.r + (d.r-d.g) * c100.r + (d.g-d.b) * c110.r + (d.b) * c111.r;
208 c.g = (1-d.r) * c000.g + (d.r-d.g) * c100.g + (d.g-d.b) * c110.g + (d.b) * c111.g;
209 c.b = (1-d.r) * c000.b + (d.r-d.g) * c100.b + (d.g-d.b) * c110.b + (d.b) * c111.b;
210 } else if (d.r > d.b) {
211 const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]];
212 const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]];
213 c.r = (1-d.r) * c000.r + (d.r-d.b) * c100.r + (d.b-d.g) * c101.r + (d.g) * c111.r;
214 c.g = (1-d.r) * c000.g + (d.r-d.b) * c100.g + (d.b-d.g) * c101.g + (d.g) * c111.g;
215 c.b = (1-d.r) * c000.b + (d.r-d.b) * c100.b + (d.b-d.g) * c101.b + (d.g) * c111.b;
217 const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]];
218 const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]];
219 c.r = (1-d.b) * c000.r + (d.b-d.r) * c001.r + (d.r-d.g) * c101.r + (d.g) * c111.r;
220 c.g = (1-d.b) * c000.g + (d.b-d.r) * c001.g + (d.r-d.g) * c101.g + (d.g) * c111.g;
221 c.b = (1-d.b) * c000.b + (d.b-d.r) * c001.b + (d.r-d.g) * c101.b + (d.g) * c111.b;
225 const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]];
226 const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]];
227 c.r = (1-d.b) * c000.r + (d.b-d.g) * c001.r + (d.g-d.r) * c011.r + (d.r) * c111.r;
228 c.g = (1-d.b) * c000.g + (d.b-d.g) * c001.g + (d.g-d.r) * c011.g + (d.r) * c111.g;
229 c.b = (1-d.b) * c000.b + (d.b-d.g) * c001.b + (d.g-d.r) * c011.b + (d.r) * c111.b;
230 } else if (d.b > d.r) {
231 const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]];
232 const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]];
233 c.r = (1-d.g) * c000.r + (d.g-d.b) * c010.r + (d.b-d.r) * c011.r + (d.r) * c111.r;
234 c.g = (1-d.g) * c000.g + (d.g-d.b) * c010.g + (d.b-d.r) * c011.g + (d.r) * c111.g;
235 c.b = (1-d.g) * c000.b + (d.g-d.b) * c010.b + (d.b-d.r) * c011.b + (d.r) * c111.b;
237 const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]];
238 const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]];
239 c.r = (1-d.g) * c000.r + (d.g-d.r) * c010.r + (d.r-d.b) * c110.r + (d.b) * c111.r;
240 c.g = (1-d.g) * c000.g + (d.g-d.r) * c010.g + (d.r-d.b) * c110.g + (d.b) * c111.g;
241 c.b = (1-d.g) * c000.b + (d.g-d.r) * c010.b + (d.r-d.b) * c110.b + (d.b) * c111.b;
247 static inline float prelut_interp_1d_linear(const Lut3DPreLut *prelut,
248 int idx, const float s)
250 const int lut_max = prelut->size - 1;
251 const float scaled = (s - prelut->min[idx]) * prelut->scale[idx];
252 const float x = av_clipf(scaled, 0.0f, lut_max);
253 const int prev = PREV(x);
254 const int next = FFMIN((int)(x) + 1, lut_max);
255 const float p = prelut->lut[idx][prev];
256 const float n = prelut->lut[idx][next];
257 const float d = x - (float)prev;
258 return lerpf(p, n, d);
261 static inline struct rgbvec apply_prelut(const Lut3DPreLut *prelut,
262 const struct rgbvec *s)
266 if (prelut->size <= 0)
269 c.r = prelut_interp_1d_linear(prelut, 0, s->r);
270 c.g = prelut_interp_1d_linear(prelut, 1, s->g);
271 c.b = prelut_interp_1d_linear(prelut, 2, s->b);
275 #define DEFINE_INTERP_FUNC_PLANAR(name, nbits, depth) \
276 static int interp_##nbits##_##name##_p##depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
279 const LUT3DContext *lut3d = ctx->priv; \
280 const Lut3DPreLut *prelut = &lut3d->prelut; \
281 const ThreadData *td = arg; \
282 const AVFrame *in = td->in; \
283 const AVFrame *out = td->out; \
284 const int direct = out == in; \
285 const int slice_start = (in->height * jobnr ) / nb_jobs; \
286 const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \
287 uint8_t *grow = out->data[0] + slice_start * out->linesize[0]; \
288 uint8_t *brow = out->data[1] + slice_start * out->linesize[1]; \
289 uint8_t *rrow = out->data[2] + slice_start * out->linesize[2]; \
290 uint8_t *arow = out->data[3] + slice_start * out->linesize[3]; \
291 const uint8_t *srcgrow = in->data[0] + slice_start * in->linesize[0]; \
292 const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1]; \
293 const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2]; \
294 const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3]; \
295 const float lut_max = lut3d->lutsize - 1; \
296 const float scale_f = 1.0f / ((1<<depth) - 1); \
297 const float scale_r = lut3d->scale.r * lut_max; \
298 const float scale_g = lut3d->scale.g * lut_max; \
299 const float scale_b = lut3d->scale.b * lut_max; \
301 for (y = slice_start; y < slice_end; y++) { \
302 uint##nbits##_t *dstg = (uint##nbits##_t *)grow; \
303 uint##nbits##_t *dstb = (uint##nbits##_t *)brow; \
304 uint##nbits##_t *dstr = (uint##nbits##_t *)rrow; \
305 uint##nbits##_t *dsta = (uint##nbits##_t *)arow; \
306 const uint##nbits##_t *srcg = (const uint##nbits##_t *)srcgrow; \
307 const uint##nbits##_t *srcb = (const uint##nbits##_t *)srcbrow; \
308 const uint##nbits##_t *srcr = (const uint##nbits##_t *)srcrrow; \
309 const uint##nbits##_t *srca = (const uint##nbits##_t *)srcarow; \
310 for (x = 0; x < in->width; x++) { \
311 const struct rgbvec rgb = {srcr[x] * scale_f, \
313 srcb[x] * scale_f}; \
314 const struct rgbvec prelut_rgb = apply_prelut(prelut, &rgb); \
315 const struct rgbvec scaled_rgb = {av_clipf(prelut_rgb.r * scale_r, 0, lut_max), \
316 av_clipf(prelut_rgb.g * scale_g, 0, lut_max), \
317 av_clipf(prelut_rgb.b * scale_b, 0, lut_max)}; \
318 struct rgbvec vec = interp_##name(lut3d, &scaled_rgb); \
319 dstr[x] = av_clip_uintp2(vec.r * (float)((1<<depth) - 1), depth); \
320 dstg[x] = av_clip_uintp2(vec.g * (float)((1<<depth) - 1), depth); \
321 dstb[x] = av_clip_uintp2(vec.b * (float)((1<<depth) - 1), depth); \
322 if (!direct && in->linesize[3]) \
325 grow += out->linesize[0]; \
326 brow += out->linesize[1]; \
327 rrow += out->linesize[2]; \
328 arow += out->linesize[3]; \
329 srcgrow += in->linesize[0]; \
330 srcbrow += in->linesize[1]; \
331 srcrrow += in->linesize[2]; \
332 srcarow += in->linesize[3]; \
337 DEFINE_INTERP_FUNC_PLANAR(nearest, 8, 8)
338 DEFINE_INTERP_FUNC_PLANAR(trilinear, 8, 8)
339 DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 8, 8)
341 DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 9)
342 DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 9)
343 DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 9)
345 DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 10)
346 DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 10)
347 DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 10)
349 DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 12)
350 DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 12)
351 DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 12)
353 DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 14)
354 DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 14)
355 DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 14)
357 DEFINE_INTERP_FUNC_PLANAR(nearest, 16, 16)
358 DEFINE_INTERP_FUNC_PLANAR(trilinear, 16, 16)
359 DEFINE_INTERP_FUNC_PLANAR(tetrahedral, 16, 16)
361 #define DEFINE_INTERP_FUNC_PLANAR_FLOAT(name, depth) \
362 static int interp_##name##_pf##depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
365 const LUT3DContext *lut3d = ctx->priv; \
366 const Lut3DPreLut *prelut = &lut3d->prelut; \
367 const ThreadData *td = arg; \
368 const AVFrame *in = td->in; \
369 const AVFrame *out = td->out; \
370 const int direct = out == in; \
371 const int slice_start = (in->height * jobnr ) / nb_jobs; \
372 const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \
373 uint8_t *grow = out->data[0] + slice_start * out->linesize[0]; \
374 uint8_t *brow = out->data[1] + slice_start * out->linesize[1]; \
375 uint8_t *rrow = out->data[2] + slice_start * out->linesize[2]; \
376 uint8_t *arow = out->data[3] + slice_start * out->linesize[3]; \
377 const uint8_t *srcgrow = in->data[0] + slice_start * in->linesize[0]; \
378 const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1]; \
379 const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2]; \
380 const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3]; \
381 const float lut_max = lut3d->lutsize - 1; \
382 const float scale_r = lut3d->scale.r * lut_max; \
383 const float scale_g = lut3d->scale.g * lut_max; \
384 const float scale_b = lut3d->scale.b * lut_max; \
386 for (y = slice_start; y < slice_end; y++) { \
387 float *dstg = (float *)grow; \
388 float *dstb = (float *)brow; \
389 float *dstr = (float *)rrow; \
390 float *dsta = (float *)arow; \
391 const float *srcg = (const float *)srcgrow; \
392 const float *srcb = (const float *)srcbrow; \
393 const float *srcr = (const float *)srcrrow; \
394 const float *srca = (const float *)srcarow; \
395 for (x = 0; x < in->width; x++) { \
396 const struct rgbvec rgb = {sanitizef(srcr[x]), \
397 sanitizef(srcg[x]), \
398 sanitizef(srcb[x])}; \
399 const struct rgbvec prelut_rgb = apply_prelut(prelut, &rgb); \
400 const struct rgbvec scaled_rgb = {av_clipf(prelut_rgb.r * scale_r, 0, lut_max), \
401 av_clipf(prelut_rgb.g * scale_g, 0, lut_max), \
402 av_clipf(prelut_rgb.b * scale_b, 0, lut_max)}; \
403 struct rgbvec vec = interp_##name(lut3d, &scaled_rgb); \
407 if (!direct && in->linesize[3]) \
410 grow += out->linesize[0]; \
411 brow += out->linesize[1]; \
412 rrow += out->linesize[2]; \
413 arow += out->linesize[3]; \
414 srcgrow += in->linesize[0]; \
415 srcbrow += in->linesize[1]; \
416 srcrrow += in->linesize[2]; \
417 srcarow += in->linesize[3]; \
422 DEFINE_INTERP_FUNC_PLANAR_FLOAT(nearest, 32)
423 DEFINE_INTERP_FUNC_PLANAR_FLOAT(trilinear, 32)
424 DEFINE_INTERP_FUNC_PLANAR_FLOAT(tetrahedral, 32)
426 #define DEFINE_INTERP_FUNC(name, nbits) \
427 static int interp_##nbits##_##name(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
430 const LUT3DContext *lut3d = ctx->priv; \
431 const Lut3DPreLut *prelut = &lut3d->prelut; \
432 const ThreadData *td = arg; \
433 const AVFrame *in = td->in; \
434 const AVFrame *out = td->out; \
435 const int direct = out == in; \
436 const int step = lut3d->step; \
437 const uint8_t r = lut3d->rgba_map[R]; \
438 const uint8_t g = lut3d->rgba_map[G]; \
439 const uint8_t b = lut3d->rgba_map[B]; \
440 const uint8_t a = lut3d->rgba_map[A]; \
441 const int slice_start = (in->height * jobnr ) / nb_jobs; \
442 const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \
443 uint8_t *dstrow = out->data[0] + slice_start * out->linesize[0]; \
444 const uint8_t *srcrow = in ->data[0] + slice_start * in ->linesize[0]; \
445 const float lut_max = lut3d->lutsize - 1; \
446 const float scale_f = 1.0f / ((1<<nbits) - 1); \
447 const float scale_r = lut3d->scale.r * lut_max; \
448 const float scale_g = lut3d->scale.g * lut_max; \
449 const float scale_b = lut3d->scale.b * lut_max; \
451 for (y = slice_start; y < slice_end; y++) { \
452 uint##nbits##_t *dst = (uint##nbits##_t *)dstrow; \
453 const uint##nbits##_t *src = (const uint##nbits##_t *)srcrow; \
454 for (x = 0; x < in->width * step; x += step) { \
455 const struct rgbvec rgb = {src[x + r] * scale_f, \
456 src[x + g] * scale_f, \
457 src[x + b] * scale_f}; \
458 const struct rgbvec prelut_rgb = apply_prelut(prelut, &rgb); \
459 const struct rgbvec scaled_rgb = {av_clipf(prelut_rgb.r * scale_r, 0, lut_max), \
460 av_clipf(prelut_rgb.g * scale_g, 0, lut_max), \
461 av_clipf(prelut_rgb.b * scale_b, 0, lut_max)}; \
462 struct rgbvec vec = interp_##name(lut3d, &scaled_rgb); \
463 dst[x + r] = av_clip_uint##nbits(vec.r * (float)((1<<nbits) - 1)); \
464 dst[x + g] = av_clip_uint##nbits(vec.g * (float)((1<<nbits) - 1)); \
465 dst[x + b] = av_clip_uint##nbits(vec.b * (float)((1<<nbits) - 1)); \
466 if (!direct && step == 4) \
467 dst[x + a] = src[x + a]; \
469 dstrow += out->linesize[0]; \
470 srcrow += in ->linesize[0]; \
475 DEFINE_INTERP_FUNC(nearest, 8)
476 DEFINE_INTERP_FUNC(trilinear, 8)
477 DEFINE_INTERP_FUNC(tetrahedral, 8)
479 DEFINE_INTERP_FUNC(nearest, 16)
480 DEFINE_INTERP_FUNC(trilinear, 16)
481 DEFINE_INTERP_FUNC(tetrahedral, 16)
483 #define MAX_LINE_SIZE 512
485 static int skip_line(const char *p)
487 while (*p && av_isspace(*p))
489 return !*p || *p == '#';
492 static char* fget_next_word(char* dst, int max, FILE* f)
499 /* skip until next non whitespace char */
500 while ((c = fgetc(f)) != EOF) {
509 /* get max bytes or up until next whitespace char */
510 for (; max > 0; max--) {
511 if ((c = fgetc(f)) == EOF)
526 #define NEXT_LINE(loop_cond) do { \
527 if (!fgets(line, sizeof(line), f)) { \
528 av_log(ctx, AV_LOG_ERROR, "Unexpected EOF\n"); \
529 return AVERROR_INVALIDDATA; \
533 #define NEXT_LINE_OR_GOTO(loop_cond, label) do { \
534 if (!fgets(line, sizeof(line), f)) { \
535 av_log(ctx, AV_LOG_ERROR, "Unexpected EOF\n"); \
536 ret = AVERROR_INVALIDDATA; \
541 static int allocate_3dlut(AVFilterContext *ctx, int lutsize, int prelut)
543 LUT3DContext *lut3d = ctx->priv;
545 if (lutsize < 2 || lutsize > MAX_LEVEL) {
546 av_log(ctx, AV_LOG_ERROR, "Too large or invalid 3D LUT size\n");
547 return AVERROR(EINVAL);
550 av_freep(&lut3d->lut);
551 lut3d->lut = av_malloc_array(lutsize * lutsize * lutsize, sizeof(*lut3d->lut));
553 return AVERROR(ENOMEM);
556 lut3d->prelut.size = PRELUT_SIZE;
557 for (i = 0; i < 3; i++) {
558 av_freep(&lut3d->prelut.lut[i]);
559 lut3d->prelut.lut[i] = av_malloc_array(PRELUT_SIZE, sizeof(*lut3d->prelut.lut[0]));
560 if (!lut3d->prelut.lut[i])
561 return AVERROR(ENOMEM);
564 lut3d->prelut.size = 0;
565 for (i = 0; i < 3; i++) {
566 av_freep(&lut3d->prelut.lut[i]);
569 lut3d->lutsize = lutsize;
570 lut3d->lutsize2 = lutsize * lutsize;
574 /* Basically r g and b float values on each line, with a facultative 3DLUTSIZE
575 * directive; seems to be generated by Davinci */
576 static int parse_dat(AVFilterContext *ctx, FILE *f)
578 LUT3DContext *lut3d = ctx->priv;
579 char line[MAX_LINE_SIZE];
580 int ret, i, j, k, size, size2;
582 lut3d->lutsize = size = 33;
585 NEXT_LINE(skip_line(line));
586 if (!strncmp(line, "3DLUTSIZE ", 10)) {
587 size = strtol(line + 10, NULL, 0);
589 NEXT_LINE(skip_line(line));
592 ret = allocate_3dlut(ctx, size, 0);
596 for (k = 0; k < size; k++) {
597 for (j = 0; j < size; j++) {
598 for (i = 0; i < size; i++) {
599 struct rgbvec *vec = &lut3d->lut[k * size2 + j * size + i];
600 if (k != 0 || j != 0 || i != 0)
601 NEXT_LINE(skip_line(line));
602 if (av_sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != 3)
603 return AVERROR_INVALIDDATA;
611 static int parse_cube(AVFilterContext *ctx, FILE *f)
613 LUT3DContext *lut3d = ctx->priv;
614 char line[MAX_LINE_SIZE];
615 float min[3] = {0.0, 0.0, 0.0};
616 float max[3] = {1.0, 1.0, 1.0};
618 while (fgets(line, sizeof(line), f)) {
619 if (!strncmp(line, "LUT_3D_SIZE", 11)) {
621 const int size = strtol(line + 12, NULL, 0);
622 const int size2 = size * size;
624 ret = allocate_3dlut(ctx, size, 0);
628 for (k = 0; k < size; k++) {
629 for (j = 0; j < size; j++) {
630 for (i = 0; i < size; i++) {
631 struct rgbvec *vec = &lut3d->lut[i * size2 + j * size + k];
636 if (!strncmp(line, "DOMAIN_", 7)) {
638 if (!strncmp(line + 7, "MIN ", 4)) vals = min;
639 else if (!strncmp(line + 7, "MAX ", 4)) vals = max;
641 return AVERROR_INVALIDDATA;
642 av_sscanf(line + 11, "%f %f %f", vals, vals + 1, vals + 2);
643 av_log(ctx, AV_LOG_DEBUG, "min: %f %f %f | max: %f %f %f\n",
644 min[0], min[1], min[2], max[0], max[1], max[2]);
646 } else if (!strncmp(line, "TITLE", 5)) {
649 } while (skip_line(line));
650 if (av_sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != 3)
651 return AVERROR_INVALIDDATA;
659 lut3d->scale.r = av_clipf(1. / (max[0] - min[0]), 0.f, 1.f);
660 lut3d->scale.g = av_clipf(1. / (max[1] - min[1]), 0.f, 1.f);
661 lut3d->scale.b = av_clipf(1. / (max[2] - min[2]), 0.f, 1.f);
666 /* Assume 17x17x17 LUT with a 16-bit depth
667 * FIXME: it seems there are various 3dl formats */
668 static int parse_3dl(AVFilterContext *ctx, FILE *f)
670 char line[MAX_LINE_SIZE];
671 LUT3DContext *lut3d = ctx->priv;
674 const int size2 = 17 * 17;
675 const float scale = 16*16*16;
677 lut3d->lutsize = size;
679 ret = allocate_3dlut(ctx, size, 0);
683 NEXT_LINE(skip_line(line));
684 for (k = 0; k < size; k++) {
685 for (j = 0; j < size; j++) {
686 for (i = 0; i < size; i++) {
688 struct rgbvec *vec = &lut3d->lut[k * size2 + j * size + i];
690 NEXT_LINE(skip_line(line));
691 if (av_sscanf(line, "%d %d %d", &r, &g, &b) != 3)
692 return AVERROR_INVALIDDATA;
703 static int parse_m3d(AVFilterContext *ctx, FILE *f)
705 LUT3DContext *lut3d = ctx->priv;
707 int ret, i, j, k, size, size2, in = -1, out = -1;
708 char line[MAX_LINE_SIZE];
709 uint8_t rgb_map[3] = {0, 1, 2};
711 while (fgets(line, sizeof(line), f)) {
712 if (!strncmp(line, "in", 2)) in = strtol(line + 2, NULL, 0);
713 else if (!strncmp(line, "out", 3)) out = strtol(line + 3, NULL, 0);
714 else if (!strncmp(line, "values", 6)) {
715 const char *p = line + 6;
716 #define SET_COLOR(id) do { \
717 while (av_isspace(*p)) \
720 case 'r': rgb_map[id] = 0; break; \
721 case 'g': rgb_map[id] = 1; break; \
722 case 'b': rgb_map[id] = 2; break; \
724 while (*p && !av_isspace(*p)) \
734 if (in == -1 || out == -1) {
735 av_log(ctx, AV_LOG_ERROR, "in and out must be defined\n");
736 return AVERROR_INVALIDDATA;
738 if (in < 2 || out < 2 ||
739 in > MAX_LEVEL*MAX_LEVEL*MAX_LEVEL ||
740 out > MAX_LEVEL*MAX_LEVEL*MAX_LEVEL) {
741 av_log(ctx, AV_LOG_ERROR, "invalid in (%d) or out (%d)\n", in, out);
742 return AVERROR_INVALIDDATA;
744 for (size = 1; size*size*size < in; size++);
745 lut3d->lutsize = size;
748 ret = allocate_3dlut(ctx, size, 0);
752 scale = 1. / (out - 1);
754 for (k = 0; k < size; k++) {
755 for (j = 0; j < size; j++) {
756 for (i = 0; i < size; i++) {
757 struct rgbvec *vec = &lut3d->lut[k * size2 + j * size + i];
761 if (av_sscanf(line, "%f %f %f", val, val + 1, val + 2) != 3)
762 return AVERROR_INVALIDDATA;
763 vec->r = val[rgb_map[0]] * scale;
764 vec->g = val[rgb_map[1]] * scale;
765 vec->b = val[rgb_map[2]] * scale;
772 static int nearest_sample_index(float *data, float x, int low, int hi)
782 av_assert0(x >= data[low]);
783 av_assert0(x <= data[hi]);
784 av_assert0((hi-low) > 0);
789 mid = (low + hi) / 2;
800 #define NEXT_FLOAT_OR_GOTO(value, label) \
801 if (!fget_next_word(line, sizeof(line) ,f)) { \
802 ret = AVERROR_INVALIDDATA; \
805 if (av_sscanf(line, "%f", &value) != 1) { \
806 ret = AVERROR_INVALIDDATA; \
810 static int parse_cinespace(AVFilterContext *ctx, FILE *f)
812 LUT3DContext *lut3d = ctx->priv;
813 char line[MAX_LINE_SIZE];
814 float in_min[3] = {0.0, 0.0, 0.0};
815 float in_max[3] = {1.0, 1.0, 1.0};
816 float out_min[3] = {0.0, 0.0, 0.0};
817 float out_max[3] = {1.0, 1.0, 1.0};
818 int inside_metadata = 0, size, size2;
822 int prelut_sizes[3] = {0, 0, 0};
823 float *in_prelut[3] = {NULL, NULL, NULL};
824 float *out_prelut[3] = {NULL, NULL, NULL};
826 NEXT_LINE_OR_GOTO(skip_line(line), end);
827 if (strncmp(line, "CSPLUTV100", 10)) {
828 av_log(ctx, AV_LOG_ERROR, "Not cineSpace LUT format\n");
829 ret = AVERROR(EINVAL);
833 NEXT_LINE_OR_GOTO(skip_line(line), end);
834 if (strncmp(line, "3D", 2)) {
835 av_log(ctx, AV_LOG_ERROR, "Not 3D LUT format\n");
836 ret = AVERROR(EINVAL);
841 NEXT_LINE_OR_GOTO(skip_line(line), end);
843 if (!strncmp(line, "BEGIN METADATA", 14)) {
847 if (!strncmp(line, "END METADATA", 12)) {
851 if (inside_metadata == 0) {
852 int size_r, size_g, size_b;
854 for (int i = 0; i < 3; i++) {
855 int npoints = strtol(line, NULL, 0);
860 if (npoints > PRELUT_SIZE) {
861 av_log(ctx, AV_LOG_ERROR, "Prelut size too large.\n");
862 ret = AVERROR_INVALIDDATA;
866 if (in_prelut[i] || out_prelut[i]) {
867 av_log(ctx, AV_LOG_ERROR, "Invalid file has multiple preluts.\n");
868 ret = AVERROR_INVALIDDATA;
872 in_prelut[i] = (float*)av_malloc(npoints * sizeof(float));
873 out_prelut[i] = (float*)av_malloc(npoints * sizeof(float));
874 if (!in_prelut[i] || !out_prelut[i]) {
875 ret = AVERROR(ENOMEM);
879 prelut_sizes[i] = npoints;
881 in_max[i] = -FLT_MAX;
882 out_min[i] = FLT_MAX;
883 out_max[i] = -FLT_MAX;
885 for (int j = 0; j < npoints; j++) {
886 NEXT_FLOAT_OR_GOTO(v, end)
887 in_min[i] = FFMIN(in_min[i], v);
888 in_max[i] = FFMAX(in_max[i], v);
890 if (j > 0 && v < last) {
891 av_log(ctx, AV_LOG_ERROR, "Invalid file, non increasing prelut.\n");
892 ret = AVERROR(ENOMEM);
898 for (int j = 0; j < npoints; j++) {
899 NEXT_FLOAT_OR_GOTO(v, end)
900 out_min[i] = FFMIN(out_min[i], v);
901 out_max[i] = FFMAX(out_max[i], v);
902 out_prelut[i][j] = v;
905 } else if (npoints == 2) {
906 NEXT_LINE_OR_GOTO(skip_line(line), end);
907 if (av_sscanf(line, "%f %f", &in_min[i], &in_max[i]) != 2) {
908 ret = AVERROR_INVALIDDATA;
911 NEXT_LINE_OR_GOTO(skip_line(line), end);
912 if (av_sscanf(line, "%f %f", &out_min[i], &out_max[i]) != 2) {
913 ret = AVERROR_INVALIDDATA;
918 av_log(ctx, AV_LOG_ERROR, "Unsupported number of pre-lut points.\n");
919 ret = AVERROR_PATCHWELCOME;
923 NEXT_LINE_OR_GOTO(skip_line(line), end);
926 if (av_sscanf(line, "%d %d %d", &size_r, &size_g, &size_b) != 3) {
927 ret = AVERROR(EINVAL);
930 if (size_r != size_g || size_r != size_b) {
931 av_log(ctx, AV_LOG_ERROR, "Unsupported size combination: %dx%dx%d.\n", size_r, size_g, size_b);
932 ret = AVERROR_PATCHWELCOME;
939 if (prelut_sizes[0] && prelut_sizes[1] && prelut_sizes[2])
942 ret = allocate_3dlut(ctx, size, prelut);
946 for (int k = 0; k < size; k++) {
947 for (int j = 0; j < size; j++) {
948 for (int i = 0; i < size; i++) {
949 struct rgbvec *vec = &lut3d->lut[i * size2 + j * size + k];
951 NEXT_LINE_OR_GOTO(skip_line(line), end);
952 if (av_sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != 3) {
953 ret = AVERROR_INVALIDDATA;
957 vec->r *= out_max[0] - out_min[0];
958 vec->g *= out_max[1] - out_min[1];
959 vec->b *= out_max[2] - out_min[2];
969 for (int c = 0; c < 3; c++) {
971 lut3d->prelut.min[c] = in_min[c];
972 lut3d->prelut.max[c] = in_max[c];
973 lut3d->prelut.scale[c] = (1.0f / (float)(in_max[c] - in_min[c])) * (lut3d->prelut.size - 1);
975 for (int i = 0; i < lut3d->prelut.size; ++i) {
976 float mix = (float) i / (float)(lut3d->prelut.size - 1);
977 float x = lerpf(in_min[c], in_max[c], mix), a, b;
979 int idx = nearest_sample_index(in_prelut[c], x, 0, prelut_sizes[c]-1);
980 av_assert0(idx + 1 < prelut_sizes[c]);
982 a = out_prelut[c][idx + 0];
983 b = out_prelut[c][idx + 1];
984 mix = x - in_prelut[c][idx];
986 lut3d->prelut.lut[c][i] = sanitizef(lerpf(a, b, mix));
989 lut3d->scale.r = 1.00f;
990 lut3d->scale.g = 1.00f;
991 lut3d->scale.b = 1.00f;
994 lut3d->scale.r = av_clipf(1. / (in_max[0] - in_min[0]), 0.f, 1.f);
995 lut3d->scale.g = av_clipf(1. / (in_max[1] - in_min[1]), 0.f, 1.f);
996 lut3d->scale.b = av_clipf(1. / (in_max[2] - in_min[2]), 0.f, 1.f);
1000 for (int c = 0; c < 3; c++) {
1001 av_freep(&in_prelut[c]);
1002 av_freep(&out_prelut[c]);
1007 static int set_identity_matrix(AVFilterContext *ctx, int size)
1009 LUT3DContext *lut3d = ctx->priv;
1011 const int size2 = size * size;
1012 const float c = 1. / (size - 1);
1014 ret = allocate_3dlut(ctx, size, 0);
1018 for (k = 0; k < size; k++) {
1019 for (j = 0; j < size; j++) {
1020 for (i = 0; i < size; i++) {
1021 struct rgbvec *vec = &lut3d->lut[k * size2 + j * size + i];
1032 static int query_formats(AVFilterContext *ctx)
1034 static const enum AVPixelFormat pix_fmts[] = {
1035 AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
1036 AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA,
1037 AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR,
1038 AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR,
1039 AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0,
1040 AV_PIX_FMT_RGB48, AV_PIX_FMT_BGR48,
1041 AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64,
1042 AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
1044 AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
1045 AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12,
1047 AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16,
1048 AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32,
1051 AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
1053 return AVERROR(ENOMEM);
1054 return ff_set_common_formats(ctx, fmts_list);
1057 static int config_input(AVFilterLink *inlink)
1059 int depth, is16bit, isfloat, planar;
1060 LUT3DContext *lut3d = inlink->dst->priv;
1061 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
1063 depth = desc->comp[0].depth;
1064 is16bit = desc->comp[0].depth > 8;
1065 planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR;
1066 isfloat = desc->flags & AV_PIX_FMT_FLAG_FLOAT;
1067 ff_fill_rgba_map(lut3d->rgba_map, inlink->format);
1068 lut3d->step = av_get_padded_bits_per_pixel(desc) >> (3 + is16bit);
1070 #define SET_FUNC(name) do { \
1071 if (planar && !isfloat) { \
1073 case 8: lut3d->interp = interp_8_##name##_p8; break; \
1074 case 9: lut3d->interp = interp_16_##name##_p9; break; \
1075 case 10: lut3d->interp = interp_16_##name##_p10; break; \
1076 case 12: lut3d->interp = interp_16_##name##_p12; break; \
1077 case 14: lut3d->interp = interp_16_##name##_p14; break; \
1078 case 16: lut3d->interp = interp_16_##name##_p16; break; \
1080 } else if (isfloat) { lut3d->interp = interp_##name##_pf32; \
1081 } else if (is16bit) { lut3d->interp = interp_16_##name; \
1082 } else { lut3d->interp = interp_8_##name; } \
1085 switch (lut3d->interpolation) {
1086 case INTERPOLATE_NEAREST: SET_FUNC(nearest); break;
1087 case INTERPOLATE_TRILINEAR: SET_FUNC(trilinear); break;
1088 case INTERPOLATE_TETRAHEDRAL: SET_FUNC(tetrahedral); break;
1096 static AVFrame *apply_lut(AVFilterLink *inlink, AVFrame *in)
1098 AVFilterContext *ctx = inlink->dst;
1099 LUT3DContext *lut3d = ctx->priv;
1100 AVFilterLink *outlink = inlink->dst->outputs[0];
1104 if (av_frame_is_writable(in)) {
1107 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
1112 av_frame_copy_props(out, in);
1117 ctx->internal->execute(ctx, lut3d->interp, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
1125 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
1127 AVFilterLink *outlink = inlink->dst->outputs[0];
1128 AVFrame *out = apply_lut(inlink, in);
1130 return AVERROR(ENOMEM);
1131 return ff_filter_frame(outlink, out);
1134 #if CONFIG_LUT3D_FILTER
1135 static const AVOption lut3d_options[] = {
1136 { "file", "set 3D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
1140 AVFILTER_DEFINE_CLASS(lut3d);
1142 static av_cold int lut3d_init(AVFilterContext *ctx)
1147 LUT3DContext *lut3d = ctx->priv;
1149 lut3d->scale.r = lut3d->scale.g = lut3d->scale.b = 1.f;
1152 return set_identity_matrix(ctx, 32);
1155 f = av_fopen_utf8(lut3d->file, "r");
1157 ret = AVERROR(errno);
1158 av_log(ctx, AV_LOG_ERROR, "%s: %s\n", lut3d->file, av_err2str(ret));
1162 ext = strrchr(lut3d->file, '.');
1164 av_log(ctx, AV_LOG_ERROR, "Unable to guess the format from the extension\n");
1165 ret = AVERROR_INVALIDDATA;
1170 if (!av_strcasecmp(ext, "dat")) {
1171 ret = parse_dat(ctx, f);
1172 } else if (!av_strcasecmp(ext, "3dl")) {
1173 ret = parse_3dl(ctx, f);
1174 } else if (!av_strcasecmp(ext, "cube")) {
1175 ret = parse_cube(ctx, f);
1176 } else if (!av_strcasecmp(ext, "m3d")) {
1177 ret = parse_m3d(ctx, f);
1178 } else if (!av_strcasecmp(ext, "csp")) {
1179 ret = parse_cinespace(ctx, f);
1181 av_log(ctx, AV_LOG_ERROR, "Unrecognized '.%s' file type\n", ext);
1182 ret = AVERROR(EINVAL);
1185 if (!ret && !lut3d->lutsize) {
1186 av_log(ctx, AV_LOG_ERROR, "3D LUT is empty\n");
1187 ret = AVERROR_INVALIDDATA;
1195 static av_cold void lut3d_uninit(AVFilterContext *ctx)
1197 LUT3DContext *lut3d = ctx->priv;
1199 av_freep(&lut3d->lut);
1201 for (i = 0; i < 3; i++) {
1202 av_freep(&lut3d->prelut.lut[i]);
1206 static const AVFilterPad lut3d_inputs[] = {
1209 .type = AVMEDIA_TYPE_VIDEO,
1210 .filter_frame = filter_frame,
1211 .config_props = config_input,
1216 static const AVFilterPad lut3d_outputs[] = {
1219 .type = AVMEDIA_TYPE_VIDEO,
1224 AVFilter ff_vf_lut3d = {
1226 .description = NULL_IF_CONFIG_SMALL("Adjust colors using a 3D LUT."),
1227 .priv_size = sizeof(LUT3DContext),
1229 .uninit = lut3d_uninit,
1230 .query_formats = query_formats,
1231 .inputs = lut3d_inputs,
1232 .outputs = lut3d_outputs,
1233 .priv_class = &lut3d_class,
1234 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
1238 #if CONFIG_HALDCLUT_FILTER
1240 static void update_clut_packed(LUT3DContext *lut3d, const AVFrame *frame)
1242 const uint8_t *data = frame->data[0];
1243 const int linesize = frame->linesize[0];
1244 const int w = lut3d->clut_width;
1245 const int step = lut3d->clut_step;
1246 const uint8_t *rgba_map = lut3d->clut_rgba_map;
1247 const int level = lut3d->lutsize;
1248 const int level2 = lut3d->lutsize2;
1250 #define LOAD_CLUT(nbits) do { \
1251 int i, j, k, x = 0, y = 0; \
1253 for (k = 0; k < level; k++) { \
1254 for (j = 0; j < level; j++) { \
1255 for (i = 0; i < level; i++) { \
1256 const uint##nbits##_t *src = (const uint##nbits##_t *) \
1257 (data + y*linesize + x*step); \
1258 struct rgbvec *vec = &lut3d->lut[i * level2 + j * level + k]; \
1259 vec->r = src[rgba_map[0]] / (float)((1<<(nbits)) - 1); \
1260 vec->g = src[rgba_map[1]] / (float)((1<<(nbits)) - 1); \
1261 vec->b = src[rgba_map[2]] / (float)((1<<(nbits)) - 1); \
1271 switch (lut3d->clut_bits) {
1272 case 8: LOAD_CLUT(8); break;
1273 case 16: LOAD_CLUT(16); break;
1277 static void update_clut_planar(LUT3DContext *lut3d, const AVFrame *frame)
1279 const uint8_t *datag = frame->data[0];
1280 const uint8_t *datab = frame->data[1];
1281 const uint8_t *datar = frame->data[2];
1282 const int glinesize = frame->linesize[0];
1283 const int blinesize = frame->linesize[1];
1284 const int rlinesize = frame->linesize[2];
1285 const int w = lut3d->clut_width;
1286 const int level = lut3d->lutsize;
1287 const int level2 = lut3d->lutsize2;
1289 #define LOAD_CLUT_PLANAR(nbits, depth) do { \
1290 int i, j, k, x = 0, y = 0; \
1292 for (k = 0; k < level; k++) { \
1293 for (j = 0; j < level; j++) { \
1294 for (i = 0; i < level; i++) { \
1295 const uint##nbits##_t *gsrc = (const uint##nbits##_t *) \
1296 (datag + y*glinesize); \
1297 const uint##nbits##_t *bsrc = (const uint##nbits##_t *) \
1298 (datab + y*blinesize); \
1299 const uint##nbits##_t *rsrc = (const uint##nbits##_t *) \
1300 (datar + y*rlinesize); \
1301 struct rgbvec *vec = &lut3d->lut[i * level2 + j * level + k]; \
1302 vec->r = gsrc[x] / (float)((1<<(depth)) - 1); \
1303 vec->g = bsrc[x] / (float)((1<<(depth)) - 1); \
1304 vec->b = rsrc[x] / (float)((1<<(depth)) - 1); \
1314 switch (lut3d->clut_bits) {
1315 case 8: LOAD_CLUT_PLANAR(8, 8); break;
1316 case 9: LOAD_CLUT_PLANAR(16, 9); break;
1317 case 10: LOAD_CLUT_PLANAR(16, 10); break;
1318 case 12: LOAD_CLUT_PLANAR(16, 12); break;
1319 case 14: LOAD_CLUT_PLANAR(16, 14); break;
1320 case 16: LOAD_CLUT_PLANAR(16, 16); break;
1324 static void update_clut_float(LUT3DContext *lut3d, const AVFrame *frame)
1326 const uint8_t *datag = frame->data[0];
1327 const uint8_t *datab = frame->data[1];
1328 const uint8_t *datar = frame->data[2];
1329 const int glinesize = frame->linesize[0];
1330 const int blinesize = frame->linesize[1];
1331 const int rlinesize = frame->linesize[2];
1332 const int w = lut3d->clut_width;
1333 const int level = lut3d->lutsize;
1334 const int level2 = lut3d->lutsize2;
1336 int i, j, k, x = 0, y = 0;
1338 for (k = 0; k < level; k++) {
1339 for (j = 0; j < level; j++) {
1340 for (i = 0; i < level; i++) {
1341 const float *gsrc = (const float *)(datag + y*glinesize);
1342 const float *bsrc = (const float *)(datab + y*blinesize);
1343 const float *rsrc = (const float *)(datar + y*rlinesize);
1344 struct rgbvec *vec = &lut3d->lut[i * level2 + j * level + k];
1357 static int config_output(AVFilterLink *outlink)
1359 AVFilterContext *ctx = outlink->src;
1360 LUT3DContext *lut3d = ctx->priv;
1363 ret = ff_framesync_init_dualinput(&lut3d->fs, ctx);
1366 outlink->w = ctx->inputs[0]->w;
1367 outlink->h = ctx->inputs[0]->h;
1368 outlink->time_base = ctx->inputs[0]->time_base;
1369 if ((ret = ff_framesync_configure(&lut3d->fs)) < 0)
1374 static int activate(AVFilterContext *ctx)
1376 LUT3DContext *s = ctx->priv;
1377 return ff_framesync_activate(&s->fs);
1380 static int config_clut(AVFilterLink *inlink)
1382 int size, level, w, h;
1383 AVFilterContext *ctx = inlink->dst;
1384 LUT3DContext *lut3d = ctx->priv;
1385 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
1389 lut3d->clut_bits = desc->comp[0].depth;
1390 lut3d->clut_planar = av_pix_fmt_count_planes(inlink->format) > 1;
1391 lut3d->clut_float = desc->flags & AV_PIX_FMT_FLAG_FLOAT;
1393 lut3d->clut_step = av_get_padded_bits_per_pixel(desc) >> 3;
1394 ff_fill_rgba_map(lut3d->clut_rgba_map, inlink->format);
1396 if (inlink->w > inlink->h)
1397 av_log(ctx, AV_LOG_INFO, "Padding on the right (%dpx) of the "
1398 "Hald CLUT will be ignored\n", inlink->w - inlink->h);
1399 else if (inlink->w < inlink->h)
1400 av_log(ctx, AV_LOG_INFO, "Padding at the bottom (%dpx) of the "
1401 "Hald CLUT will be ignored\n", inlink->h - inlink->w);
1402 lut3d->clut_width = w = h = FFMIN(inlink->w, inlink->h);
1404 for (level = 1; level*level*level < w; level++);
1405 size = level*level*level;
1407 av_log(ctx, AV_LOG_WARNING, "The Hald CLUT width does not match the level\n");
1408 return AVERROR_INVALIDDATA;
1410 av_assert0(w == h && w == size);
1412 if (level > MAX_LEVEL) {
1413 const int max_clut_level = sqrt(MAX_LEVEL);
1414 const int max_clut_size = max_clut_level*max_clut_level*max_clut_level;
1415 av_log(ctx, AV_LOG_ERROR, "Too large Hald CLUT "
1416 "(maximum level is %d, or %dx%d CLUT)\n",
1417 max_clut_level, max_clut_size, max_clut_size);
1418 return AVERROR(EINVAL);
1421 return allocate_3dlut(ctx, level, 0);
1424 static int update_apply_clut(FFFrameSync *fs)
1426 AVFilterContext *ctx = fs->parent;
1427 LUT3DContext *lut3d = ctx->priv;
1428 AVFilterLink *inlink = ctx->inputs[0];
1429 AVFrame *master, *second, *out;
1432 ret = ff_framesync_dualinput_get(fs, &master, &second);
1436 return ff_filter_frame(ctx->outputs[0], master);
1437 if (lut3d->clut_float)
1438 update_clut_float(ctx->priv, second);
1439 else if (lut3d->clut_planar)
1440 update_clut_planar(ctx->priv, second);
1442 update_clut_packed(ctx->priv, second);
1443 out = apply_lut(inlink, master);
1444 return ff_filter_frame(ctx->outputs[0], out);
1447 static av_cold int haldclut_init(AVFilterContext *ctx)
1449 LUT3DContext *lut3d = ctx->priv;
1450 lut3d->scale.r = lut3d->scale.g = lut3d->scale.b = 1.f;
1451 lut3d->fs.on_event = update_apply_clut;
1455 static av_cold void haldclut_uninit(AVFilterContext *ctx)
1457 LUT3DContext *lut3d = ctx->priv;
1458 ff_framesync_uninit(&lut3d->fs);
1459 av_freep(&lut3d->lut);
1462 static const AVOption haldclut_options[] = {
1466 FRAMESYNC_DEFINE_CLASS(haldclut, LUT3DContext, fs);
1468 static const AVFilterPad haldclut_inputs[] = {
1471 .type = AVMEDIA_TYPE_VIDEO,
1472 .config_props = config_input,
1475 .type = AVMEDIA_TYPE_VIDEO,
1476 .config_props = config_clut,
1481 static const AVFilterPad haldclut_outputs[] = {
1484 .type = AVMEDIA_TYPE_VIDEO,
1485 .config_props = config_output,
1490 AVFilter ff_vf_haldclut = {
1492 .description = NULL_IF_CONFIG_SMALL("Adjust colors using a Hald CLUT."),
1493 .priv_size = sizeof(LUT3DContext),
1494 .preinit = haldclut_framesync_preinit,
1495 .init = haldclut_init,
1496 .uninit = haldclut_uninit,
1497 .query_formats = query_formats,
1498 .activate = activate,
1499 .inputs = haldclut_inputs,
1500 .outputs = haldclut_outputs,
1501 .priv_class = &haldclut_class,
1502 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
1506 #if CONFIG_LUT1D_FILTER
1508 enum interp_1d_mode {
1509 INTERPOLATE_1D_NEAREST,
1510 INTERPOLATE_1D_LINEAR,
1511 INTERPOLATE_1D_CUBIC,
1512 INTERPOLATE_1D_COSINE,
1513 INTERPOLATE_1D_SPLINE,
1517 #define MAX_1D_LEVEL 65536
1519 typedef struct LUT1DContext {
1520 const AVClass *class;
1522 int interpolation; ///<interp_1d_mode
1523 struct rgbvec scale;
1524 uint8_t rgba_map[4];
1526 float lut[3][MAX_1D_LEVEL];
1528 avfilter_action_func *interp;
1532 #define OFFSET(x) offsetof(LUT1DContext, x)
1534 static void set_identity_matrix_1d(LUT1DContext *lut1d, int size)
1536 const float c = 1. / (size - 1);
1539 lut1d->lutsize = size;
1540 for (i = 0; i < size; i++) {
1541 lut1d->lut[0][i] = i * c;
1542 lut1d->lut[1][i] = i * c;
1543 lut1d->lut[2][i] = i * c;
1547 static int parse_cinespace_1d(AVFilterContext *ctx, FILE *f)
1549 LUT1DContext *lut1d = ctx->priv;
1550 char line[MAX_LINE_SIZE];
1551 float in_min[3] = {0.0, 0.0, 0.0};
1552 float in_max[3] = {1.0, 1.0, 1.0};
1553 float out_min[3] = {0.0, 0.0, 0.0};
1554 float out_max[3] = {1.0, 1.0, 1.0};
1555 int inside_metadata = 0, size;
1557 NEXT_LINE(skip_line(line));
1558 if (strncmp(line, "CSPLUTV100", 10)) {
1559 av_log(ctx, AV_LOG_ERROR, "Not cineSpace LUT format\n");
1560 return AVERROR(EINVAL);
1563 NEXT_LINE(skip_line(line));
1564 if (strncmp(line, "1D", 2)) {
1565 av_log(ctx, AV_LOG_ERROR, "Not 1D LUT format\n");
1566 return AVERROR(EINVAL);
1570 NEXT_LINE(skip_line(line));
1572 if (!strncmp(line, "BEGIN METADATA", 14)) {
1573 inside_metadata = 1;
1576 if (!strncmp(line, "END METADATA", 12)) {
1577 inside_metadata = 0;
1580 if (inside_metadata == 0) {
1581 for (int i = 0; i < 3; i++) {
1582 int npoints = strtol(line, NULL, 0);
1585 av_log(ctx, AV_LOG_ERROR, "Unsupported number of pre-lut points.\n");
1586 return AVERROR_PATCHWELCOME;
1589 NEXT_LINE(skip_line(line));
1590 if (av_sscanf(line, "%f %f", &in_min[i], &in_max[i]) != 2)
1591 return AVERROR_INVALIDDATA;
1592 NEXT_LINE(skip_line(line));
1593 if (av_sscanf(line, "%f %f", &out_min[i], &out_max[i]) != 2)
1594 return AVERROR_INVALIDDATA;
1595 NEXT_LINE(skip_line(line));
1598 size = strtol(line, NULL, 0);
1600 if (size < 2 || size > MAX_1D_LEVEL) {
1601 av_log(ctx, AV_LOG_ERROR, "Too large or invalid 1D LUT size\n");
1602 return AVERROR(EINVAL);
1605 lut1d->lutsize = size;
1607 for (int i = 0; i < size; i++) {
1608 NEXT_LINE(skip_line(line));
1609 if (av_sscanf(line, "%f %f %f", &lut1d->lut[0][i], &lut1d->lut[1][i], &lut1d->lut[2][i]) != 3)
1610 return AVERROR_INVALIDDATA;
1611 lut1d->lut[0][i] *= out_max[0] - out_min[0];
1612 lut1d->lut[1][i] *= out_max[1] - out_min[1];
1613 lut1d->lut[2][i] *= out_max[2] - out_min[2];
1620 lut1d->scale.r = av_clipf(1. / (in_max[0] - in_min[0]), 0.f, 1.f);
1621 lut1d->scale.g = av_clipf(1. / (in_max[1] - in_min[1]), 0.f, 1.f);
1622 lut1d->scale.b = av_clipf(1. / (in_max[2] - in_min[2]), 0.f, 1.f);
1627 static int parse_cube_1d(AVFilterContext *ctx, FILE *f)
1629 LUT1DContext *lut1d = ctx->priv;
1630 char line[MAX_LINE_SIZE];
1631 float min[3] = {0.0, 0.0, 0.0};
1632 float max[3] = {1.0, 1.0, 1.0};
1634 while (fgets(line, sizeof(line), f)) {
1635 if (!strncmp(line, "LUT_1D_SIZE", 11)) {
1636 const int size = strtol(line + 12, NULL, 0);
1639 if (size < 2 || size > MAX_1D_LEVEL) {
1640 av_log(ctx, AV_LOG_ERROR, "Too large or invalid 1D LUT size\n");
1641 return AVERROR(EINVAL);
1643 lut1d->lutsize = size;
1644 for (i = 0; i < size; i++) {
1648 if (!strncmp(line, "DOMAIN_", 7)) {
1650 if (!strncmp(line + 7, "MIN ", 4)) vals = min;
1651 else if (!strncmp(line + 7, "MAX ", 4)) vals = max;
1653 return AVERROR_INVALIDDATA;
1654 av_sscanf(line + 11, "%f %f %f", vals, vals + 1, vals + 2);
1655 av_log(ctx, AV_LOG_DEBUG, "min: %f %f %f | max: %f %f %f\n",
1656 min[0], min[1], min[2], max[0], max[1], max[2]);
1658 } else if (!strncmp(line, "LUT_1D_INPUT_RANGE ", 19)) {
1659 av_sscanf(line + 19, "%f %f", min, max);
1660 min[1] = min[2] = min[0];
1661 max[1] = max[2] = max[0];
1663 } else if (!strncmp(line, "TITLE", 5)) {
1666 } while (skip_line(line));
1667 if (av_sscanf(line, "%f %f %f", &lut1d->lut[0][i], &lut1d->lut[1][i], &lut1d->lut[2][i]) != 3)
1668 return AVERROR_INVALIDDATA;
1674 lut1d->scale.r = av_clipf(1. / (max[0] - min[0]), 0.f, 1.f);
1675 lut1d->scale.g = av_clipf(1. / (max[1] - min[1]), 0.f, 1.f);
1676 lut1d->scale.b = av_clipf(1. / (max[2] - min[2]), 0.f, 1.f);
1681 static const AVOption lut1d_options[] = {
1682 { "file", "set 1D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
1683 { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_1D_LINEAR}, 0, NB_INTERP_1D_MODE-1, FLAGS, "interp_mode" },
1684 { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_NEAREST}, INT_MIN, INT_MAX, FLAGS, "interp_mode" },
1685 { "linear", "use values from the linear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_LINEAR}, INT_MIN, INT_MAX, FLAGS, "interp_mode" },
1686 { "cosine", "use values from the cosine interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_COSINE}, INT_MIN, INT_MAX, FLAGS, "interp_mode" },
1687 { "cubic", "use values from the cubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_CUBIC}, INT_MIN, INT_MAX, FLAGS, "interp_mode" },
1688 { "spline", "use values from the spline interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_SPLINE}, INT_MIN, INT_MAX, FLAGS, "interp_mode" },
1692 AVFILTER_DEFINE_CLASS(lut1d);
1694 static inline float interp_1d_nearest(const LUT1DContext *lut1d,
1695 int idx, const float s)
1697 return lut1d->lut[idx][NEAR(s)];
1700 #define NEXT1D(x) (FFMIN((int)(x) + 1, lut1d->lutsize - 1))
1702 static inline float interp_1d_linear(const LUT1DContext *lut1d,
1703 int idx, const float s)
1705 const int prev = PREV(s);
1706 const int next = NEXT1D(s);
1707 const float d = s - prev;
1708 const float p = lut1d->lut[idx][prev];
1709 const float n = lut1d->lut[idx][next];
1711 return lerpf(p, n, d);
1714 static inline float interp_1d_cosine(const LUT1DContext *lut1d,
1715 int idx, const float s)
1717 const int prev = PREV(s);
1718 const int next = NEXT1D(s);
1719 const float d = s - prev;
1720 const float p = lut1d->lut[idx][prev];
1721 const float n = lut1d->lut[idx][next];
1722 const float m = (1.f - cosf(d * M_PI)) * .5f;
1724 return lerpf(p, n, m);
1727 static inline float interp_1d_cubic(const LUT1DContext *lut1d,
1728 int idx, const float s)
1730 const int prev = PREV(s);
1731 const int next = NEXT1D(s);
1732 const float mu = s - prev;
1733 float a0, a1, a2, a3, mu2;
1735 float y0 = lut1d->lut[idx][FFMAX(prev - 1, 0)];
1736 float y1 = lut1d->lut[idx][prev];
1737 float y2 = lut1d->lut[idx][next];
1738 float y3 = lut1d->lut[idx][FFMIN(next + 1, lut1d->lutsize - 1)];
1742 a0 = y3 - y2 - y0 + y1;
1747 return a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3;
1750 static inline float interp_1d_spline(const LUT1DContext *lut1d,
1751 int idx, const float s)
1753 const int prev = PREV(s);
1754 const int next = NEXT1D(s);
1755 const float x = s - prev;
1756 float c0, c1, c2, c3;
1758 float y0 = lut1d->lut[idx][FFMAX(prev - 1, 0)];
1759 float y1 = lut1d->lut[idx][prev];
1760 float y2 = lut1d->lut[idx][next];
1761 float y3 = lut1d->lut[idx][FFMIN(next + 1, lut1d->lutsize - 1)];
1764 c1 = .5f * (y2 - y0);
1765 c2 = y0 - 2.5f * y1 + 2.f * y2 - .5f * y3;
1766 c3 = .5f * (y3 - y0) + 1.5f * (y1 - y2);
1768 return ((c3 * x + c2) * x + c1) * x + c0;
1771 #define DEFINE_INTERP_FUNC_PLANAR_1D(name, nbits, depth) \
1772 static int interp_1d_##nbits##_##name##_p##depth(AVFilterContext *ctx, \
1773 void *arg, int jobnr, \
1777 const LUT1DContext *lut1d = ctx->priv; \
1778 const ThreadData *td = arg; \
1779 const AVFrame *in = td->in; \
1780 const AVFrame *out = td->out; \
1781 const int direct = out == in; \
1782 const int slice_start = (in->height * jobnr ) / nb_jobs; \
1783 const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \
1784 uint8_t *grow = out->data[0] + slice_start * out->linesize[0]; \
1785 uint8_t *brow = out->data[1] + slice_start * out->linesize[1]; \
1786 uint8_t *rrow = out->data[2] + slice_start * out->linesize[2]; \
1787 uint8_t *arow = out->data[3] + slice_start * out->linesize[3]; \
1788 const uint8_t *srcgrow = in->data[0] + slice_start * in->linesize[0]; \
1789 const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1]; \
1790 const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2]; \
1791 const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3]; \
1792 const float factor = (1 << depth) - 1; \
1793 const float scale_r = (lut1d->scale.r / factor) * (lut1d->lutsize - 1); \
1794 const float scale_g = (lut1d->scale.g / factor) * (lut1d->lutsize - 1); \
1795 const float scale_b = (lut1d->scale.b / factor) * (lut1d->lutsize - 1); \
1797 for (y = slice_start; y < slice_end; y++) { \
1798 uint##nbits##_t *dstg = (uint##nbits##_t *)grow; \
1799 uint##nbits##_t *dstb = (uint##nbits##_t *)brow; \
1800 uint##nbits##_t *dstr = (uint##nbits##_t *)rrow; \
1801 uint##nbits##_t *dsta = (uint##nbits##_t *)arow; \
1802 const uint##nbits##_t *srcg = (const uint##nbits##_t *)srcgrow; \
1803 const uint##nbits##_t *srcb = (const uint##nbits##_t *)srcbrow; \
1804 const uint##nbits##_t *srcr = (const uint##nbits##_t *)srcrrow; \
1805 const uint##nbits##_t *srca = (const uint##nbits##_t *)srcarow; \
1806 for (x = 0; x < in->width; x++) { \
1807 float r = srcr[x] * scale_r; \
1808 float g = srcg[x] * scale_g; \
1809 float b = srcb[x] * scale_b; \
1810 r = interp_1d_##name(lut1d, 0, r); \
1811 g = interp_1d_##name(lut1d, 1, g); \
1812 b = interp_1d_##name(lut1d, 2, b); \
1813 dstr[x] = av_clip_uintp2(r * factor, depth); \
1814 dstg[x] = av_clip_uintp2(g * factor, depth); \
1815 dstb[x] = av_clip_uintp2(b * factor, depth); \
1816 if (!direct && in->linesize[3]) \
1817 dsta[x] = srca[x]; \
1819 grow += out->linesize[0]; \
1820 brow += out->linesize[1]; \
1821 rrow += out->linesize[2]; \
1822 arow += out->linesize[3]; \
1823 srcgrow += in->linesize[0]; \
1824 srcbrow += in->linesize[1]; \
1825 srcrrow += in->linesize[2]; \
1826 srcarow += in->linesize[3]; \
1831 DEFINE_INTERP_FUNC_PLANAR_1D(nearest, 8, 8)
1832 DEFINE_INTERP_FUNC_PLANAR_1D(linear, 8, 8)
1833 DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 8, 8)
1834 DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 8, 8)
1835 DEFINE_INTERP_FUNC_PLANAR_1D(spline, 8, 8)
1837 DEFINE_INTERP_FUNC_PLANAR_1D(nearest, 16, 9)
1838 DEFINE_INTERP_FUNC_PLANAR_1D(linear, 16, 9)
1839 DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 16, 9)
1840 DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 16, 9)
1841 DEFINE_INTERP_FUNC_PLANAR_1D(spline, 16, 9)
1843 DEFINE_INTERP_FUNC_PLANAR_1D(nearest, 16, 10)
1844 DEFINE_INTERP_FUNC_PLANAR_1D(linear, 16, 10)
1845 DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 16, 10)
1846 DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 16, 10)
1847 DEFINE_INTERP_FUNC_PLANAR_1D(spline, 16, 10)
1849 DEFINE_INTERP_FUNC_PLANAR_1D(nearest, 16, 12)
1850 DEFINE_INTERP_FUNC_PLANAR_1D(linear, 16, 12)
1851 DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 16, 12)
1852 DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 16, 12)
1853 DEFINE_INTERP_FUNC_PLANAR_1D(spline, 16, 12)
1855 DEFINE_INTERP_FUNC_PLANAR_1D(nearest, 16, 14)
1856 DEFINE_INTERP_FUNC_PLANAR_1D(linear, 16, 14)
1857 DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 16, 14)
1858 DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 16, 14)
1859 DEFINE_INTERP_FUNC_PLANAR_1D(spline, 16, 14)
1861 DEFINE_INTERP_FUNC_PLANAR_1D(nearest, 16, 16)
1862 DEFINE_INTERP_FUNC_PLANAR_1D(linear, 16, 16)
1863 DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 16, 16)
1864 DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 16, 16)
1865 DEFINE_INTERP_FUNC_PLANAR_1D(spline, 16, 16)
1867 #define DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(name, depth) \
1868 static int interp_1d_##name##_pf##depth(AVFilterContext *ctx, \
1869 void *arg, int jobnr, \
1873 const LUT1DContext *lut1d = ctx->priv; \
1874 const ThreadData *td = arg; \
1875 const AVFrame *in = td->in; \
1876 const AVFrame *out = td->out; \
1877 const int direct = out == in; \
1878 const int slice_start = (in->height * jobnr ) / nb_jobs; \
1879 const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \
1880 uint8_t *grow = out->data[0] + slice_start * out->linesize[0]; \
1881 uint8_t *brow = out->data[1] + slice_start * out->linesize[1]; \
1882 uint8_t *rrow = out->data[2] + slice_start * out->linesize[2]; \
1883 uint8_t *arow = out->data[3] + slice_start * out->linesize[3]; \
1884 const uint8_t *srcgrow = in->data[0] + slice_start * in->linesize[0]; \
1885 const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1]; \
1886 const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2]; \
1887 const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3]; \
1888 const float lutsize = lut1d->lutsize - 1; \
1889 const float scale_r = lut1d->scale.r * lutsize; \
1890 const float scale_g = lut1d->scale.g * lutsize; \
1891 const float scale_b = lut1d->scale.b * lutsize; \
1893 for (y = slice_start; y < slice_end; y++) { \
1894 float *dstg = (float *)grow; \
1895 float *dstb = (float *)brow; \
1896 float *dstr = (float *)rrow; \
1897 float *dsta = (float *)arow; \
1898 const float *srcg = (const float *)srcgrow; \
1899 const float *srcb = (const float *)srcbrow; \
1900 const float *srcr = (const float *)srcrrow; \
1901 const float *srca = (const float *)srcarow; \
1902 for (x = 0; x < in->width; x++) { \
1903 float r = av_clipf(sanitizef(srcr[x]) * scale_r, 0.0f, lutsize); \
1904 float g = av_clipf(sanitizef(srcg[x]) * scale_g, 0.0f, lutsize); \
1905 float b = av_clipf(sanitizef(srcb[x]) * scale_b, 0.0f, lutsize); \
1906 r = interp_1d_##name(lut1d, 0, r); \
1907 g = interp_1d_##name(lut1d, 1, g); \
1908 b = interp_1d_##name(lut1d, 2, b); \
1912 if (!direct && in->linesize[3]) \
1913 dsta[x] = srca[x]; \
1915 grow += out->linesize[0]; \
1916 brow += out->linesize[1]; \
1917 rrow += out->linesize[2]; \
1918 arow += out->linesize[3]; \
1919 srcgrow += in->linesize[0]; \
1920 srcbrow += in->linesize[1]; \
1921 srcrrow += in->linesize[2]; \
1922 srcarow += in->linesize[3]; \
1927 DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(nearest, 32)
1928 DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(linear, 32)
1929 DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(cosine, 32)
1930 DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(cubic, 32)
1931 DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(spline, 32)
1933 #define DEFINE_INTERP_FUNC_1D(name, nbits) \
1934 static int interp_1d_##nbits##_##name(AVFilterContext *ctx, void *arg, \
1935 int jobnr, int nb_jobs) \
1938 const LUT1DContext *lut1d = ctx->priv; \
1939 const ThreadData *td = arg; \
1940 const AVFrame *in = td->in; \
1941 const AVFrame *out = td->out; \
1942 const int direct = out == in; \
1943 const int step = lut1d->step; \
1944 const uint8_t r = lut1d->rgba_map[R]; \
1945 const uint8_t g = lut1d->rgba_map[G]; \
1946 const uint8_t b = lut1d->rgba_map[B]; \
1947 const uint8_t a = lut1d->rgba_map[A]; \
1948 const int slice_start = (in->height * jobnr ) / nb_jobs; \
1949 const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \
1950 uint8_t *dstrow = out->data[0] + slice_start * out->linesize[0]; \
1951 const uint8_t *srcrow = in ->data[0] + slice_start * in ->linesize[0]; \
1952 const float factor = (1 << nbits) - 1; \
1953 const float scale_r = (lut1d->scale.r / factor) * (lut1d->lutsize - 1); \
1954 const float scale_g = (lut1d->scale.g / factor) * (lut1d->lutsize - 1); \
1955 const float scale_b = (lut1d->scale.b / factor) * (lut1d->lutsize - 1); \
1957 for (y = slice_start; y < slice_end; y++) { \
1958 uint##nbits##_t *dst = (uint##nbits##_t *)dstrow; \
1959 const uint##nbits##_t *src = (const uint##nbits##_t *)srcrow; \
1960 for (x = 0; x < in->width * step; x += step) { \
1961 float rr = src[x + r] * scale_r; \
1962 float gg = src[x + g] * scale_g; \
1963 float bb = src[x + b] * scale_b; \
1964 rr = interp_1d_##name(lut1d, 0, rr); \
1965 gg = interp_1d_##name(lut1d, 1, gg); \
1966 bb = interp_1d_##name(lut1d, 2, bb); \
1967 dst[x + r] = av_clip_uint##nbits(rr * factor); \
1968 dst[x + g] = av_clip_uint##nbits(gg * factor); \
1969 dst[x + b] = av_clip_uint##nbits(bb * factor); \
1970 if (!direct && step == 4) \
1971 dst[x + a] = src[x + a]; \
1973 dstrow += out->linesize[0]; \
1974 srcrow += in ->linesize[0]; \
1979 DEFINE_INTERP_FUNC_1D(nearest, 8)
1980 DEFINE_INTERP_FUNC_1D(linear, 8)
1981 DEFINE_INTERP_FUNC_1D(cosine, 8)
1982 DEFINE_INTERP_FUNC_1D(cubic, 8)
1983 DEFINE_INTERP_FUNC_1D(spline, 8)
1985 DEFINE_INTERP_FUNC_1D(nearest, 16)
1986 DEFINE_INTERP_FUNC_1D(linear, 16)
1987 DEFINE_INTERP_FUNC_1D(cosine, 16)
1988 DEFINE_INTERP_FUNC_1D(cubic, 16)
1989 DEFINE_INTERP_FUNC_1D(spline, 16)
1991 static int config_input_1d(AVFilterLink *inlink)
1993 int depth, is16bit, isfloat, planar;
1994 LUT1DContext *lut1d = inlink->dst->priv;
1995 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
1997 depth = desc->comp[0].depth;
1998 is16bit = desc->comp[0].depth > 8;
1999 planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR;
2000 isfloat = desc->flags & AV_PIX_FMT_FLAG_FLOAT;
2001 ff_fill_rgba_map(lut1d->rgba_map, inlink->format);
2002 lut1d->step = av_get_padded_bits_per_pixel(desc) >> (3 + is16bit);
2004 #define SET_FUNC_1D(name) do { \
2005 if (planar && !isfloat) { \
2007 case 8: lut1d->interp = interp_1d_8_##name##_p8; break; \
2008 case 9: lut1d->interp = interp_1d_16_##name##_p9; break; \
2009 case 10: lut1d->interp = interp_1d_16_##name##_p10; break; \
2010 case 12: lut1d->interp = interp_1d_16_##name##_p12; break; \
2011 case 14: lut1d->interp = interp_1d_16_##name##_p14; break; \
2012 case 16: lut1d->interp = interp_1d_16_##name##_p16; break; \
2014 } else if (isfloat) { lut1d->interp = interp_1d_##name##_pf32; \
2015 } else if (is16bit) { lut1d->interp = interp_1d_16_##name; \
2016 } else { lut1d->interp = interp_1d_8_##name; } \
2019 switch (lut1d->interpolation) {
2020 case INTERPOLATE_1D_NEAREST: SET_FUNC_1D(nearest); break;
2021 case INTERPOLATE_1D_LINEAR: SET_FUNC_1D(linear); break;
2022 case INTERPOLATE_1D_COSINE: SET_FUNC_1D(cosine); break;
2023 case INTERPOLATE_1D_CUBIC: SET_FUNC_1D(cubic); break;
2024 case INTERPOLATE_1D_SPLINE: SET_FUNC_1D(spline); break;
2032 static av_cold int lut1d_init(AVFilterContext *ctx)
2037 LUT1DContext *lut1d = ctx->priv;
2039 lut1d->scale.r = lut1d->scale.g = lut1d->scale.b = 1.f;
2042 set_identity_matrix_1d(lut1d, 32);
2046 f = av_fopen_utf8(lut1d->file, "r");
2048 ret = AVERROR(errno);
2049 av_log(ctx, AV_LOG_ERROR, "%s: %s\n", lut1d->file, av_err2str(ret));
2053 ext = strrchr(lut1d->file, '.');
2055 av_log(ctx, AV_LOG_ERROR, "Unable to guess the format from the extension\n");
2056 ret = AVERROR_INVALIDDATA;
2061 if (!av_strcasecmp(ext, "cube") || !av_strcasecmp(ext, "1dlut")) {
2062 ret = parse_cube_1d(ctx, f);
2063 } else if (!av_strcasecmp(ext, "csp")) {
2064 ret = parse_cinespace_1d(ctx, f);
2066 av_log(ctx, AV_LOG_ERROR, "Unrecognized '.%s' file type\n", ext);
2067 ret = AVERROR(EINVAL);
2070 if (!ret && !lut1d->lutsize) {
2071 av_log(ctx, AV_LOG_ERROR, "1D LUT is empty\n");
2072 ret = AVERROR_INVALIDDATA;
2080 static AVFrame *apply_1d_lut(AVFilterLink *inlink, AVFrame *in)
2082 AVFilterContext *ctx = inlink->dst;
2083 LUT1DContext *lut1d = ctx->priv;
2084 AVFilterLink *outlink = inlink->dst->outputs[0];
2088 if (av_frame_is_writable(in)) {
2091 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
2096 av_frame_copy_props(out, in);
2101 ctx->internal->execute(ctx, lut1d->interp, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
2109 static int filter_frame_1d(AVFilterLink *inlink, AVFrame *in)
2111 AVFilterLink *outlink = inlink->dst->outputs[0];
2112 AVFrame *out = apply_1d_lut(inlink, in);
2114 return AVERROR(ENOMEM);
2115 return ff_filter_frame(outlink, out);
2118 static const AVFilterPad lut1d_inputs[] = {
2121 .type = AVMEDIA_TYPE_VIDEO,
2122 .filter_frame = filter_frame_1d,
2123 .config_props = config_input_1d,
2128 static const AVFilterPad lut1d_outputs[] = {
2131 .type = AVMEDIA_TYPE_VIDEO,
2136 AVFilter ff_vf_lut1d = {
2138 .description = NULL_IF_CONFIG_SMALL("Adjust colors using a 1D LUT."),
2139 .priv_size = sizeof(LUT1DContext),
2141 .query_formats = query_formats,
2142 .inputs = lut1d_inputs,
2143 .outputs = lut1d_outputs,
2144 .priv_class = &lut1d_class,
2145 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,