avfilter/vf_vectorscope: add graticule
[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/intreadwrite.h"
23 #include "libavutil/opt.h"
24 #include "libavutil/parseutils.h"
25 #include "libavutil/pixdesc.h"
26 #include "avfilter.h"
27 #include "formats.h"
28 #include "internal.h"
29 #include "video.h"
30
31 enum VectorscopeMode {
32     GRAY,
33     COLOR,
34     COLOR2,
35     COLOR3,
36     COLOR4,
37     COLOR5,
38     MODE_NB
39 };
40
41 typedef struct VectorscopeContext {
42     const AVClass *class;
43     int mode;
44     int intensity;
45     float fintensity;
46     const uint8_t *bg_color;
47     int planewidth[4];
48     int planeheight[4];
49     int hsub, vsub;
50     int x, y, pd;
51     int is_yuv;
52     int size;
53     int depth;
54     int mult;
55     int envelope;
56     int graticule;
57     float opacity;
58     int flags;
59     int cs;
60     uint8_t peak[1024][1024];
61
62     void (*vectorscope)(struct VectorscopeContext *s,
63                         AVFrame *in, AVFrame *out, int pd);
64     void (*graticulef)(struct VectorscopeContext *s, AVFrame *out,
65                        int X, int Y, int D, int P);
66 } VectorscopeContext;
67
68 #define OFFSET(x) offsetof(VectorscopeContext, x)
69 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
70
71 static const AVOption vectorscope_options[] = {
72     { "mode", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"},
73     { "m",    "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"},
74     {   "gray",   0, 0, AV_OPT_TYPE_CONST, {.i64=GRAY},   0, 0, FLAGS, "mode" },
75     {   "color",  0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR},  0, 0, FLAGS, "mode" },
76     {   "color2", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR2}, 0, 0, FLAGS, "mode" },
77     {   "color3", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR3}, 0, 0, FLAGS, "mode" },
78     {   "color4", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR4}, 0, 0, FLAGS, "mode" },
79     {   "color5", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR5}, 0, 0, FLAGS, "mode" },
80     { "x", "set color component on X axis", OFFSET(x), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS},
81     { "y", "set color component on Y axis", OFFSET(y), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS},
82     { "intensity", "set intensity", OFFSET(fintensity), AV_OPT_TYPE_FLOAT, {.dbl=0.004}, 0, 1, FLAGS},
83     { "i",         "set intensity", OFFSET(fintensity), AV_OPT_TYPE_FLOAT, {.dbl=0.004}, 0, 1, FLAGS},
84     { "envelope",  "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "envelope"},
85     { "e",         "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "envelope"},
86     {   "none",         0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "envelope" },
87     {   "instant",      0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "envelope" },
88     {   "peak",         0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "envelope" },
89     {   "peak+instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "envelope" },
90     { "graticule", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "graticule"},
91     { "g",         "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "graticule"},
92     {   "none",         0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "graticule" },
93     {   "green",        0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "graticule" },
94     {   "color",        0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "graticule" },
95     { "opacity", "set graticule opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS},
96     { "o",       "set graticule opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS},
97     { "flags", "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 3, FLAGS, "flags"},
98     { "f",     "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 3, FLAGS, "flags"},
99     {   "white", "draw white point", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "flags" },
100     {   "black", "draw black point", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "flags" },
101     { NULL }
102 };
103
104 AVFILTER_DEFINE_CLASS(vectorscope);
105
106 static const enum AVPixelFormat out_yuv8_pix_fmts[] = {
107     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P,
108     AV_PIX_FMT_NONE
109 };
110
111 static const enum AVPixelFormat out_yuv9_pix_fmts[] = {
112     AV_PIX_FMT_YUV444P9,
113     AV_PIX_FMT_NONE
114 };
115
116 static const enum AVPixelFormat out_yuv10_pix_fmts[] = {
117     AV_PIX_FMT_YUV444P10,
118     AV_PIX_FMT_NONE
119 };
120
121 static const enum AVPixelFormat out_rgb8_pix_fmts[] = {
122     AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
123     AV_PIX_FMT_NONE
124 };
125
126 static const enum AVPixelFormat out_rgb9_pix_fmts[] = {
127     AV_PIX_FMT_GBRP9,
128     AV_PIX_FMT_NONE
129 };
130
131 static const enum AVPixelFormat out_rgb10_pix_fmts[] = {
132     AV_PIX_FMT_GBRP10,
133     AV_PIX_FMT_NONE
134 };
135
136 static const enum AVPixelFormat in1_pix_fmts[] = {
137     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
138     AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10,
139     AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
140     AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
141     AV_PIX_FMT_NONE
142 };
143
144 static const enum AVPixelFormat in2_pix_fmts[] = {
145     AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P,
146     AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P,
147     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
148     AV_PIX_FMT_YUV411P,  AV_PIX_FMT_YUVJ411P,
149     AV_PIX_FMT_YUV440P,  AV_PIX_FMT_YUV410P,
150     AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
151     AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
152     AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
153     AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
154     AV_PIX_FMT_NONE
155 };
156
157 static int query_formats(AVFilterContext *ctx)
158 {
159     VectorscopeContext *s = ctx->priv;
160     const enum AVPixelFormat *out_pix_fmts;
161     const AVPixFmtDescriptor *desc;
162     AVFilterFormats *avff;
163     int depth, rgb, i, ret;
164
165     if (!ctx->inputs[0]->in_formats ||
166         !ctx->inputs[0]->in_formats->nb_formats) {
167         return AVERROR(EAGAIN);
168     }
169
170     if (!ctx->inputs[0]->out_formats) {
171         const enum AVPixelFormat *in_pix_fmts;
172
173         if ((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1))
174             in_pix_fmts = in2_pix_fmts;
175         else
176             in_pix_fmts = in1_pix_fmts;
177         if ((ret = ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->out_formats)) < 0)
178             return ret;
179     }
180
181     avff = ctx->inputs[0]->in_formats;
182     desc = av_pix_fmt_desc_get(avff->formats[0]);
183     rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
184     depth = desc->comp[0].depth;
185     for (i = 1; i < avff->nb_formats; i++) {
186         desc = av_pix_fmt_desc_get(avff->formats[i]);
187         if (rgb != (desc->flags & AV_PIX_FMT_FLAG_RGB) ||
188             depth != desc->comp[0].depth)
189             return AVERROR(EAGAIN);
190     }
191
192     if (rgb && depth == 8)
193         out_pix_fmts = out_rgb8_pix_fmts;
194     else if (rgb && depth == 9)
195         out_pix_fmts = out_rgb9_pix_fmts;
196     else if (rgb && depth == 10)
197         out_pix_fmts = out_rgb10_pix_fmts;
198     else if (depth == 9)
199         out_pix_fmts = out_yuv9_pix_fmts;
200     else if (depth == 10)
201         out_pix_fmts = out_yuv10_pix_fmts;
202     else if (depth == 8)
203         out_pix_fmts = out_yuv8_pix_fmts;
204     else
205         return AVERROR(EAGAIN);
206     if ((ret = ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->in_formats)) < 0)
207         return ret;
208
209     return 0;
210 }
211
212 static const uint8_t black_yuva_color[4] = { 0, 127, 127, 0 };
213 static const uint8_t black_gbrp_color[4] = { 0, 0, 0, 0 };
214
215 static int config_output(AVFilterLink *outlink)
216 {
217     VectorscopeContext *s = outlink->src->priv;
218
219     s->intensity = s->fintensity * (s->size - 1);
220     outlink->h = outlink->w = s->size;
221     outlink->sample_aspect_ratio = (AVRational){1,1};
222     return 0;
223 }
224
225 static void envelope_instant16(VectorscopeContext *s, AVFrame *out)
226 {
227     const int dlinesize = out->linesize[0] / 2;
228     uint16_t *dpd = s->mode == COLOR || !s->is_yuv ? (uint16_t *)out->data[s->pd] : (uint16_t *)out->data[0];
229     const int max = s->size - 1;
230     int i, j;
231
232     for (i = 0; i < out->height; i++) {
233         for (j = 0; j < out->width; j++) {
234             const int pos = i * dlinesize + j;
235             const int poa = (i - 1) * dlinesize + j;
236             const int pob = (i + 1) * dlinesize + j;
237
238             if (dpd[pos] && (((!j || !dpd[pos - 1]) || ((j == (out->width - 1)) || !dpd[pos + 1]))
239                          || ((!i || !dpd[poa]) || ((i == (out->height - 1)) || !dpd[pob])))) {
240                 dpd[pos] = max;
241             }
242         }
243     }
244 }
245
246 static void envelope_peak16(VectorscopeContext *s, AVFrame *out)
247 {
248     const int dlinesize = out->linesize[0] / 2;
249     uint16_t *dpd = s->mode == COLOR || !s->is_yuv ? (uint16_t *)out->data[s->pd] : (uint16_t *)out->data[0];
250     const int max = s->size - 1;
251     int i, j;
252
253     for (i = 0; i < out->height; i++) {
254         for (j = 0; j < out->width; j++) {
255             const int pos = i * dlinesize + j;
256
257             if (dpd[pos])
258                 s->peak[i][j] = 1;
259         }
260     }
261
262     if (s->envelope == 3)
263         envelope_instant16(s, out);
264
265     for (i = 0; i < out->height; i++) {
266         for (j = 0; j < out->width; j++) {
267             const int pos = i * dlinesize + j;
268
269             if (s->peak[i][j] && (((!j || !s->peak[i][j-1]) || ((j == (out->width - 1)) || !s->peak[i][j + 1]))
270                               || ((!i || !s->peak[i-1][j]) || ((i == (out->height - 1)) || !s->peak[i + 1][j])))) {
271                 dpd[pos] = max;
272             }
273         }
274     }
275 }
276
277 static void envelope_instant(VectorscopeContext *s, AVFrame *out)
278 {
279     const int dlinesize = out->linesize[0];
280     uint8_t *dpd = s->mode == COLOR || !s->is_yuv ? out->data[s->pd] : out->data[0];
281     int i, j;
282
283     for (i = 0; i < out->height; i++) {
284         for (j = 0; j < out->width; j++) {
285             const int pos = i * dlinesize + j;
286             const int poa = (i - 1) * dlinesize + j;
287             const int pob = (i + 1) * dlinesize + j;
288
289             if (dpd[pos] && (((!j || !dpd[pos - 1]) || ((j == (out->width - 1)) || !dpd[pos + 1]))
290                          || ((!i || !dpd[poa]) || ((i == (out->height - 1)) || !dpd[pob])))) {
291                 dpd[pos] = 255;
292             }
293         }
294     }
295 }
296
297 static void envelope_peak(VectorscopeContext *s, AVFrame *out)
298 {
299     const int dlinesize = out->linesize[0];
300     uint8_t *dpd = s->mode == COLOR || !s->is_yuv ? out->data[s->pd] : out->data[0];
301     int i, j;
302
303     for (i = 0; i < out->height; i++) {
304         for (j = 0; j < out->width; j++) {
305             const int pos = i * dlinesize + j;
306
307             if (dpd[pos])
308                 s->peak[i][j] = 1;
309         }
310     }
311
312     if (s->envelope == 3)
313         envelope_instant(s, out);
314
315     for (i = 0; i < out->height; i++) {
316         for (j = 0; j < out->width; j++) {
317             const int pos = i * dlinesize + j;
318
319             if (s->peak[i][j] && (((!j || !s->peak[i][j-1]) || ((j == (out->width - 1)) || !s->peak[i][j + 1]))
320                               || ((!i || !s->peak[i-1][j]) || ((i == (out->height - 1)) || !s->peak[i + 1][j])))) {
321                 dpd[pos] = 255;
322             }
323         }
324     }
325 }
326
327 static void envelope16(VectorscopeContext *s, AVFrame *out)
328 {
329     if (!s->envelope) {
330         return;
331     } else if (s->envelope == 1) {
332         envelope_instant16(s, out);
333     } else {
334         envelope_peak16(s, out);
335     }
336 }
337
338 static void envelope(VectorscopeContext *s, AVFrame *out)
339 {
340     if (!s->envelope) {
341         return;
342     } else if (s->envelope == 1) {
343         envelope_instant(s, out);
344     } else {
345         envelope_peak(s, out);
346     }
347 }
348
349 static void vectorscope16(VectorscopeContext *s, AVFrame *in, AVFrame *out, int pd)
350 {
351     const uint16_t * const *src = (const uint16_t * const *)in->data;
352     const int slinesizex = in->linesize[s->x] / 2;
353     const int slinesizey = in->linesize[s->y] / 2;
354     const int slinesized = in->linesize[pd] / 2;
355     const int dlinesize = out->linesize[0] / 2;
356     const int intensity = s->intensity;
357     const int px = s->x, py = s->y;
358     const int h = s->planeheight[py];
359     const int w = s->planewidth[px];
360     const uint16_t *spx = src[px];
361     const uint16_t *spy = src[py];
362     const uint16_t *spd = src[pd];
363     const int hsub = s->hsub;
364     const int vsub = s->vsub;
365     uint16_t **dst = (uint16_t **)out->data;
366     uint16_t *dpx = dst[px];
367     uint16_t *dpy = dst[py];
368     uint16_t *dpd = dst[pd];
369     const int max = s->size - 1;
370     const int mid = s->size / 2;
371     int i, j, k;
372
373     for (k = 0; k < 4 && dst[k]; k++) {
374         const int mult = s->mult;
375
376         for (i = 0; i < out->height ; i++)
377             for (j = 0; j < out->width; j++)
378                 AV_WN16(out->data[k] + i * out->linesize[k] + j * 2,
379                         (s->mode == COLOR || s->mode == COLOR5) && k == s->pd ? 0 : s->bg_color[k] * mult);
380     }
381
382     switch (s->mode) {
383     case COLOR:
384     case COLOR5:
385     case GRAY:
386         if (s->is_yuv) {
387             for (i = 0; i < h; i++) {
388                 const int iwx = i * slinesizex;
389                 const int iwy = i * slinesizey;
390                 for (j = 0; j < w; j++) {
391                     const int x = FFMIN(spx[iwx + j], max);
392                     const int y = FFMIN(spy[iwy + j], max);
393                     const int pos = y * dlinesize + x;
394
395                     dpd[pos] = FFMIN(dpd[pos] + intensity, max);
396                     if (dst[3])
397                         dst[3][pos] = max;
398                 }
399             }
400         } else {
401             for (i = 0; i < h; i++) {
402                 const int iwx = i * slinesizex;
403                 const int iwy = i * slinesizey;
404                 for (j = 0; j < w; j++) {
405                     const int x = FFMIN(spx[iwx + j], max);
406                     const int y = FFMIN(spy[iwy + j], max);
407                     const int pos = y * dlinesize + x;
408
409                     dst[0][pos] = FFMIN(dst[0][pos] + intensity, max);
410                     dst[1][pos] = FFMIN(dst[1][pos] + intensity, max);
411                     dst[2][pos] = FFMIN(dst[2][pos] + intensity, max);
412                     if (dst[3])
413                         dst[3][pos] = max;
414                 }
415             }
416         }
417         break;
418     case COLOR2:
419         if (s->is_yuv) {
420             for (i = 0; i < h; i++) {
421                 const int iw1 = i * slinesizex;
422                 const int iw2 = i * slinesizey;
423                 for (j = 0; j < w; j++) {
424                     const int x = FFMIN(spx[iw1 + j], max);
425                     const int y = FFMIN(spy[iw2 + j], max);
426                     const int pos = y * dlinesize + x;
427
428                     if (!dpd[pos])
429                         dpd[pos] = FFABS(mid - x) + FFABS(mid - y);
430                     dpx[pos] = x;
431                     dpy[pos] = y;
432                     if (dst[3])
433                         dst[3][pos] = max;
434                 }
435             }
436         } else {
437             for (i = 0; i < h; i++) {
438                 const int iw1 = i * slinesizex;
439                 const int iw2 = i * slinesizey;
440                 for (j = 0; j < w; j++) {
441                     const int x = FFMIN(spx[iw1 + j], max);
442                     const int y = FFMIN(spy[iw2 + j], max);
443                     const int pos = y * dlinesize + x;
444
445                     if (!dpd[pos])
446                         dpd[pos] = FFMIN(x + y, max);
447                     dpx[pos] = x;
448                     dpy[pos] = y;
449                     if (dst[3])
450                         dst[3][pos] = max;
451                 }
452             }
453         }
454         break;
455     case COLOR3:
456         for (i = 0; i < h; i++) {
457             const int iw1 = i * slinesizex;
458             const int iw2 = i * slinesizey;
459             for (j = 0; j < w; j++) {
460                 const int x = FFMIN(spx[iw1 + j], max);
461                 const int y = FFMIN(spy[iw2 + j], max);
462                 const int pos = y * dlinesize + x;
463
464                 dpd[pos] = FFMIN(max, dpd[pos] + intensity);
465                 dpx[pos] = x;
466                 dpy[pos] = y;
467                 if (dst[3])
468                     dst[3][pos] = max;
469             }
470         }
471         break;
472     case COLOR4:
473         for (i = 0; i < in->height; i++) {
474             const int iwx = (i >> vsub) * slinesizex;
475             const int iwy = (i >> vsub) * slinesizey;
476             const int iwd = i * slinesized;
477             for (j = 0; j < in->width; j++) {
478                 const int x = FFMIN(spx[iwx + (j >> hsub)], max);
479                 const int y = FFMIN(spy[iwy + (j >> hsub)], max);
480                 const int pos = y * dlinesize + x;
481
482                 dpd[pos] = FFMAX(spd[iwd + j], dpd[pos]);
483                 dpx[pos] = x;
484                 dpy[pos] = y;
485                 if (dst[3])
486                     dst[3][pos] = max;
487             }
488         }
489         break;
490     default:
491         av_assert0(0);
492     }
493
494     envelope16(s, out);
495
496     if (s->mode == COLOR) {
497         for (i = 0; i < out->height; i++) {
498             for (j = 0; j < out->width; j++) {
499                 if (!dpd[i * dlinesize + j]) {
500                     dpx[i * dlinesize + j] = j;
501                     dpy[i * dlinesize + j] = i;
502                     dpd[i * dlinesize + j] = mid;
503                 }
504             }
505         }
506     } else if (s->mode == COLOR5) {
507         for (i = 0; i < out->height; i++) {
508             for (j = 0; j < out->width; j++) {
509                 if (!dpd[i * dlinesize + j]) {
510                     dpx[i * dlinesize + j] = j;
511                     dpy[i * dlinesize + j] = i;
512                     dpd[i * dlinesize + j] = mid * M_SQRT2 - hypot(i - mid, j - mid);
513                 }
514             }
515         }
516     }
517 }
518
519 static void vectorscope8(VectorscopeContext *s, AVFrame *in, AVFrame *out, int pd)
520 {
521     const uint8_t * const *src = (const uint8_t * const *)in->data;
522     const int slinesizex = in->linesize[s->x];
523     const int slinesizey = in->linesize[s->y];
524     const int slinesized = in->linesize[pd];
525     const int dlinesize = out->linesize[0];
526     const int intensity = s->intensity;
527     const int px = s->x, py = s->y;
528     const int h = s->planeheight[py];
529     const int w = s->planewidth[px];
530     const uint8_t *spx = src[px];
531     const uint8_t *spy = src[py];
532     const uint8_t *spd = src[pd];
533     const int hsub = s->hsub;
534     const int vsub = s->vsub;
535     uint8_t **dst = out->data;
536     uint8_t *dpx = dst[px];
537     uint8_t *dpy = dst[py];
538     uint8_t *dpd = dst[pd];
539     int i, j, k;
540
541     for (k = 0; k < 4 && dst[k]; k++)
542         for (i = 0; i < out->height ; i++)
543             memset(dst[k] + i * out->linesize[k],
544                    (s->mode == COLOR || s->mode == COLOR5) && k == s->pd ? 0 : s->bg_color[k], out->width);
545
546     switch (s->mode) {
547     case COLOR5:
548     case COLOR:
549     case GRAY:
550         if (s->is_yuv) {
551             for (i = 0; i < h; i++) {
552                 const int iwx = i * slinesizex;
553                 const int iwy = i * slinesizey;
554                 for (j = 0; j < w; j++) {
555                     const int x = spx[iwx + j];
556                     const int y = spy[iwy + j];
557                     const int pos = y * dlinesize + x;
558
559                     dpd[pos] = FFMIN(dpd[pos] + intensity, 255);
560                     if (dst[3])
561                         dst[3][pos] = 255;
562                 }
563             }
564         } else {
565             for (i = 0; i < h; i++) {
566                 const int iwx = i * slinesizex;
567                 const int iwy = i * slinesizey;
568                 for (j = 0; j < w; j++) {
569                     const int x = spx[iwx + j];
570                     const int y = spy[iwy + j];
571                     const int pos = y * dlinesize + x;
572
573                     dst[0][pos] = FFMIN(dst[0][pos] + intensity, 255);
574                     dst[1][pos] = FFMIN(dst[1][pos] + intensity, 255);
575                     dst[2][pos] = FFMIN(dst[2][pos] + intensity, 255);
576                     if (dst[3])
577                         dst[3][pos] = 255;
578                 }
579             }
580         }
581         break;
582     case COLOR2:
583         if (s->is_yuv) {
584             for (i = 0; i < h; i++) {
585                 const int iw1 = i * slinesizex;
586                 const int iw2 = i * slinesizey;
587                 for (j = 0; j < w; j++) {
588                     const int x = spx[iw1 + j];
589                     const int y = spy[iw2 + j];
590                     const int pos = y * dlinesize + x;
591
592                     if (!dpd[pos])
593                         dpd[pos] = FFABS(128 - x) + FFABS(128 - y);
594                     dpx[pos] = x;
595                     dpy[pos] = y;
596                     if (dst[3])
597                         dst[3][pos] = 255;
598                 }
599             }
600         } else {
601             for (i = 0; i < h; i++) {
602                 const int iw1 = i * slinesizex;
603                 const int iw2 = i * slinesizey;
604                 for (j = 0; j < w; j++) {
605                     const int x = spx[iw1 + j];
606                     const int y = spy[iw2 + j];
607                     const int pos = y * dlinesize + x;
608
609                     if (!dpd[pos])
610                         dpd[pos] = FFMIN(x + y, 255);
611                     dpx[pos] = x;
612                     dpy[pos] = y;
613                     if (dst[3])
614                         dst[3][pos] = 255;
615                 }
616             }
617         }
618         break;
619     case COLOR3:
620         for (i = 0; i < h; i++) {
621             const int iw1 = i * slinesizex;
622             const int iw2 = i * slinesizey;
623             for (j = 0; j < w; j++) {
624                 const int x = spx[iw1 + j];
625                 const int y = spy[iw2 + j];
626                 const int pos = y * dlinesize + x;
627
628                 dpd[pos] = FFMIN(255, dpd[pos] + intensity);
629                 dpx[pos] = x;
630                 dpy[pos] = y;
631                 if (dst[3])
632                     dst[3][pos] = 255;
633             }
634         }
635         break;
636     case COLOR4:
637         for (i = 0; i < in->height; i++) {
638             const int iwx = (i >> vsub) * slinesizex;
639             const int iwy = (i >> vsub) * slinesizey;
640             const int iwd = i * slinesized;
641             for (j = 0; j < in->width; j++) {
642                 const int x = spx[iwx + (j >> hsub)];
643                 const int y = spy[iwy + (j >> hsub)];
644                 const int pos = y * dlinesize + x;
645
646                 dpd[pos] = FFMAX(spd[iwd + j], dpd[pos]);
647                 dpx[pos] = x;
648                 dpy[pos] = y;
649                 if (dst[3])
650                     dst[3][pos] = 255;
651             }
652         }
653         break;
654     default:
655         av_assert0(0);
656     }
657
658     envelope(s, out);
659
660     if (s->mode == COLOR) {
661         for (i = 0; i < out->height; i++) {
662             for (j = 0; j < out->width; j++) {
663                 if (!dpd[i * out->linesize[pd] + j]) {
664                     dpx[i * out->linesize[px] + j] = j;
665                     dpy[i * out->linesize[py] + j] = i;
666                     dpd[i * out->linesize[pd] + j] = 128;
667                 }
668             }
669         }
670     } else if (s->mode == COLOR5) {
671         for (i = 0; i < out->height; i++) {
672             for (j = 0; j < out->width; j++) {
673                 if (!dpd[i * out->linesize[pd] + j]) {
674                     dpx[i * out->linesize[px] + j] = j;
675                     dpy[i * out->linesize[py] + j] = i;
676                     dpd[i * out->linesize[pd] + j] = 128 * M_SQRT2 - hypot(i - 128, j - 128);
677                 }
678             }
679         }
680     }
681 }
682
683 const static uint16_t positions[][14][3] = {
684   { { 210,  16, 146 }, { 170, 166,  16 }, { 145,  54,  34 },
685     { 106, 202, 222 }, {  81,  90, 240 }, {  41, 240, 110 },
686     { 162,  44, 142 }, { 131, 156,  44 }, { 112,  72,  58 },
687     {  84, 184, 198 }, {  65, 100, 212 }, {  35, 212, 114 },
688     { 235, 128, 128 }, { 16, 128, 128 } },
689   { {  63, 102, 240 }, {  32, 240, 118 }, { 188, 154,  16 },
690     { 219,  16, 138 }, { 173,  42,  26 }, {  78, 214, 230 },
691     {  28, 212, 120 }, {  51, 109, 212 }, {  63, 193, 204 },
692     { 133,  63,  52 }, { 145, 147,  44 }, { 168,  44, 136 },
693     { 235, 128, 128 }, { 16, 128, 128 } },
694   { { 210*2,  16*2, 146*2 }, { 170*2, 166*2,  16*2 }, { 145*2,  54*2,  34*2 },
695     { 106*2, 202*2, 222*2 }, {  81*2,  90*2, 240*2 }, {  41*2, 240*2, 110*2 },
696     { 162*2,  44*2, 142*2 }, { 131*2, 156*2,  44*2 }, { 112*2,  72*2,  58*2 },
697     {  84*2, 184*2, 198*2 }, {  65*2, 100*2, 212*2 }, {  35*2, 212*2, 114*2 },
698     { 470, 256, 256 }, { 32, 256, 256 } },
699   { {  63*2, 102*2, 240*2 }, {  32*2, 240*2, 118*2 }, { 188*2, 154*2,  16*2 },
700     { 219*2,  16*2, 138*2 }, { 173*2,  42*2,  26*2 }, {  78*2, 214*2, 230*2 },
701     {  28*2, 212*2, 120*2 }, {  51*2, 109*2, 212*2 }, {  63*2, 193*2, 204*2 },
702     { 133*2,  63*2,  52*2 }, { 145*2, 147*2,  44*2 }, { 168*2,  44*2, 136*2 },
703     { 470, 256, 256 }, { 32, 256, 256 } },
704   { { 210*4,  16*4, 146*4 }, { 170*4, 166*4,  16*4 }, { 145*4,  54*4,  34*4 },
705     { 106*4, 202*4, 222*4 }, {  81*4,  90*4, 240*4 }, {  41*4, 240*4, 110*4 },
706     { 162*4,  44*4, 142*4 }, { 131*4, 156*4,  44*4 }, { 112*4,  72*4,  58*4 },
707     {  84*4, 184*4, 198*4 }, {  65*4, 100*4, 212*4 }, {  35*4, 212*4, 114*4 },
708     { 940, 512, 512 }, { 64, 512, 512 } },
709   { {  63*4, 102*4, 240*4 }, {  32*4, 240*4, 118*4 }, { 188*4, 154*4,  16*4 },
710     { 219*4,  16*4, 138*4 }, { 173*4,  42*4,  26*4 }, {  78*4, 214*4, 230*4 },
711     {  28*4, 212*4, 120*4 }, {  51*4, 109*4, 212*4 }, {  63*4, 193*4, 204*4 },
712     { 133*4,  63*4,  52*4 }, { 145*4, 147*4,  44*4 }, { 168*4,  44*4, 136*4 },
713     { 940, 512, 512 }, { 64, 512, 512 } },
714 };
715
716 static void draw_dots(uint8_t *dst, int L, int v, float o)
717 {
718     const float f = 1. - o;
719     const float V = o * v;
720     int l = L * 2;
721
722     dst[ l - 3] = dst[ l - 3] * f + V;
723     dst[ l + 3] = dst[ l + 3] * f + V;
724     dst[-l - 3] = dst[-l - 3] * f + V;
725     dst[-l + 3] = dst[-l + 3] * f + V;
726
727     l += L;
728
729     dst[ l - 3] = dst[ l - 3] * f + V;
730     dst[ l + 3] = dst[ l + 3] * f + V;
731     dst[ l - 2] = dst[ l - 2] * f + V;
732     dst[ l + 2] = dst[ l + 2] * f + V;
733     dst[-l - 3] = dst[-l - 3] * f + V;
734     dst[-l + 3] = dst[-l + 3] * f + V;
735     dst[-l - 2] = dst[-l - 2] * f + V;
736     dst[-l + 2] = dst[-l + 2] * f + V;
737 }
738
739 static void draw_dots16(uint16_t *dst, int L, int v, float o)
740 {
741     const float f = 1. - o;
742     const float V = o * v;
743     int l = L * 2;
744
745     dst[ l - 3] = dst[ l - 3] * f + V;
746     dst[ l + 3] = dst[ l + 3] * f + V;
747     dst[-l - 3] = dst[-l - 3] * f + V;
748     dst[-l + 3] = dst[-l + 3] * f + V;
749
750     l += L;
751
752     dst[ l - 3] = dst[ l - 3] * f + V;
753     dst[ l + 3] = dst[ l + 3] * f + V;
754     dst[ l - 2] = dst[ l - 2] * f + V;
755     dst[ l + 2] = dst[ l + 2] * f + V;
756     dst[-l - 3] = dst[-l - 3] * f + V;
757     dst[-l + 3] = dst[-l + 3] * f + V;
758     dst[-l - 2] = dst[-l - 2] * f + V;
759     dst[-l + 2] = dst[-l + 2] * f + V;
760 }
761
762 static void none_graticule(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P)
763 {
764 }
765
766 static void color_graticule16(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P)
767 {
768     const float o = s->opacity;
769     int i;
770
771     for (i = 0; i < 12; i++) {
772         int x = positions[P][i][X];
773         int y = positions[P][i][Y];
774         int d = positions[P][i][D];
775
776         draw_dots16((uint16_t *)(out->data[D] + y * out->linesize[D] + x * 2), out->linesize[D] / 2, d, o);
777         draw_dots16((uint16_t *)(out->data[X] + y * out->linesize[X] + x * 2), out->linesize[X] / 2, x, o);
778         draw_dots16((uint16_t *)(out->data[Y] + y * out->linesize[Y] + x * 2), out->linesize[Y] / 2, y, o);
779     }
780
781     if (s->flags & 1) {
782         int x = positions[P][12][X];
783         int y = positions[P][12][Y];
784         int d = positions[P][12][D];
785
786         draw_dots16((uint16_t *)(out->data[D] + y * out->linesize[D] + x * 2), out->linesize[D] / 2, d, o);
787         draw_dots16((uint16_t *)(out->data[X] + y * out->linesize[X] + x * 2), out->linesize[X] / 2, x, o);
788         draw_dots16((uint16_t *)(out->data[Y] + y * out->linesize[Y] + x * 2), out->linesize[Y] / 2, y, o);
789     }
790
791     if (s->flags & 2) {
792         int x = positions[P][13][X];
793         int y = positions[P][13][Y];
794         int d = positions[P][13][D];
795
796         draw_dots16((uint16_t *)(out->data[D] + y * out->linesize[D] + x * 2), out->linesize[D] / 2, d, o);
797         draw_dots16((uint16_t *)(out->data[X] + y * out->linesize[X] + x * 2), out->linesize[X] / 2, x, o);
798         draw_dots16((uint16_t *)(out->data[Y] + y * out->linesize[Y] + x * 2), out->linesize[Y] / 2, y, o);
799     }
800 }
801
802 static void color_graticule(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P)
803 {
804     const float o = s->opacity;
805     int i;
806
807     for (i = 0; i < 12; i++) {
808         int x = positions[P][i][X];
809         int y = positions[P][i][Y];
810         int d = positions[P][i][D];
811
812         draw_dots(out->data[D] + y * out->linesize[D] + x, out->linesize[D], d, o);
813         draw_dots(out->data[X] + y * out->linesize[X] + x, out->linesize[X], x, o);
814         draw_dots(out->data[Y] + y * out->linesize[Y] + x, out->linesize[Y], y, o);
815     }
816
817     if (s->flags & 1) {
818         int x = positions[P][12][X];
819         int y = positions[P][12][Y];
820         int d = positions[P][12][D];
821
822         draw_dots(out->data[D] + y * out->linesize[D] + x, out->linesize[D], d, o);
823         draw_dots(out->data[X] + y * out->linesize[X] + x, out->linesize[X], x, o);
824         draw_dots(out->data[Y] + y * out->linesize[Y] + x, out->linesize[Y], y, o);
825     }
826
827     if (s->flags & 2) {
828         int x = positions[P][13][X];
829         int y = positions[P][13][Y];
830         int d = positions[P][12][D];
831
832         draw_dots(out->data[D] + y * out->linesize[D] + x, out->linesize[D], d, o);
833         draw_dots(out->data[X] + y * out->linesize[X] + x, out->linesize[X], x, o);
834         draw_dots(out->data[Y] + y * out->linesize[Y] + x, out->linesize[Y], y, o);
835     }
836 }
837
838 static void green_graticule16(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P)
839 {
840     const float o = s->opacity;
841     const int m = s->mult;
842     int i;
843
844     for (i = 0; i < 12; i++) {
845         int x = positions[P][i][X];
846         int y = positions[P][i][Y];
847
848         draw_dots16((uint16_t *)(out->data[D] + y * out->linesize[D] + x * 2), out->linesize[D] / 2, 128 * m, o);
849         draw_dots16((uint16_t *)(out->data[X] + y * out->linesize[X] + x * 2), out->linesize[X] / 2, 0, o);
850         draw_dots16((uint16_t *)(out->data[Y] + y * out->linesize[Y] + x * 2), out->linesize[Y] / 2, 0, o);
851     }
852
853     if (s->flags & 1) {
854         int x = positions[P][12][X];
855         int y = positions[P][12][Y];
856
857         draw_dots16((uint16_t *)(out->data[D] + y * out->linesize[D] + x * 2), out->linesize[D] / 2, 128 * m, o);
858         draw_dots16((uint16_t *)(out->data[X] + y * out->linesize[X] + x * 2), out->linesize[X] / 2, 0, o);
859         draw_dots16((uint16_t *)(out->data[Y] + y * out->linesize[Y] + x * 2), out->linesize[Y] / 2, 0, o);
860     }
861
862     if (s->flags & 2) {
863         int x = positions[P][13][X];
864         int y = positions[P][13][Y];
865
866         draw_dots16((uint16_t *)(out->data[D] + y * out->linesize[D] + x * 2), out->linesize[D] / 2, 128 * m, o);
867         draw_dots16((uint16_t *)(out->data[X] + y * out->linesize[X] + x * 2), out->linesize[X] / 2, 0, o);
868         draw_dots16((uint16_t *)(out->data[Y] + y * out->linesize[Y] + x * 2), out->linesize[Y] / 2, 0, o);
869     }
870 }
871
872 static void green_graticule(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P)
873 {
874     const float o = s->opacity;
875     int i;
876
877     for (i = 0; i < 12; i++) {
878         int x = positions[P][i][X];
879         int y = positions[P][i][Y];
880
881         draw_dots(out->data[D] + y * out->linesize[D] + x, out->linesize[D], 128, o);
882         draw_dots(out->data[X] + y * out->linesize[X] + x, out->linesize[X], 0, o);
883         draw_dots(out->data[Y] + y * out->linesize[Y] + x, out->linesize[Y], 0, o);
884     }
885
886     if (s->flags & 1) {
887         int x = positions[P][12][X];
888         int y = positions[P][12][Y];
889
890         draw_dots(out->data[D] + y * out->linesize[D] + x, out->linesize[D], 128, o);
891         draw_dots(out->data[X] + y * out->linesize[X] + x, out->linesize[X], 0, o);
892         draw_dots(out->data[Y] + y * out->linesize[Y] + x, out->linesize[Y], 0, o);
893     }
894
895     if (s->flags & 2) {
896         int x = positions[P][13][X];
897         int y = positions[P][13][Y];
898
899         draw_dots(out->data[D] + y * out->linesize[D] + x, out->linesize[D], 128, o);
900         draw_dots(out->data[X] + y * out->linesize[X] + x, out->linesize[X], 0, o);
901         draw_dots(out->data[Y] + y * out->linesize[Y] + x, out->linesize[Y], 0, o);
902     }
903 }
904
905 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
906 {
907     AVFilterContext *ctx  = inlink->dst;
908     VectorscopeContext *s = ctx->priv;
909     AVFilterLink *outlink = ctx->outputs[0];
910     AVFrame *out;
911
912     switch (av_frame_get_colorspace(in)) {
913     case AVCOL_SPC_SMPTE170M:
914     case AVCOL_SPC_BT470BG:
915         s->cs = (s->depth - 8) * 2 + 0;
916         break;
917     case AVCOL_SPC_BT709:
918     default:
919         s->cs = (s->depth - 8) * 2 + 1;
920     }
921
922     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
923     if (!out) {
924         av_frame_free(&in);
925         return AVERROR(ENOMEM);
926     }
927     out->pts = in->pts;
928
929     s->vectorscope(s, in, out, s->pd);
930     s->graticulef(s, out, s->x, s->y, s->pd, s->cs);
931
932     av_frame_free(&in);
933     return ff_filter_frame(outlink, out);
934 }
935
936 static int config_input(AVFilterLink *inlink)
937 {
938     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
939     VectorscopeContext *s = inlink->dst->priv;
940
941     s->is_yuv = !(desc->flags & AV_PIX_FMT_FLAG_RGB);
942     s->size = 1 << desc->comp[0].depth;
943     s->mult = s->size / 256;
944     s->depth = desc->comp[0].depth;
945
946     if (s->mode == GRAY && s->is_yuv)
947         s->pd = 0;
948     else {
949         if ((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1))
950             s->pd = 0;
951         else if ((s->x == 0 && s->y == 2) || (s->x == 2 && s->y == 0))
952             s->pd = 1;
953         else if ((s->x == 0 && s->y == 1) || (s->x == 1 && s->y == 0))
954             s->pd = 2;
955     }
956
957     if (s->size == 256)
958         s->vectorscope = vectorscope8;
959     else
960         s->vectorscope = vectorscope16;
961
962     s->graticulef = none_graticule;
963
964     if (s->is_yuv && s->size == 256) {
965         if (s->graticule == 1)
966             s->graticulef = green_graticule;
967         else if (s->graticule == 2)
968             s->graticulef = color_graticule;
969     } else if (s->is_yuv) {
970         if (s->graticule == 1)
971             s->graticulef = green_graticule16;
972         else if (s->graticule == 2)
973             s->graticulef = color_graticule16;
974     }
975
976     switch (inlink->format) {
977     case AV_PIX_FMT_GBRP10:
978     case AV_PIX_FMT_GBRP9:
979     case AV_PIX_FMT_GBRAP:
980     case AV_PIX_FMT_GBRP:
981         s->bg_color = black_gbrp_color;
982         break;
983     default:
984         s->bg_color = black_yuva_color;
985     }
986
987     s->hsub = desc->log2_chroma_w;
988     s->vsub = desc->log2_chroma_h;
989     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
990     s->planeheight[0] = s->planeheight[3] = inlink->h;
991     s->planewidth[1]  = s->planewidth[2]  = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
992     s->planewidth[0]  = s->planewidth[3]  = inlink->w;
993
994     return 0;
995 }
996
997 static const AVFilterPad inputs[] = {
998     {
999         .name         = "default",
1000         .type         = AVMEDIA_TYPE_VIDEO,
1001         .filter_frame = filter_frame,
1002         .config_props = config_input,
1003     },
1004     { NULL }
1005 };
1006
1007 static const AVFilterPad outputs[] = {
1008     {
1009         .name         = "default",
1010         .type         = AVMEDIA_TYPE_VIDEO,
1011         .config_props = config_output,
1012     },
1013     { NULL }
1014 };
1015
1016 AVFilter ff_vf_vectorscope = {
1017     .name          = "vectorscope",
1018     .description   = NULL_IF_CONFIG_SMALL("Video vectorscope."),
1019     .priv_size     = sizeof(VectorscopeContext),
1020     .priv_class    = &vectorscope_class,
1021     .query_formats = query_formats,
1022     .inputs        = inputs,
1023     .outputs       = outputs,
1024 };