]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_colorchannelmixer.c
Merge commit '4a7af92cc80ced8498626401ed21f25ffe6740c8'
[ffmpeg] / libavfilter / vf_colorchannelmixer.c
1 /*
2  * Copyright (c) 2013 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/opt.h"
22 #include "avfilter.h"
23 #include "drawutils.h"
24 #include "formats.h"
25 #include "internal.h"
26 #include "video.h"
27
28 #define R 0
29 #define G 1
30 #define B 2
31 #define A 3
32
33 typedef struct {
34     const AVClass *class;
35     double rr, rg, rb, ra;
36     double gr, gg, gb, ga;
37     double br, bg, bb, ba;
38     double ar, ag, ab, aa;
39
40     int *lut[4][4];
41
42     int *buffer;
43
44     uint8_t rgba_map[4];
45 } ColorChannelMixerContext;
46
47 #define OFFSET(x) offsetof(ColorChannelMixerContext, x)
48 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
49 static const AVOption colorchannelmixer_options[] = {
50     { "rr", "set the red gain for the red channel",     OFFSET(rr), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
51     { "rg", "set the green gain for the red channel",   OFFSET(rg), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
52     { "rb", "set the blue gain for the red channel",    OFFSET(rb), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
53     { "ra", "set the alpha gain for the red channel",   OFFSET(ra), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
54     { "gr", "set the red gain for the green channel",   OFFSET(gr), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
55     { "gg", "set the green gain for the green channel", OFFSET(gg), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
56     { "gb", "set the blue gain for the green channel",  OFFSET(gb), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
57     { "ga", "set the alpha gain for the green channel", OFFSET(ga), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
58     { "br", "set the red gain for the blue channel",    OFFSET(br), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
59     { "bg", "set the green gain for the blue channel",  OFFSET(bg), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
60     { "bb", "set the blue gain for the blue channel",   OFFSET(bb), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
61     { "ba", "set the alpha gain for the blue channel",  OFFSET(ba), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
62     { "ar", "set the red gain for the alpha channel",   OFFSET(ar), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
63     { "ag", "set the green gain for the alpha channel", OFFSET(ag), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
64     { "ab", "set the blue gain for the alpha channel",  OFFSET(ab), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
65     { "aa", "set the alpha gain for the alpha channel", OFFSET(aa), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
66     { NULL }
67 };
68
69 AVFILTER_DEFINE_CLASS(colorchannelmixer);
70
71 static int query_formats(AVFilterContext *ctx)
72 {
73     static const enum AVPixelFormat pix_fmts[] = {
74         AV_PIX_FMT_RGB24,  AV_PIX_FMT_BGR24,
75         AV_PIX_FMT_RGBA,   AV_PIX_FMT_BGRA,
76         AV_PIX_FMT_ARGB,   AV_PIX_FMT_ABGR,
77         AV_PIX_FMT_0RGB,   AV_PIX_FMT_0BGR,
78         AV_PIX_FMT_RGB0,   AV_PIX_FMT_BGR0,
79         AV_PIX_FMT_RGB48,  AV_PIX_FMT_BGR48,
80         AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64,
81         AV_PIX_FMT_NONE
82     };
83
84     ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
85     return 0;
86 }
87
88 static int config_output(AVFilterLink *outlink)
89 {
90     AVFilterContext *ctx = outlink->src;
91     ColorChannelMixerContext *cm = ctx->priv;
92     int i, j, size, *buffer;
93
94     switch (outlink->format) {
95     case AV_PIX_FMT_RGB48:
96     case AV_PIX_FMT_BGR48:
97     case AV_PIX_FMT_RGBA64:
98     case AV_PIX_FMT_BGRA64:
99         if (outlink->format == AV_PIX_FMT_RGB48 ||
100             outlink->format == AV_PIX_FMT_RGBA64) {
101             cm->rgba_map[R] = 0;
102             cm->rgba_map[G] = 1;
103             cm->rgba_map[B] = 2;
104             cm->rgba_map[A] = 3;
105         } else {
106             cm->rgba_map[R] = 2;
107             cm->rgba_map[G] = 1;
108             cm->rgba_map[B] = 0;
109             cm->rgba_map[A] = 3;
110         }
111         size = 65536;
112         break;
113     default:
114         ff_fill_rgba_map(cm->rgba_map, outlink->format);
115         size = 256;
116     }
117
118     cm->buffer = buffer = av_malloc(16 * size * sizeof(*cm->buffer));
119     if (!cm->buffer)
120         return AVERROR(ENOMEM);
121
122     for (i = 0; i < 4; i++)
123         for (j = 0; j < 4; j++, buffer += size)
124             cm->lut[i][j] = buffer;
125
126     for (i = 0; i < size; i++) {
127         cm->lut[R][R][i] = i * cm->rr;
128         cm->lut[R][G][i] = i * cm->rg;
129         cm->lut[R][B][i] = i * cm->rb;
130         cm->lut[R][A][i] = i * cm->ra;
131
132         cm->lut[G][R][i] = i * cm->gr;
133         cm->lut[G][G][i] = i * cm->gg;
134         cm->lut[G][B][i] = i * cm->gb;
135         cm->lut[G][A][i] = i * cm->ga;
136
137         cm->lut[B][R][i] = i * cm->br;
138         cm->lut[B][G][i] = i * cm->bg;
139         cm->lut[B][B][i] = i * cm->bb;
140         cm->lut[B][A][i] = i * cm->ba;
141
142         cm->lut[A][R][i] = i * cm->ar;
143         cm->lut[A][G][i] = i * cm->ag;
144         cm->lut[A][B][i] = i * cm->ab;
145         cm->lut[A][A][i] = i * cm->aa;
146     }
147
148     return 0;
149 }
150
151 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
152 {
153     AVFilterContext *ctx = inlink->dst;
154     ColorChannelMixerContext *cm = ctx->priv;
155     AVFilterLink *outlink = ctx->outputs[0];
156     const uint8_t roffset = cm->rgba_map[R];
157     const uint8_t goffset = cm->rgba_map[G];
158     const uint8_t boffset = cm->rgba_map[B];
159     const uint8_t aoffset = cm->rgba_map[A];
160     const uint8_t *srcrow = in->data[0];
161     uint8_t *dstrow;
162     AVFrame *out;
163     int i, j;
164
165     if (av_frame_is_writable(in)) {
166         out = in;
167     } else {
168         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
169         if (!out) {
170             av_frame_free(&in);
171             return AVERROR(ENOMEM);
172         }
173         av_frame_copy_props(out, in);
174     }
175
176     dstrow = out->data[0];
177     switch (outlink->format) {
178     case AV_PIX_FMT_BGR24:
179     case AV_PIX_FMT_RGB24:
180         for (i = 0; i < outlink->h; i++) {
181             const uint8_t *src = srcrow;
182             uint8_t *dst = dstrow;
183
184             for (j = 0; j < outlink->w * 3; j += 3) {
185                 const uint8_t rin = src[j + roffset];
186                 const uint8_t gin = src[j + goffset];
187                 const uint8_t bin = src[j + boffset];
188
189                 dst[j + roffset] = av_clip_uint8(cm->lut[R][R][rin] +
190                                                  cm->lut[R][G][gin] +
191                                                  cm->lut[R][B][bin]);
192                 dst[j + goffset] = av_clip_uint8(cm->lut[G][R][rin] +
193                                                  cm->lut[G][G][gin] +
194                                                  cm->lut[G][B][bin]);
195                 dst[j + boffset] = av_clip_uint8(cm->lut[B][R][rin] +
196                                                  cm->lut[B][G][gin] +
197                                                  cm->lut[B][B][bin]);
198             }
199
200             srcrow += in->linesize[0];
201             dstrow += out->linesize[0];
202         }
203         break;
204     case AV_PIX_FMT_0BGR:
205     case AV_PIX_FMT_0RGB:
206     case AV_PIX_FMT_BGR0:
207     case AV_PIX_FMT_RGB0:
208         for (i = 0; i < outlink->h; i++) {
209             const uint8_t *src = srcrow;
210             uint8_t *dst = dstrow;
211
212             for (j = 0; j < outlink->w * 4; j += 4) {
213                 const uint8_t rin = src[j + roffset];
214                 const uint8_t gin = src[j + goffset];
215                 const uint8_t bin = src[j + boffset];
216
217                 dst[j + roffset] = av_clip_uint8(cm->lut[R][R][rin] +
218                                                  cm->lut[R][G][gin] +
219                                                  cm->lut[R][B][bin]);
220                 dst[j + goffset] = av_clip_uint8(cm->lut[G][R][rin] +
221                                                  cm->lut[G][G][gin] +
222                                                  cm->lut[G][B][bin]);
223                 dst[j + boffset] = av_clip_uint8(cm->lut[B][R][rin] +
224                                                  cm->lut[B][G][gin] +
225                                                  cm->lut[B][B][bin]);
226                 if (in != out)
227                     dst[j + aoffset] = 0;
228             }
229
230             srcrow += in->linesize[0];
231             dstrow += out->linesize[0];
232         }
233         break;
234     case AV_PIX_FMT_ABGR:
235     case AV_PIX_FMT_ARGB:
236     case AV_PIX_FMT_BGRA:
237     case AV_PIX_FMT_RGBA:
238         for (i = 0; i < outlink->h; i++) {
239             const uint8_t *src = srcrow;
240             uint8_t *dst = dstrow;
241
242             for (j = 0; j < outlink->w * 4; j += 4) {
243                 const uint8_t rin = src[j + roffset];
244                 const uint8_t gin = src[j + goffset];
245                 const uint8_t bin = src[j + boffset];
246                 const uint8_t ain = src[j + aoffset];
247
248                 dst[j + roffset] = av_clip_uint8(cm->lut[R][R][rin] +
249                                                  cm->lut[R][G][gin] +
250                                                  cm->lut[R][B][bin] +
251                                                  cm->lut[R][A][ain]);
252                 dst[j + goffset] = av_clip_uint8(cm->lut[G][R][rin] +
253                                                  cm->lut[G][G][gin] +
254                                                  cm->lut[G][B][bin] +
255                                                  cm->lut[G][A][ain]);
256                 dst[j + boffset] = av_clip_uint8(cm->lut[B][R][rin] +
257                                                  cm->lut[B][G][gin] +
258                                                  cm->lut[B][B][bin] +
259                                                  cm->lut[B][A][ain]);
260                 dst[j + aoffset] = av_clip_uint8(cm->lut[A][R][rin] +
261                                                  cm->lut[A][G][gin] +
262                                                  cm->lut[A][B][bin] +
263                                                  cm->lut[A][A][ain]);
264             }
265
266             srcrow += in->linesize[0];
267             dstrow += out->linesize[0];
268         }
269         break;
270     case AV_PIX_FMT_BGR48:
271     case AV_PIX_FMT_RGB48:
272         for (i = 0; i < outlink->h; i++) {
273             const uint16_t *src = (const uint16_t *)srcrow;
274             uint16_t *dst = (uint16_t *)dstrow;
275
276             for (j = 0; j < outlink->w * 3; j += 3) {
277                 const uint16_t rin = src[j + roffset];
278                 const uint16_t gin = src[j + goffset];
279                 const uint16_t bin = src[j + boffset];
280
281                 dst[j + roffset] = av_clip_uint16(cm->lut[R][R][rin] +
282                                                   cm->lut[R][G][gin] +
283                                                   cm->lut[R][B][bin]);
284                 dst[j + goffset] = av_clip_uint16(cm->lut[G][R][rin] +
285                                                   cm->lut[G][G][gin] +
286                                                   cm->lut[G][B][bin]);
287                 dst[j + boffset] = av_clip_uint16(cm->lut[B][R][rin] +
288                                                   cm->lut[B][G][gin] +
289                                                   cm->lut[B][B][bin]);
290             }
291
292             srcrow += in->linesize[0];
293             dstrow += out->linesize[0];
294         }
295         break;
296     case AV_PIX_FMT_BGRA64:
297     case AV_PIX_FMT_RGBA64:
298         for (i = 0; i < outlink->h; i++) {
299             const uint16_t *src = (const uint16_t *)srcrow;
300             uint16_t *dst = (uint16_t *)dstrow;
301
302             for (j = 0; j < outlink->w * 4; j += 4) {
303                 const uint16_t rin = src[j + roffset];
304                 const uint16_t gin = src[j + goffset];
305                 const uint16_t bin = src[j + boffset];
306                 const uint16_t ain = src[j + aoffset];
307
308                 dst[j + roffset] = av_clip_uint16(cm->lut[R][R][rin] +
309                                                   cm->lut[R][G][gin] +
310                                                   cm->lut[R][B][bin] +
311                                                   cm->lut[R][A][ain]);
312                 dst[j + goffset] = av_clip_uint16(cm->lut[G][R][rin] +
313                                                   cm->lut[G][G][gin] +
314                                                   cm->lut[G][B][bin] +
315                                                   cm->lut[G][A][ain]);
316                 dst[j + boffset] = av_clip_uint16(cm->lut[B][R][rin] +
317                                                   cm->lut[B][G][gin] +
318                                                   cm->lut[B][B][bin] +
319                                                   cm->lut[B][A][ain]);
320                 dst[j + aoffset] = av_clip_uint16(cm->lut[A][R][rin] +
321                                                   cm->lut[A][G][gin] +
322                                                   cm->lut[A][B][bin] +
323                                                   cm->lut[A][A][ain]);
324             }
325
326             srcrow += in->linesize[0];
327             dstrow += out->linesize[0];
328         }
329     }
330
331     if (in != out)
332         av_frame_free(&in);
333     return ff_filter_frame(ctx->outputs[0], out);
334 }
335
336 static av_cold void uninit(AVFilterContext *ctx)
337 {
338     ColorChannelMixerContext *cm = ctx->priv;
339
340     av_freep(&cm->buffer);
341 }
342
343 static const AVFilterPad colorchannelmixer_inputs[] = {
344     {
345         .name           = "default",
346         .type           = AVMEDIA_TYPE_VIDEO,
347         .filter_frame   = filter_frame,
348     },
349     { NULL }
350 };
351
352 static const AVFilterPad colorchannelmixer_outputs[] = {
353      {
354          .name         = "default",
355          .type         = AVMEDIA_TYPE_VIDEO,
356          .config_props = config_output,
357      },
358      { NULL }
359 };
360
361 AVFilter avfilter_vf_colorchannelmixer = {
362     .name          = "colorchannelmixer",
363     .description   = NULL_IF_CONFIG_SMALL("Adjust colors by mixing color channels."),
364     .priv_size     = sizeof(ColorChannelMixerContext),
365     .priv_class    = &colorchannelmixer_class,
366     .uninit        = uninit,
367     .query_formats = query_formats,
368     .inputs        = colorchannelmixer_inputs,
369     .outputs       = colorchannelmixer_outputs,
370     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE,
371 };