avfilter/vf_vectorscope: make intensity user configurable
[ffmpeg.git] / libavfilter / vf_vectorscope.c
1 /*
2  * Copyright (c) 2015 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/avassert.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/parseutils.h"
24 #include "libavutil/pixdesc.h"
25 #include "avfilter.h"
26 #include "formats.h"
27 #include "internal.h"
28 #include "video.h"
29
30 enum VectorscopeMode {
31     GRAY,
32     COLOR,
33     COLOR2,
34     COLOR3,
35     MODE_NB
36 };
37
38 typedef struct VectorscopeContext {
39     const AVClass *class;
40     int mode;
41     int intensity;
42     const uint8_t *bg_color;
43     int planewidth[4];
44     int planeheight[4];
45     int x, y, pd;
46     int is_yuv;
47 } VectorscopeContext;
48
49 #define OFFSET(x) offsetof(VectorscopeContext, x)
50 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
51
52 static const AVOption vectorscope_options[] = {
53     { "mode", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"},
54     { "gray",   0, 0, AV_OPT_TYPE_CONST, {.i64=GRAY},   0, 0, FLAGS, "mode" },
55     { "color",  0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR},  0, 0, FLAGS, "mode" },
56     { "color2", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR2}, 0, 0, FLAGS, "mode" },
57     { "color3", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR3}, 0, 0, FLAGS, "mode" },
58     { "x", "set color component on X axis", OFFSET(x), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS},
59     { "y", "set color component on Y axis", OFFSET(y), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS},
60     { "intensity", "set intensity", OFFSET(intensity), AV_OPT_TYPE_INT, {.i64=1}, 1, 255, FLAGS},
61     { NULL }
62 };
63
64 AVFILTER_DEFINE_CLASS(vectorscope);
65
66 static const enum AVPixelFormat out_yuv_pix_fmts[] = {
67     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P,
68     AV_PIX_FMT_NONE
69 };
70
71 static const enum AVPixelFormat out_rgb_pix_fmts[] = {
72     AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
73     AV_PIX_FMT_NONE
74 };
75
76 static const enum AVPixelFormat in1_pix_fmts[] = {
77     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
78     AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
79     AV_PIX_FMT_NONE
80 };
81
82 static const enum AVPixelFormat in2_pix_fmts[] = {
83     AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P,
84     AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P,
85     AV_PIX_FMT_YUV411P,  AV_PIX_FMT_YUVJ411P,
86     AV_PIX_FMT_YUV440P,  AV_PIX_FMT_YUV410P,
87     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
88     AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
89     AV_PIX_FMT_NONE
90 };
91
92 static int query_formats(AVFilterContext *ctx)
93 {
94     VectorscopeContext *s = ctx->priv;
95     const enum AVPixelFormat *out_pix_fmts;
96     const AVPixFmtDescriptor *desc;
97     AVFilterFormats *avff;
98     int rgb, i;
99
100     if (!ctx->inputs[0]->in_formats ||
101         !ctx->inputs[0]->in_formats->nb_formats) {
102         return AVERROR(EAGAIN);
103     }
104
105     if (!ctx->inputs[0]->out_formats) {
106         const enum AVPixelFormat *in_pix_fmts;
107
108         if ((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1))
109             in_pix_fmts = in2_pix_fmts;
110         else
111             in_pix_fmts = in1_pix_fmts;
112         ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->out_formats);
113     }
114
115     avff = ctx->inputs[0]->in_formats;
116     desc = av_pix_fmt_desc_get(avff->formats[0]);
117     rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
118     for (i = 1; i < avff->nb_formats; i++) {
119         desc = av_pix_fmt_desc_get(avff->formats[i]);
120         if (rgb != desc->flags & AV_PIX_FMT_FLAG_RGB)
121             return AVERROR(EAGAIN);
122     }
123
124     if (rgb)
125         out_pix_fmts = out_rgb_pix_fmts;
126     else
127         out_pix_fmts = out_yuv_pix_fmts;
128     ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->in_formats);
129
130     return 0;
131 }
132
133 static const uint8_t black_yuva_color[4] = { 0, 127, 127, 0 };
134 static const uint8_t black_gbrp_color[4] = { 0, 0, 0, 0 };
135
136 static int config_input(AVFilterLink *inlink)
137 {
138     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
139     VectorscopeContext *s = inlink->dst->priv;
140
141     if (s->mode == GRAY)
142         s->pd = 0;
143     else {
144         if ((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1))
145             s->pd = 0;
146         else if ((s->x == 0 && s->y == 2) || (s->x == 2 && s->y == 0))
147             s->pd = 1;
148         else if ((s->x == 0 && s->y == 1) || (s->x == 1 && s->y == 0))
149             s->pd = 2;
150     }
151
152     switch (inlink->format) {
153     case AV_PIX_FMT_GBRAP:
154     case AV_PIX_FMT_GBRP:
155         s->bg_color = black_gbrp_color;
156         break;
157     default:
158         s->bg_color = black_yuva_color;
159     }
160
161     s->planeheight[1] = s->planeheight[2] = FF_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
162     s->planeheight[0] = s->planeheight[3] = inlink->h;
163     s->planewidth[1]  = s->planewidth[2]  = FF_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
164     s->planewidth[0]  = s->planewidth[3]  = inlink->w;
165
166     return 0;
167 }
168
169 static int config_output(AVFilterLink *outlink)
170 {
171     VectorscopeContext *s = outlink->src->priv;
172     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
173     int depth = desc->comp[0].depth_minus1 + 1;
174
175     s->is_yuv = !(desc->flags & AV_PIX_FMT_FLAG_RGB);
176     outlink->h = outlink->w = 1 << depth;
177     outlink->sample_aspect_ratio = (AVRational){1,1};
178     return 0;
179 }
180
181 static void vectorscope(VectorscopeContext *s, AVFrame *in, AVFrame *out, int pd)
182 {
183     const uint8_t * const *src = (const uint8_t * const *)in->data;
184     const int slinesizex = in->linesize[s->x];
185     const int slinesizey = in->linesize[s->y];
186     const int dlinesize = out->linesize[0];
187     const int intensity = s->intensity;
188     int i, j, px = s->x, py = s->y;
189     const int h = s->planeheight[py];
190     const int w = s->planewidth[px];
191     const uint8_t *spx = src[px];
192     const uint8_t *spy = src[py];
193     uint8_t **dst = out->data;
194     uint8_t *dpx = dst[px];
195     uint8_t *dpy = dst[py];
196     uint8_t *dpd = dst[pd];
197
198     switch (s->mode) {
199     case COLOR:
200     case GRAY:
201         if (s->is_yuv) {
202             for (i = 0; i < h; i++) {
203                 const int iwx = i * slinesizex;
204                 const int iwy = i * slinesizey;
205                 for (j = 0; j < w; j++) {
206                     const int x = spx[iwx + j];
207                     const int y = spy[iwy + j];
208                     const int pos = y * dlinesize + x;
209
210                     dpd[pos] = FFMIN(dpd[pos] + intensity, 255);
211                     if (dst[3])
212                         dst[3][pos] = 255;
213                 }
214             }
215         } else {
216             for (i = 0; i < h; i++) {
217                 const int iwx = i * slinesizex;
218                 const int iwy = i * slinesizey;
219                 for (j = 0; j < w; j++) {
220                     const int x = spx[iwx + j];
221                     const int y = spy[iwy + j];
222                     const int pos = y * dlinesize + x;
223
224                     dst[0][pos] = FFMIN(dst[0][pos] + intensity, 255);
225                     dst[1][pos] = FFMIN(dst[1][pos] + intensity, 255);
226                     dst[2][pos] = FFMIN(dst[2][pos] + intensity, 255);
227                     if (dst[3])
228                         dst[3][pos] = 255;
229                 }
230             }
231         }
232         if (s->mode == COLOR) {
233             for (i = 0; i < out->height; i++) {
234                 for (j = 0; j < out->width; j++) {
235                     if (!dpd[i * out->linesize[pd] + j]) {
236                         dpx[i * out->linesize[px] + j] = j;
237                         dpy[i * out->linesize[py] + j] = i;
238                     }
239                 }
240             }
241         }
242         break;
243     case COLOR2:
244         if (s->is_yuv) {
245             for (i = 0; i < h; i++) {
246                 const int iw1 = i * slinesizex;
247                 const int iw2 = i * slinesizey;
248                 for (j = 0; j < w; j++) {
249                     const int x = spx[iw1 + j];
250                     const int y = spy[iw2 + j];
251                     const int pos = y * dlinesize + x;
252
253                     if (!dpd[pos])
254                         dpd[pos] = FFABS(128 - x) + FFABS(128 - y);
255                     dpx[pos] = x;
256                     dpy[pos] = y;
257                     if (dst[3])
258                         dst[3][pos] = 255;
259                 }
260             }
261         } else {
262             for (i = 0; i < h; i++) {
263                 const int iw1 = i * slinesizex;
264                 const int iw2 = i * slinesizey;
265                 for (j = 0; j < w; j++) {
266                     const int x = spx[iw1 + j];
267                     const int y = spy[iw2 + j];
268                     const int pos = y * dlinesize + x;
269
270                     if (!dpd[pos])
271                         dpd[pos] = FFMIN(x + y, 255);
272                     dpx[pos] = x;
273                     dpy[pos] = y;
274                     if (dst[3])
275                         dst[3][pos] = 255;
276                 }
277             }
278         }
279         break;
280     case COLOR3:
281         for (i = 0; i < h; i++) {
282             const int iw1 = i * slinesizex;
283             const int iw2 = i * slinesizey;
284             for (j = 0; j < w; j++) {
285                 const int x = spx[iw1 + j];
286                 const int y = spy[iw2 + j];
287                 const int pos = y * dlinesize + x;
288
289                 dpd[pos] = FFMIN(255, dpd[pos] + intensity);
290                 dpx[pos] = x;
291                 dpy[pos] = y;
292                 if (dst[3])
293                     dst[3][pos] = 255;
294             }
295         }
296         break;
297     default:
298         av_assert0(0);
299     }
300 }
301
302 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
303 {
304     AVFilterContext *ctx  = inlink->dst;
305     VectorscopeContext *s = ctx->priv;
306     AVFilterLink *outlink = ctx->outputs[0];
307     AVFrame *out;
308     uint8_t **dst;
309     int i, k;
310
311     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
312     if (!out) {
313         av_frame_free(&in);
314         return AVERROR(ENOMEM);
315     }
316     out->pts = in->pts;
317     dst = out->data;
318
319     for (k = 0; k < 4 && dst[k]; k++)
320         for (i = 0; i < outlink->h ; i++)
321             memset(dst[k] + i * out->linesize[k],
322                    s->mode == COLOR && k == s->pd ? 0 : s->bg_color[k], outlink->w);
323
324     vectorscope(s, in, out, s->pd);
325
326     av_frame_free(&in);
327     return ff_filter_frame(outlink, out);
328 }
329
330 static const AVFilterPad inputs[] = {
331     {
332         .name         = "default",
333         .type         = AVMEDIA_TYPE_VIDEO,
334         .filter_frame = filter_frame,
335         .config_props = config_input,
336     },
337     { NULL }
338 };
339
340 static const AVFilterPad outputs[] = {
341     {
342         .name         = "default",
343         .type         = AVMEDIA_TYPE_VIDEO,
344         .config_props = config_output,
345     },
346     { NULL }
347 };
348
349 AVFilter ff_vf_vectorscope = {
350     .name          = "vectorscope",
351     .description   = NULL_IF_CONFIG_SMALL("Video vectorscope."),
352     .priv_size     = sizeof(VectorscopeContext),
353     .priv_class    = &vectorscope_class,
354     .query_formats = query_formats,
355     .inputs        = inputs,
356     .outputs       = outputs,
357 };