Imported Upstream version 6.1
[platform/upstream/ffmpeg.git] / libavfilter / vf_pseudocolor.c
1 /*
2  * Copyright (c) 2017 Paul B Mahol
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 "libavutil/attributes.h"
22 #include "libavutil/common.h"
23 #include "libavutil/eval.h"
24 #include "libavutil/imgutils.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/pixdesc.h"
27 #include "avfilter.h"
28 #include "internal.h"
29 #include "video.h"
30
31 static const char *const var_names[] = {
32     "w",        ///< width of the input video
33     "h",        ///< height of the input video
34     "val",      ///< input value for the pixel
35     "ymin",
36     "umin",
37     "vmin",
38     "amin",
39     "ymax",
40     "umax",
41     "vmax",
42     "amax",
43     NULL
44 };
45
46 enum var_name {
47     VAR_W,
48     VAR_H,
49     VAR_VAL,
50     VAR_YMIN,
51     VAR_UMIN,
52     VAR_VMIN,
53     VAR_AMIN,
54     VAR_YMAX,
55     VAR_UMAX,
56     VAR_VMAX,
57     VAR_AMAX,
58     VAR_VARS_NB
59 };
60
61 enum Curves {
62     MAGMA,
63     INFERNO,
64     PLASMA,
65     VIRIDIS,
66     TURBO,
67     CIVIDIS,
68     SOLAR,
69     SPECTRAL,
70     COOL,
71     HEAT,
72     FIERY,
73     BLUES,
74     GREEN,
75     HELIX,
76     NB_CURVES,
77 };
78
79 enum Presets {
80     PRESET_MAGMA,
81     PRESET_INFERNO,
82     PRESET_PLASMA,
83     PRESET_VIRIDIS,
84     PRESET_TURBO,
85     PRESET_CIVIDIS,
86     PRESET_RANGE1,
87     PRESET_RANGE2,
88     PRESET_SHADOWS,
89     PRESET_HIGHLIGHTS,
90     PRESET_SOLAR,
91     PRESET_NOMINAL,
92     PRESET_PREFERRED,
93     PRESET_TOTAL,
94     PRESET_SPECTRAL,
95     PRESET_COOL,
96     PRESET_HEAT,
97     PRESET_FIERY,
98     PRESET_BLUES,
99     PRESET_GREEN,
100     PRESET_HELIX,
101     NB_PRESETS,
102 };
103
104 typedef double (*curve_fun)(double x);
105
106 typedef struct Curve {
107     double coef[3][8];
108     double offset[3];
109     curve_fun fun[3];
110     int yuv;
111 } Curve;
112
113 typedef struct Fill {
114     float fill[4];
115 } Fill;
116
117 typedef struct Range {
118     AVRational start, end;
119 } Range;
120
121 typedef struct Preset {
122     int nb_segments;
123     const Range *ranges;
124     const Curve *curves;
125     const Fill  *fills;
126 } Preset;
127
128 static const Range full_range   = {{0, 1}, {1, 1}};
129 static const Range nominal_range[] = {{{0, 1}, {4096, 65536}}, {{4096, 65536}, {60161, 65536}}, {{60161, 65536}, {1, 1}}};
130 static const Range preferred_range[] = {{{0, 1}, {1280, 65536}}, {{1280, 65536}, {62977, 65536}}, {{62977, 65536}, {1, 1}}};
131 static const Range total_range[] = {{{0, 1}, {256, 65536}}, {{256, 65536}, {65280, 65536}}, {{65280, 65536}, {1, 1}}};
132 static const Range spec1_range[] = {{{0, 1}, {16, 256}}, {{16, 256}, {236, 256}}, {{236, 256}, {256, 256}}};
133 static const Range spec2_range[] = {{{0, 1}, {16, 256}}, {{16, 256}, {22, 256}}, {{22, 256}, {226, 256}}, {{226, 256}, {236, 256}}, {{236, 256}, {256, 256}}};
134 static const Range shadows_range[] = {{{0, 1}, {32, 256}}, {{32, 256}, {256, 256}}};
135 static const Range highlights_range[] = {{{0,1}, {214,256}}, {{214, 256}, {224, 256}}, {{224, 256}, {256, 256}}};
136
137 static const Fill spec1_fills[] = {{{0.5f, 0.f, .5f, 1.f}}, {{-1.f, -1.f, -1.f, 1.f}}, {{1.f, 0.f, 0.f, 1.f}}};
138 static const Fill spec2_fills[] = {{{0.5f, 0.f, .5f, 1.f}}, {{0.f, 1.f, 1.f, 1.f}}, {{-1.f, -1.f, -1.f, 1.f}}, {{1.f, 1.f, 0.f, 1.f}}, {{1.f, 0.f, 0.f, 1.f}}};
139 static const Fill shadows_fills[] = {{{0.8f, 0.4f, .8f, 1.f}}, {{-1.f, -1.f, -1.f, 1.f}}};
140 static const Fill highlights_fills[] = {{{-1.f, -1.f, -1.f, 1.f}}, {{1.f, 0.3f, 0.6f, 1.f}}, {{1.f, 0.2f, .5f, 1.f}}};
141
142 static double limit(double x)
143 {
144     return av_clipd(x, 0., 1.);
145 }
146
147 static double solarfun(double x)
148 {
149     return 0.5 * sin(x) + 0.5;
150 }
151
152 static double coolfunu(double x)
153 {
154     return 0.25 * sin(2.0 * x * M_PI - M_PI) + 0.5;
155 }
156
157 static double coolfunv(double x)
158 {
159     return 0.25 * sin(2.0 * x * M_PI) + 0.5;
160 }
161
162 static double heatfunu(double x)
163 {
164     return 0.25 * cos(2.0 * x * M_PI + M_PI) + 0.75;
165 }
166
167 static double heatfunv(double x)
168 {
169     return 0.25 * sin(2.0 * x * M_PI) + 0.5;
170 }
171
172 static double fieryfunu(double x)
173 {
174     return 0.75 - 0.25 * cos(2.0 * x * M_PI);
175 }
176
177 static double fieryfunv(double x)
178 {
179     return 0.25 + 0.25 * cos(2.0 * x * M_PI);
180 }
181
182 static double helixfunu(double x)
183 {
184     return 0.5 + 0.15 * sin(5.0 * x * M_PI + M_PI);
185 }
186
187 static double helixfunv(double x)
188 {
189     return 0.5 + 0.15 * cos(6.0 * x * M_PI + M_PI_2);
190 }
191
192 static const Curve curves[] =
193 {
194     [MAGMA] = {{
195         {-7.5631093e-16,  7.4289183e-13, -2.8525484e-10,  5.4446085e-08, -5.5596238e-06,  3.0569325e-04, -2.3137421e-03,  1.2152095e-02 },
196         { 1.3217636e-15, -1.2214648e-12,  4.4319712e-10, -8.0197993e-08,  7.6598370e-06, -3.6523704e-04,  8.4836670e-03, -2.5536888e-02 },
197         {-1.1446568e-15,  1.0013446e-12, -3.5651575e-10,  6.6775016e-08, -6.7120346e-06,  2.7346619e-04,  4.7969657e-03,  1.1971441e-02 },
198     }, .fun = { limit, limit, limit }, },
199     [INFERNO] = {{
200         {-3.9848859e-18,  9.4821649e-14, -6.7371977e-11,  1.8469937e-08, -2.5359307e-06,  1.7959053e-04,  3.9782564e-04,  2.8845935e-04 },
201         { 6.8408539e-16, -6.5499979e-13,  2.4562526e-10, -4.5989298e-08,  4.5723324e-06, -2.2111913e-04,  5.2023164e-03, -1.1226064e-02 },
202         {-2.9921470e-15,  2.5864165e-12, -8.7403799e-10,  1.4713388e-07, -1.2701505e-05,  4.5159935e-04,  3.1087989e-03,  1.9122831e-02 },
203     }, .fun = { limit, limit, limit }, },
204     [PLASMA] = {{
205         { 3.6196089e-16, -3.3623041e-13,  1.2324010e-10, -2.2769060e-08,  2.2297792e-06, -1.2567829e-04,  9.9791629e-03,  5.7247918e-02 },
206         { 5.0262888e-16, -5.3193896e-13,  2.2451715e-10, -4.7529623e-08,  5.1374873e-06, -2.3260136e-04,  3.1502825e-03,  1.5362491e-02 },
207         {-1.7782261e-16,  2.2487839e-13, -1.0610236e-10,  2.4112644e-08, -2.6331623e-06,  8.9499751e-05,  2.1386328e-03,  5.3824268e-01 },
208     }, .fun = { limit, limit, limit }, },
209     [VIRIDIS] = {{
210         { 9.4850045e-16, -8.6629383e-13,  3.0310944e-10, -5.1340396e-08,  4.6024275e-06, -2.2744239e-04,  4.5559993e-03,  2.5662350e-01 },
211         { 9.6461041e-17, -6.9209477e-14,  1.7625397e-11, -2.0229773e-09,  1.4900110e-07, -1.9315187e-05,  5.8967339e-03,  3.9544827e-03 },
212         { 5.1785449e-16, -3.6663004e-13,  1.0249990e-10, -1.5431998e-08,  1.5007941e-06, -1.2001502e-04,  7.6951526e-03,  3.2292815e-01 },
213     }, .fun = { limit, limit, limit }, },
214     [TURBO] = {{
215         {-4.3683890e-15,  3.7020347e-12, -1.1712592e-09,  1.6401790e-07, -8.6842919e-06, -1.8542465e-06,  8.4485325e-03,  1.6267077e-01 },
216         {-4.0011069e-16,  2.7861423e-13, -6.3388921e-11,  5.8872238e-09, -5.4466522e-07,  1.8037114e-05,  1.0599869e-02,  7.6914696e-02 },
217         {-2.8242609e-15,  2.9234108e-12, -1.1726546e-09,  2.2552115e-07, -2.0059387e-05,  5.0595552e-04,  1.7714932e-02,  2.7271836e-01 },
218     }, .fun = { limit, limit, limit }, },
219     [CIVIDIS] = {{
220         {-9.5484131e-16,  9.6988184e-13, -4.0058766e-10,  8.5743924e-08, -9.9644797e-06,  5.9197908e-04, -1.0361579e-02,  3.3164429e-02 },
221         { 1.2731941e-17, -9.4238449e-15,  2.2808841e-12, -1.1548296e-10, -2.3888913e-08,  3.8986680e-06,  2.5879330e-03,  1.2769733e-01 },
222         { 4.6004608e-16, -5.0686849e-13,  2.2753449e-10, -5.3074099e-08,  6.7196096e-06, -4.4120020e-04,  1.3435551e-02,  2.8293355e-01 },
223     }, .fun = { limit, limit, limit }, },
224     [SOLAR] = {{
225         { 0, 0, 0, 0, 0.000001983938313, -0.0007618323, 0.2, -M_PI_2 },
226         { 0, 0, 0, 0, 0.000001983938313, -0.0007618323, 0.2, -M_PI_2 },
227         { 0, 0, 0, 0, 0.000001983938313, -0.0007618323, 0.2, -M_PI_2 },
228     },
229     .offset = { 0., -9., 9. },
230     .fun = { solarfun, solarfun, solarfun }, },
231     [SPECTRAL] = {{
232         { -1.6820e-15,   1.4982e-12,  -5.0442e-10,   8.0490e-08,  -6.1903e-06,   1.5821e-04, 6.4359e-03,   6.2887e-01 },
233         {  1.2526e-15,  -1.2203e-12,   4.7013e-10,  -8.9360e-08,   8.3839e-06,  -3.6642e-04, 1.4784e-02,  -9.8075e-03 },
234         {  1.4755e-15,  -1.6765e-12,   7.3188e-10,  -1.5522e-07,   1.6406e-05,  -7.7883e-04, 1.4502e-02,   2.1597e-01 },
235     }, .fun = { limit, limit, limit }, },
236     [COOL] = {{
237         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
238         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
239         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
240     },
241     .offset = { 0., 0., 0 },
242     .yuv = 1,
243     .fun = { coolfunu, limit, coolfunv }, },
244     [HEAT] = {{
245         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
246         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
247         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
248     },
249     .offset = { 0., 0., 0 },
250     .yuv = 1,
251     .fun = { heatfunu, limit, heatfunv }, },
252     [FIERY] = {{
253         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
254         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
255         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
256     },
257     .offset = { 0., 0., 0 },
258     .yuv = 1,
259     .fun = { fieryfunu, limit, fieryfunv }, },
260     [BLUES] = {{
261         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
262         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
263         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
264     },
265     .offset = { 0., 0., 0 },
266     .yuv = 1,
267     .fun = { fieryfunv, limit, fieryfunu }, },
268     [GREEN] = {{
269         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
270         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
271         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
272     },
273     .offset = { 0., 0., 0 },
274     .yuv = 1,
275     .fun = { fieryfunv, limit, fieryfunv }, },
276     [HELIX] = {{
277         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
278         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
279         { 0, 0, 0, 0, 0, 0, 1./256, 0 },
280     },
281     .offset = { 0., 0., 0 },
282     .yuv = 1,
283     .fun = { helixfunu, limit, helixfunv }, },
284 };
285
286 static const Preset presets[] =
287 {
288     [PRESET_MAGMA]   = { 1, &full_range, &curves[MAGMA],   NULL },
289     [PRESET_INFERNO] = { 1, &full_range, &curves[INFERNO], NULL },
290     [PRESET_PLASMA]  = { 1, &full_range, &curves[PLASMA],  NULL },
291     [PRESET_VIRIDIS] = { 1, &full_range, &curves[VIRIDIS], NULL },
292     [PRESET_TURBO]   = { 1, &full_range, &curves[TURBO],   NULL },
293     [PRESET_CIVIDIS] = { 1, &full_range, &curves[CIVIDIS], NULL },
294     [PRESET_RANGE1]  = { 3, spec1_range, NULL,             spec1_fills },
295     [PRESET_NOMINAL] = { 3, nominal_range, NULL,           spec1_fills },
296     [PRESET_PREFERRED]={ 3, preferred_range, NULL,         spec1_fills },
297     [PRESET_TOTAL]   = { 3, total_range, NULL,             spec1_fills },
298     [PRESET_RANGE2]  = { 5, spec2_range, NULL,             spec2_fills },
299     [PRESET_SHADOWS] = { 2, shadows_range, NULL,           shadows_fills },
300     [PRESET_HIGHLIGHTS] = { 3, highlights_range, NULL,     highlights_fills },
301     [PRESET_SOLAR]   = { 1, &full_range, &curves[SOLAR],   NULL },
302     [PRESET_SPECTRAL]= { 1, &full_range, &curves[SPECTRAL],NULL },
303     [PRESET_COOL]    = { 1, &full_range, &curves[COOL],    NULL },
304     [PRESET_HEAT]    = { 1, &full_range, &curves[HEAT],    NULL },
305     [PRESET_FIERY]   = { 1, &full_range, &curves[FIERY],   NULL },
306     [PRESET_BLUES]   = { 1, &full_range, &curves[BLUES],   NULL },
307     [PRESET_GREEN]   = { 1, &full_range, &curves[GREEN],   NULL },
308     [PRESET_HELIX]   = { 1, &full_range, &curves[HELIX],   NULL },
309 };
310
311 typedef struct PseudoColorContext {
312     const AVClass *class;
313     int preset;
314     float opacity;
315     int max;
316     int index;
317     int nb_planes;
318     int color;
319     int linesize[4];
320     int width[4], height[4];
321     double var_values[VAR_VARS_NB];
322     char   *comp_expr_str[4];
323     AVExpr *comp_expr[4];
324     float lut[4][256*256];
325
326     void (*filter[4])(int max, int width, int height,
327                       const uint8_t *index, const uint8_t *src,
328                       uint8_t *dst,
329                       ptrdiff_t ilinesize,
330                       ptrdiff_t slinesize,
331                       ptrdiff_t dlinesize,
332                       float *lut,
333                       float opacity);
334 } PseudoColorContext;
335
336 #define OFFSET(x) offsetof(PseudoColorContext, x)
337 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
338
339 static const AVOption pseudocolor_options[] = {
340     { "c0", "set component #0 expression", OFFSET(comp_expr_str[0]), AV_OPT_TYPE_STRING, {.str="val"},   .flags = FLAGS },
341     { "c1", "set component #1 expression", OFFSET(comp_expr_str[1]), AV_OPT_TYPE_STRING, {.str="val"},   .flags = FLAGS },
342     { "c2", "set component #2 expression", OFFSET(comp_expr_str[2]), AV_OPT_TYPE_STRING, {.str="val"},   .flags = FLAGS },
343     { "c3", "set component #3 expression", OFFSET(comp_expr_str[3]), AV_OPT_TYPE_STRING, {.str="val"},   .flags = FLAGS },
344     { "index", "set component as base",    OFFSET(index),            AV_OPT_TYPE_INT,    {.i64=0}, 0, 3, .flags = FLAGS },
345     { "i",  "set component as base",       OFFSET(index),            AV_OPT_TYPE_INT,    {.i64=0}, 0, 3, .flags = FLAGS },
346     { "preset", "set preset",              OFFSET(preset),           AV_OPT_TYPE_INT,    {.i64=-1},-1, NB_PRESETS-1, .flags = FLAGS, "preset" },
347     { "p",  "set preset",                  OFFSET(preset),           AV_OPT_TYPE_INT,    {.i64=-1},-1, NB_PRESETS-1, .flags = FLAGS, "preset" },
348     { "none",       NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=-1},             .flags = FLAGS, "preset" },
349     { "magma",      NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_MAGMA},   .flags = FLAGS, "preset" },
350     { "inferno",    NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_INFERNO}, .flags = FLAGS, "preset" },
351     { "plasma",     NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_PLASMA},  .flags = FLAGS, "preset" },
352     { "viridis",    NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_VIRIDIS}, .flags = FLAGS, "preset" },
353     { "turbo",      NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_TURBO},   .flags = FLAGS, "preset" },
354     { "cividis",    NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_CIVIDIS}, .flags = FLAGS, "preset" },
355     { "range1",     NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_RANGE1},  .flags = FLAGS, "preset" },
356     { "range2",     NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_RANGE2},  .flags = FLAGS, "preset" },
357     { "shadows",    NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_SHADOWS}, .flags = FLAGS, "preset" },
358     { "highlights", NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_HIGHLIGHTS},.flags=FLAGS, "preset" },
359     { "solar",      NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_SOLAR},   .flags=FLAGS, "preset" },
360     { "nominal",    NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_NOMINAL}, .flags=FLAGS, "preset" },
361     { "preferred",  NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_PREFERRED},.flags=FLAGS,"preset" },
362     { "total",      NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_TOTAL},   .flags=FLAGS, "preset" },
363     { "spectral",   NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_SPECTRAL},.flags = FLAGS, "preset" },
364     { "cool",       NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_COOL},    .flags = FLAGS, "preset" },
365     { "heat",       NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_HEAT},    .flags = FLAGS, "preset" },
366     { "fiery",      NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_FIERY},   .flags = FLAGS, "preset" },
367     { "blues",      NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_BLUES},   .flags = FLAGS, "preset" },
368     { "green",      NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_GREEN},   .flags = FLAGS, "preset" },
369     { "helix",      NULL,                  0,                        AV_OPT_TYPE_CONST,  {.i64=PRESET_HELIX},   .flags = FLAGS, "preset" },
370     { "opacity", "set pseudocolor opacity",OFFSET(opacity),          AV_OPT_TYPE_FLOAT,  {.dbl=1}, 0, 1, .flags = FLAGS },
371     { NULL }
372 };
373
374 static const enum AVPixelFormat pix_fmts[] = {
375     AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
376     AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P,
377     AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA422P,
378     AV_PIX_FMT_YUV444P, AV_PIX_FMT_GBRP,
379     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_GBRAP,
380     AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUVA422P9,
381     AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUVA420P9,
382     AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUVA444P9,
383     AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUVA420P10,
384     AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUVA422P10,
385     AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUVA444P10,
386     AV_PIX_FMT_YUV420P12,
387     AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUVA422P12,
388     AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUVA444P12,
389     AV_PIX_FMT_YUV420P14,
390     AV_PIX_FMT_YUV422P14,
391     AV_PIX_FMT_YUV444P14,
392     AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUVA420P16,
393     AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUVA422P16,
394     AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUVA444P16,
395     AV_PIX_FMT_GBRP9,
396     AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
397     AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12,
398     AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRAP14,
399     AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16,
400     AV_PIX_FMT_NONE
401 };
402
403 static inline float lerpf(float v0, float v1, float f)
404 {
405     return v0 + (v1 - v0) * f;
406 }
407
408 #define PCLIP(v, max, dst, src, x) \
409     if (v >= 0 && v <= max) {      \
410         dst[x] = lerpf(src[x], v, opacity);\
411     } else {                       \
412         dst[x] = src[x];           \
413     }
414
415 static void pseudocolor_filter(int max, int width, int height,
416                                const uint8_t *index,
417                                const uint8_t *src,
418                                uint8_t *dst,
419                                ptrdiff_t ilinesize,
420                                ptrdiff_t slinesize,
421                                ptrdiff_t dlinesize,
422                                float *lut,
423                                float opacity)
424 {
425     int x, y;
426
427     for (y = 0; y < height; y++) {
428         for (x = 0; x < width; x++) {
429             int v = lut[index[x]];
430
431             PCLIP(v, max, dst, src, x);
432         }
433         index += ilinesize;
434         src += slinesize;
435         dst += dlinesize;
436     }
437 }
438
439 static void pseudocolor_filter_11(int max, int width, int height,
440                                   const uint8_t *index,
441                                   const uint8_t *src,
442                                   uint8_t *dst,
443                                   ptrdiff_t ilinesize,
444                                   ptrdiff_t slinesize,
445                                   ptrdiff_t dlinesize,
446                                   float *lut,
447                                   float opacity)
448 {
449     int x, y;
450
451     for (y = 0; y < height; y++) {
452         for (x = 0; x < width; x++) {
453             int v = lut[index[(y << 1) * ilinesize + (x << 1)]];
454
455             PCLIP(v, max, dst, src, x);
456         }
457         src += slinesize;
458         dst += dlinesize;
459     }
460 }
461
462 static void pseudocolor_filter_11d(int max, int width, int height,
463                                    const uint8_t *index,
464                                    const uint8_t *src,
465                                    uint8_t *dst,
466                                    ptrdiff_t ilinesize,
467                                    ptrdiff_t slinesize,
468                                    ptrdiff_t dlinesize,
469                                    float *lut,
470                                    float opacity)
471 {
472     int x, y;
473
474     for (y = 0; y < height; y++) {
475         for (x = 0; x < width; x++) {
476             int v = lut[index[(y >> 1) * ilinesize + (x >> 1)]];
477
478             PCLIP(v, max, dst, src, x);
479         }
480         src += slinesize;
481         dst += dlinesize;
482     }
483 }
484
485 static void pseudocolor_filter_10(int max, int width, int height,
486                                   const uint8_t *index,
487                                   const uint8_t *src,
488                                   uint8_t *dst,
489                                   ptrdiff_t ilinesize,
490                                   ptrdiff_t slinesize,
491                                   ptrdiff_t dlinesize,
492                                   float *lut,
493                                   float opacity)
494 {
495     int x, y;
496
497     for (y = 0; y < height; y++) {
498         for (x = 0; x < width; x++) {
499             int v = lut[index[x << 1]];
500
501             PCLIP(v, max, dst, src, x);
502         }
503         index += ilinesize;
504         src += slinesize;
505         dst += dlinesize;
506     }
507 }
508
509 static void pseudocolor_filter_10d(int max, int width, int height,
510                                    const uint8_t *index,
511                                    const uint8_t *src,
512                                    uint8_t *dst,
513                                    ptrdiff_t ilinesize,
514                                    ptrdiff_t slinesize,
515                                    ptrdiff_t dlinesize,
516                                    float *lut,
517                                    float opacity)
518 {
519     int x, y;
520
521     for (y = 0; y < height; y++) {
522         for (x = 0; x < width; x++) {
523             int v = lut[index[x >> 1]];
524
525             PCLIP(v, max, dst, src, x);
526         }
527         index += ilinesize;
528         src += slinesize;
529         dst += dlinesize;
530     }
531 }
532
533 static void pseudocolor_filter_16(int max, int width, int height,
534                                   const uint8_t *iindex,
535                                   const uint8_t *ssrc,
536                                   uint8_t *ddst,
537                                   ptrdiff_t ilinesize,
538                                   ptrdiff_t slinesize,
539                                   ptrdiff_t dlinesize,
540                                   float *lut,
541                                   float opacity)
542 {
543     const uint16_t *index = (const uint16_t *)iindex;
544     const uint16_t *src = (const uint16_t *)ssrc;
545     uint16_t *dst = (uint16_t *)ddst;
546     int x, y;
547
548     for (y = 0; y < height; y++) {
549         for (x = 0; x < width; x++) {
550             int v = lut[index[x]];
551
552             PCLIP(v, max, dst, src, x);
553         }
554         index += ilinesize / 2;
555         src += slinesize / 2;
556         dst += dlinesize / 2;
557     }
558 }
559
560 static void pseudocolor_filter_16_10(int max, int width, int height,
561                                      const uint8_t *iindex,
562                                      const uint8_t *ssrc,
563                                      uint8_t *ddst,
564                                      ptrdiff_t ilinesize,
565                                      ptrdiff_t slinesize,
566                                      ptrdiff_t dlinesize,
567                                      float *lut,
568                                      float opacity)
569 {
570     const uint16_t *index = (const uint16_t *)iindex;
571     const uint16_t *src = (const uint16_t *)ssrc;
572     uint16_t *dst = (uint16_t *)ddst;
573     int x, y;
574
575     for (y = 0; y < height; y++) {
576         for (x = 0; x < width; x++) {
577             int v = lut[index[x << 1]];
578
579             PCLIP(v, max, dst, src, x);
580         }
581         index += ilinesize / 2;
582         src += slinesize / 2;
583         dst += dlinesize / 2;
584     }
585 }
586
587 static void pseudocolor_filter_16_10d(int max, int width, int height,
588                                       const uint8_t *iindex,
589                                       const uint8_t *ssrc,
590                                       uint8_t *ddst,
591                                       ptrdiff_t ilinesize,
592                                       ptrdiff_t slinesize,
593                                       ptrdiff_t dlinesize,
594                                       float *lut,
595                                       float opacity)
596 {
597     const uint16_t *index = (const uint16_t *)iindex;
598     const uint16_t *src = (const uint16_t *)ssrc;
599     uint16_t *dst = (uint16_t *)ddst;
600     int x, y;
601
602     for (y = 0; y < height; y++) {
603         for (x = 0; x < width; x++) {
604             int v = lut[index[x >> 1]];
605
606             PCLIP(v, max, dst, src, x);
607         }
608         index += ilinesize / 2;
609         src += slinesize / 2;
610         dst += dlinesize / 2;
611     }
612 }
613
614 static void pseudocolor_filter_16_11(int max, int width, int height,
615                                      const uint8_t *iindex,
616                                      const uint8_t *ssrc,
617                                      uint8_t *ddst,
618                                      ptrdiff_t ilinesize,
619                                      ptrdiff_t slinesize,
620                                      ptrdiff_t dlinesize,
621                                      float *lut,
622                                      float opacity)
623 {
624     const uint16_t *index = (const uint16_t *)iindex;
625     const uint16_t *src = (const uint16_t *)ssrc;
626     uint16_t *dst = (uint16_t *)ddst;
627     int x, y;
628
629     ilinesize /= 2;
630     dlinesize /= 2;
631     slinesize /= 2;
632
633     for (y = 0; y < height; y++) {
634         for (x = 0; x < width; x++) {
635             int v = lut[index[(y << 1) * ilinesize + (x << 1)]];
636
637             PCLIP(v, max, dst, src, x);
638         }
639         src += slinesize;
640         dst += dlinesize;
641     }
642 }
643
644 static void pseudocolor_filter_16_11d(int max, int width, int height,
645                                       const uint8_t *iindex,
646                                       const uint8_t *ssrc,
647                                       uint8_t *ddst,
648                                       ptrdiff_t ilinesize,
649                                       ptrdiff_t slinesize,
650                                       ptrdiff_t dlinesize,
651                                       float *lut,
652                                       float opacity)
653 {
654     const uint16_t *index = (const uint16_t *)iindex;
655     const uint16_t *src = (const uint16_t *)ssrc;
656     uint16_t *dst = (uint16_t *)ddst;
657     int x, y;
658
659     ilinesize /= 2;
660     dlinesize /= 2;
661     slinesize /= 2;
662
663     for (y = 0; y < height; y++) {
664         for (x = 0; x < width; x++) {
665             int v = lut[index[(y >> 1) * ilinesize + (x >> 1)]];
666
667             PCLIP(v, max, dst, src, x);
668         }
669         src += slinesize;
670         dst += dlinesize;
671     }
672 }
673
674 #define RGB_TO_Y_BT709(r, g, b) \
675 ((0.21260*219.0/255.0) * (r) + (0.71520*219.0/255.0) * (g) + \
676  (0.07220*219.0/255.0) * (b))
677
678 #define RGB_TO_U_BT709(r1, g1, b1, max) \
679 (-(0.11457*224.0/255.0) * r1 - (0.38543*224.0/255.0) * g1 + \
680     (0.50000*224.0/255.0) * b1 + max * 0.5)
681
682 #define RGB_TO_V_BT709(r1, g1, b1, max) \
683 ((0.50000*224.0/255.0) * r1 - (0.45415*224.0/255.0) * g1 - \
684    (0.04585*224.0/255.0) * b1 + max * 0.5)
685
686 #define Wr 0.2126
687 #define Wb 0.0722
688 #define Wg (1 - Wr - Wb)
689 #define Umax 0.436
690 #define Vmax 0.615
691
692 #define YUV_BT709_TO_R(y, u, v, max) \
693     ((y + v * (1 - Wr) / Vmax) * max)
694 #define YUV_BT709_TO_G(y, u, v, max) \
695     ((y - (u * Wb * (1 - Wb) / (Umax * Wg)) - (v * Wr * (1 - Wr) / (Vmax * Wg))) * max)
696 #define YUV_BT709_TO_B(y, u, v, max) \
697     ((y + u * (1 - Wb) / Umax) * max)
698
699 static double poly_eval(const double *const poly, double x, curve_fun fun)
700 {
701     double res = 0.;
702
703     for (int i = 0; i < 8; i++) {
704         res += pow(x, i) * poly[7-i];
705     }
706
707     return fun(res);
708 }
709
710 static int config_input(AVFilterLink *inlink)
711 {
712     AVFilterContext *ctx = inlink->dst;
713     PseudoColorContext *s = ctx->priv;
714     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
715     int depth, ret, hsub, vsub, color, rgb;
716
717     rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
718     depth = desc->comp[0].depth;
719     s->max = (1 << depth) - 1;
720     s->nb_planes = av_pix_fmt_count_planes(inlink->format);
721
722     if (s->index >= s->nb_planes) {
723         av_log(ctx, AV_LOG_ERROR, "index out of allowed range\n");
724         return AVERROR(EINVAL);
725     }
726
727     if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0)
728         return ret;
729
730     hsub = desc->log2_chroma_w;
731     vsub = desc->log2_chroma_h;
732     s->height[1] = s->height[2] = AV_CEIL_RSHIFT(inlink->h, vsub);
733     s->height[0] = s->height[3] = inlink->h;
734     s->width[1]  = s->width[2]  = AV_CEIL_RSHIFT(inlink->w, hsub);
735     s->width[0]  = s->width[3]  = inlink->w;
736
737     s->var_values[VAR_W] = inlink->w;
738     s->var_values[VAR_H] = inlink->h;
739
740     s->var_values[VAR_YMIN] = 16 * (1 << (depth - 8));
741     s->var_values[VAR_UMIN] = 16 * (1 << (depth - 8));
742     s->var_values[VAR_VMIN] = 16 * (1 << (depth - 8));
743     s->var_values[VAR_AMIN] = 0;
744     s->var_values[VAR_YMAX] = 235 * (1 << (depth - 8));
745     s->var_values[VAR_UMAX] = 240 * (1 << (depth - 8));
746     s->var_values[VAR_VMAX] = 240 * (1 << (depth - 8));
747     s->var_values[VAR_AMAX] = s->max;
748
749     for (color = 0; color < s->nb_planes && s->preset < 0; color++) {
750         double res;
751         int val;
752
753         /* create the parsed expression */
754         av_expr_free(s->comp_expr[color]);
755         s->comp_expr[color] = NULL;
756         ret = av_expr_parse(&s->comp_expr[color], s->comp_expr_str[color],
757                             var_names, NULL, NULL, NULL, NULL, 0, ctx);
758         if (ret < 0) {
759             av_log(ctx, AV_LOG_ERROR,
760                    "Error when parsing the expression '%s' for the component %d and color %d.\n",
761                    s->comp_expr_str[color], color, color);
762             return AVERROR(EINVAL);
763         }
764
765         /* compute the lut */
766         for (val = 0; val < FF_ARRAY_ELEMS(s->lut[color]); val++) {
767             s->var_values[VAR_VAL] = val;
768
769             res = av_expr_eval(s->comp_expr[color], s->var_values, s);
770             if (isnan(res)) {
771                 av_log(ctx, AV_LOG_ERROR,
772                        "Error when evaluating the expression '%s' for the value %d for the component %d.\n",
773                        s->comp_expr_str[color], val, color);
774                 return AVERROR(EINVAL);
775             }
776             s->lut[color][val] = res;
777         }
778     }
779
780     if (s->preset >= 0) {
781         int nb_segments = presets[s->preset].nb_segments;
782
783         for (int seg = 0; seg < nb_segments; seg++) {
784             AVRational rstart = presets[s->preset].ranges[seg].start;
785             AVRational rend   = presets[s->preset].ranges[seg].end;
786             int start = av_rescale_rnd(s->max + 1, rstart.num, rstart.den, AV_ROUND_UP);
787             int end   = av_rescale_rnd(s->max + 1, rend.num, rend.den, AV_ROUND_UP);
788
789             for (int i = start; i < end; i++) {
790                 if (!presets[s->preset].curves) {
791                     const Fill fill = presets[s->preset].fills[seg];
792                     double r, g, b, a;
793
794                     g = fill.fill[1];
795                     b = fill.fill[2];
796                     r = fill.fill[0];
797                     a = fill.fill[3];
798
799                     if (g >= 0.f && b >= 0.f && r >= 0.f) {
800                         g *= s->max;
801                         b *= s->max;
802                         r *= s->max;
803
804                         if (!rgb) {
805                             double y = RGB_TO_Y_BT709(r, g, b);
806                             double u = RGB_TO_U_BT709(r, g, b, s->max);
807                             double v = RGB_TO_V_BT709(r, g, b, s->max);
808
809                             r = v;
810                             g = y;
811                             b = u;
812                         }
813                     }
814
815                     s->lut[0][i] = g;
816                     s->lut[1][i] = b;
817                     s->lut[2][i] = r;
818                     s->lut[3][i] = a * s->max;
819                 } else {
820                     const Curve curve = presets[s->preset].curves[seg];
821                     const double lf = i / (double)s->max * 256.;
822                     double r, g, b;
823
824                     g = poly_eval(curve.coef[1], lf + curve.offset[1], curve.fun[1]);
825                     b = poly_eval(curve.coef[2], lf + curve.offset[2], curve.fun[2]);
826                     r = poly_eval(curve.coef[0], lf + curve.offset[0], curve.fun[0]);
827
828                     if (!curve.yuv || !rgb) {
829                         g *= s->max;
830                         b *= s->max;
831                         r *= s->max;
832                     }
833
834                     if (!rgb && !curve.yuv) {
835                         double y = RGB_TO_Y_BT709(r, g, b);
836                         double u = RGB_TO_U_BT709(r, g, b, s->max);
837                         double v = RGB_TO_V_BT709(r, g, b, s->max);
838
839                         r = v;
840                         g = y;
841                         b = u;
842                     } else if (rgb && curve.yuv) {
843                         double y = g;
844                         double u = b - 0.5;
845                         double v = r - 0.5;
846
847                         r = av_clipd(YUV_BT709_TO_R(y, u, v, s->max), 0, s->max);
848                         g = av_clipd(YUV_BT709_TO_G(y, u, v, s->max), 0, s->max);
849                         b = av_clipd(YUV_BT709_TO_B(y, u, v, s->max), 0, s->max);
850                     }
851
852                     s->lut[0][i] = g;
853                     s->lut[1][i] = b;
854                     s->lut[2][i] = r;
855                     s->lut[3][i] = 1.f * s->max;
856                 }
857             }
858         }
859     }
860
861     switch (inlink->format) {
862     case AV_PIX_FMT_YUV444P:
863     case AV_PIX_FMT_YUVA444P:
864     case AV_PIX_FMT_GBRP:
865     case AV_PIX_FMT_GBRAP:
866     case AV_PIX_FMT_GRAY8:
867         s->filter[0] = s->filter[1] = s->filter[2] = s->filter[3] = pseudocolor_filter;
868         break;
869     case AV_PIX_FMT_YUV420P:
870     case AV_PIX_FMT_YUVA420P:
871         switch (s->index) {
872         case 0:
873         case 3:
874             s->filter[0] = s->filter[3] = pseudocolor_filter;
875             s->filter[1] = s->filter[2] = pseudocolor_filter_11;
876             break;
877         case 1:
878         case 2:
879             s->filter[0] = s->filter[3] = pseudocolor_filter_11d;
880             s->filter[1] = s->filter[2] = pseudocolor_filter;
881             break;
882         }
883         break;
884     case AV_PIX_FMT_YUV422P:
885     case AV_PIX_FMT_YUVA422P:
886         switch (s->index) {
887         case 0:
888         case 3:
889             s->filter[0] = s->filter[3] = pseudocolor_filter;
890             s->filter[1] = s->filter[2] = pseudocolor_filter_10;
891             break;
892         case 1:
893         case 2:
894             s->filter[0] = s->filter[3] = pseudocolor_filter_10d;
895             s->filter[1] = s->filter[2] = pseudocolor_filter;
896             break;
897         }
898         break;
899     case AV_PIX_FMT_YUV444P9:
900     case AV_PIX_FMT_YUVA444P9:
901     case AV_PIX_FMT_YUV444P10:
902     case AV_PIX_FMT_YUVA444P10:
903     case AV_PIX_FMT_YUV444P12:
904     case AV_PIX_FMT_YUVA444P12:
905     case AV_PIX_FMT_YUV444P14:
906     case AV_PIX_FMT_YUV444P16:
907     case AV_PIX_FMT_YUVA444P16:
908     case AV_PIX_FMT_GBRP9:
909     case AV_PIX_FMT_GBRP10:
910     case AV_PIX_FMT_GBRP12:
911     case AV_PIX_FMT_GBRP14:
912     case AV_PIX_FMT_GBRP16:
913     case AV_PIX_FMT_GBRAP10:
914     case AV_PIX_FMT_GBRAP12:
915     case AV_PIX_FMT_GBRAP14:
916     case AV_PIX_FMT_GBRAP16:
917     case AV_PIX_FMT_GRAY9:
918     case AV_PIX_FMT_GRAY10:
919     case AV_PIX_FMT_GRAY12:
920     case AV_PIX_FMT_GRAY14:
921     case AV_PIX_FMT_GRAY16:
922         s->filter[0] = s->filter[1] = s->filter[2] = s->filter[3] = pseudocolor_filter_16;
923         break;
924     case AV_PIX_FMT_YUV422P9:
925     case AV_PIX_FMT_YUVA422P9:
926     case AV_PIX_FMT_YUV422P10:
927     case AV_PIX_FMT_YUVA422P10:
928     case AV_PIX_FMT_YUV422P12:
929     case AV_PIX_FMT_YUVA422P12:
930     case AV_PIX_FMT_YUV422P14:
931     case AV_PIX_FMT_YUV422P16:
932     case AV_PIX_FMT_YUVA422P16:
933         switch (s->index) {
934         case 0:
935         case 3:
936             s->filter[0] = s->filter[3] = pseudocolor_filter_16;
937             s->filter[1] = s->filter[2] = pseudocolor_filter_16_10;
938             break;
939         case 1:
940         case 2:
941             s->filter[0] = s->filter[3] = pseudocolor_filter_16_10d;
942             s->filter[1] = s->filter[2] = pseudocolor_filter_16;
943             break;
944         }
945         break;
946     case AV_PIX_FMT_YUV420P9:
947     case AV_PIX_FMT_YUVA420P9:
948     case AV_PIX_FMT_YUV420P10:
949     case AV_PIX_FMT_YUVA420P10:
950     case AV_PIX_FMT_YUV420P12:
951     case AV_PIX_FMT_YUV420P14:
952     case AV_PIX_FMT_YUV420P16:
953     case AV_PIX_FMT_YUVA420P16:
954         switch (s->index) {
955         case 0:
956         case 3:
957             s->filter[0] = s->filter[3] = pseudocolor_filter_16;
958             s->filter[1] = s->filter[2] = pseudocolor_filter_16_11;
959             break;
960         case 1:
961         case 2:
962             s->filter[0] = s->filter[3] = pseudocolor_filter_16_11d;
963             s->filter[1] = s->filter[2] = pseudocolor_filter_16;
964             break;
965         }
966         break;
967     }
968
969     return 0;
970 }
971
972 typedef struct ThreadData {
973     AVFrame *in, *out;
974 } ThreadData;
975
976 static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
977 {
978     PseudoColorContext *s = ctx->priv;
979     ThreadData *td = arg;
980     AVFrame *in = td->in;
981     AVFrame *out = td->out;
982
983     for (int plane = 0; plane < s->nb_planes; plane++) {
984         const int slice_start = (s->height[plane] * jobnr) / nb_jobs;
985         const int slice_end = (s->height[plane] * (jobnr+1)) / nb_jobs;
986         const int islice_start = (s->height[s->index] * jobnr) / nb_jobs;
987         ptrdiff_t ilinesize = in->linesize[s->index];
988         ptrdiff_t slinesize = in->linesize[plane];
989         ptrdiff_t dlinesize = out->linesize[plane];
990         const uint8_t *index = in->data[s->index] + islice_start * ilinesize;
991         const uint8_t *src = in->data[plane] + slice_start * slinesize;
992         uint8_t *dst = out->data[plane] + slice_start * dlinesize;
993
994         s->filter[plane](s->max, s->width[plane], slice_end - slice_start,
995                          index, src, dst, ilinesize, slinesize,
996                          dlinesize, s->lut[plane], s->opacity);
997     }
998
999     return 0;
1000 }
1001
1002 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
1003 {
1004     AVFilterContext *ctx = inlink->dst;
1005     PseudoColorContext *s = ctx->priv;
1006     AVFilterLink *outlink = ctx->outputs[0];
1007     ThreadData td;
1008     AVFrame *out;
1009
1010     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
1011     if (!out) {
1012         av_frame_free(&in);
1013         return AVERROR(ENOMEM);
1014     }
1015     av_frame_copy_props(out, in);
1016
1017     td.out = out, td.in = in;
1018     ff_filter_execute(ctx, filter_slice, &td, NULL,
1019                       FFMIN(s->height[1], ff_filter_get_nb_threads(ctx)));
1020
1021     av_frame_free(&in);
1022     return ff_filter_frame(outlink, out);
1023 }
1024
1025 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
1026                            char *res, int res_len, int flags)
1027 {
1028     int ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
1029
1030     if (ret < 0)
1031         return ret;
1032
1033     return config_input(ctx->inputs[0]);
1034 }
1035
1036 static const AVFilterPad inputs[] = {
1037     {
1038         .name         = "default",
1039         .type         = AVMEDIA_TYPE_VIDEO,
1040         .filter_frame = filter_frame,
1041         .config_props = config_input,
1042     },
1043 };
1044
1045 static av_cold void uninit(AVFilterContext *ctx)
1046 {
1047     PseudoColorContext *s = ctx->priv;
1048     int i;
1049
1050     for (i = 0; i < 4; i++) {
1051         av_expr_free(s->comp_expr[i]);
1052         s->comp_expr[i] = NULL;
1053     }
1054 }
1055
1056 AVFILTER_DEFINE_CLASS(pseudocolor);
1057
1058 const AVFilter ff_vf_pseudocolor = {
1059     .name          = "pseudocolor",
1060     .description   = NULL_IF_CONFIG_SMALL("Make pseudocolored video frames."),
1061     .priv_size     = sizeof(PseudoColorContext),
1062     .priv_class    = &pseudocolor_class,
1063     .uninit        = uninit,
1064     FILTER_INPUTS(inputs),
1065     FILTER_OUTPUTS(ff_video_default_filterpad),
1066     FILTER_PIXFMTS_ARRAY(pix_fmts),
1067     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
1068     .process_command = process_command,
1069 };