]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_vectorscope.c
Merge commit 'ffb9b7a6bab6c6bfd3dd9a7c32e3724209824999'
[ffmpeg] / 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 "libavutil/xga_font_data.h"
27 #include "avfilter.h"
28 #include "formats.h"
29 #include "internal.h"
30 #include "video.h"
31
32 enum VectorscopeMode {
33     GRAY,
34     COLOR,
35     COLOR2,
36     COLOR3,
37     COLOR4,
38     COLOR5,
39     MODE_NB
40 };
41
42 typedef struct VectorscopeContext {
43     const AVClass *class;
44     int mode;
45     int intensity;
46     float fintensity;
47     uint16_t bg_color[4];
48     int planewidth[4];
49     int planeheight[4];
50     int hsub, vsub;
51     int x, y, pd;
52     int is_yuv;
53     int size;
54     int depth;
55     int mult;
56     int envelope;
57     int graticule;
58     float opacity;
59     float bgopacity;
60     float lthreshold;
61     float hthreshold;
62     int tmin;
63     int tmax;
64     int flags;
65     int colorspace;
66     int cs;
67     uint8_t *peak_memory;
68     uint8_t **peak;
69
70     void (*vectorscope)(struct VectorscopeContext *s,
71                         AVFrame *in, AVFrame *out, int pd);
72     void (*graticulef)(struct VectorscopeContext *s, AVFrame *out,
73                        int X, int Y, int D, int P);
74 } VectorscopeContext;
75
76 #define OFFSET(x) offsetof(VectorscopeContext, x)
77 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
78
79 static const AVOption vectorscope_options[] = {
80     { "mode", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"},
81     { "m",    "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"},
82     {   "gray",   0, 0, AV_OPT_TYPE_CONST, {.i64=GRAY},   0, 0, FLAGS, "mode" },
83     {   "color",  0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR},  0, 0, FLAGS, "mode" },
84     {   "color2", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR2}, 0, 0, FLAGS, "mode" },
85     {   "color3", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR3}, 0, 0, FLAGS, "mode" },
86     {   "color4", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR4}, 0, 0, FLAGS, "mode" },
87     {   "color5", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR5}, 0, 0, FLAGS, "mode" },
88     { "x", "set color component on X axis", OFFSET(x), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS},
89     { "y", "set color component on Y axis", OFFSET(y), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS},
90     { "intensity", "set intensity", OFFSET(fintensity), AV_OPT_TYPE_FLOAT, {.dbl=0.004}, 0, 1, FLAGS},
91     { "i",         "set intensity", OFFSET(fintensity), AV_OPT_TYPE_FLOAT, {.dbl=0.004}, 0, 1, FLAGS},
92     { "envelope",  "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "envelope"},
93     { "e",         "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "envelope"},
94     {   "none",         0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "envelope" },
95     {   "instant",      0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "envelope" },
96     {   "peak",         0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "envelope" },
97     {   "peak+instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "envelope" },
98     { "graticule", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "graticule"},
99     { "g",         "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "graticule"},
100     {   "none",         0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "graticule" },
101     {   "green",        0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "graticule" },
102     {   "color",        0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "graticule" },
103     { "opacity", "set graticule opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS},
104     { "o",       "set graticule opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS},
105     { "flags", "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=4}, 0, 7, FLAGS, "flags"},
106     { "f",     "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=4}, 0, 7, FLAGS, "flags"},
107     {   "white", "draw white point", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "flags" },
108     {   "black", "draw black point", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "flags" },
109     {   "name",  "draw point name",  0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, "flags" },
110     { "bgopacity", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.3}, 0, 1, FLAGS},
111     { "b",         "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.3}, 0, 1, FLAGS},
112     { "lthreshold", "set low threshold",  OFFSET(lthreshold), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, 1, FLAGS},
113     { "l",          "set low threshold",  OFFSET(lthreshold), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, 1, FLAGS},
114     { "hthreshold", "set high threshold", OFFSET(hthreshold), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, FLAGS},
115     { "h",          "set high threshold", OFFSET(hthreshold), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, FLAGS},
116     { "colorspace", "set colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "colorspace"},
117     { "c",          "set colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "colorspace"},
118     {   "auto",       0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "colorspace" },
119     {   "601",        0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "colorspace" },
120     {   "709",        0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "colorspace" },
121     { NULL }
122 };
123
124 AVFILTER_DEFINE_CLASS(vectorscope);
125
126 static const enum AVPixelFormat out_yuv8_pix_fmts[] = {
127     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P,
128     AV_PIX_FMT_NONE
129 };
130
131 static const enum AVPixelFormat out_yuv9_pix_fmts[] = {
132     AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUV444P9,
133     AV_PIX_FMT_NONE
134 };
135
136 static const enum AVPixelFormat out_yuv10_pix_fmts[] = {
137     AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUV444P10,
138     AV_PIX_FMT_NONE
139 };
140
141 static const enum AVPixelFormat out_yuv12_pix_fmts[] = {
142     AV_PIX_FMT_YUV444P12,
143     AV_PIX_FMT_NONE
144 };
145
146 static const enum AVPixelFormat out_rgb8_pix_fmts[] = {
147     AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
148     AV_PIX_FMT_NONE
149 };
150
151 static const enum AVPixelFormat out_rgb9_pix_fmts[] = {
152     AV_PIX_FMT_GBRP9,
153     AV_PIX_FMT_NONE
154 };
155
156 static const enum AVPixelFormat out_rgb10_pix_fmts[] = {
157     AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
158     AV_PIX_FMT_NONE
159 };
160
161 static const enum AVPixelFormat out_rgb12_pix_fmts[] = {
162     AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12,
163     AV_PIX_FMT_NONE
164 };
165
166 static const enum AVPixelFormat in1_pix_fmts[] = {
167     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
168     AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10,
169     AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10,
170     AV_PIX_FMT_YUV444P12,
171     AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
172     AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
173     AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12,
174     AV_PIX_FMT_NONE
175 };
176
177 static const enum AVPixelFormat in2_pix_fmts[] = {
178     AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P,
179     AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P,
180     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
181     AV_PIX_FMT_YUV411P,  AV_PIX_FMT_YUVJ411P,
182     AV_PIX_FMT_YUV440P,  AV_PIX_FMT_YUV410P,
183     AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
184     AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
185     AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12,
186     AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
187     AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
188     AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
189     AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
190     AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
191     AV_PIX_FMT_NONE
192 };
193
194 static int query_formats(AVFilterContext *ctx)
195 {
196     VectorscopeContext *s = ctx->priv;
197     const enum AVPixelFormat *out_pix_fmts;
198     const AVPixFmtDescriptor *desc;
199     AVFilterFormats *avff;
200     int depth, rgb, i, ret;
201
202     if (!ctx->inputs[0]->in_formats ||
203         !ctx->inputs[0]->in_formats->nb_formats) {
204         return AVERROR(EAGAIN);
205     }
206
207     if (!ctx->inputs[0]->out_formats) {
208         const enum AVPixelFormat *in_pix_fmts;
209
210         if ((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1))
211             in_pix_fmts = in2_pix_fmts;
212         else
213             in_pix_fmts = in1_pix_fmts;
214         if ((ret = ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->out_formats)) < 0)
215             return ret;
216     }
217
218     avff = ctx->inputs[0]->in_formats;
219     desc = av_pix_fmt_desc_get(avff->formats[0]);
220     rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
221     depth = desc->comp[0].depth;
222     for (i = 1; i < avff->nb_formats; i++) {
223         desc = av_pix_fmt_desc_get(avff->formats[i]);
224         if (rgb != (desc->flags & AV_PIX_FMT_FLAG_RGB) ||
225             depth != desc->comp[0].depth)
226             return AVERROR(EAGAIN);
227     }
228
229     if (rgb && depth == 8)
230         out_pix_fmts = out_rgb8_pix_fmts;
231     else if (rgb && depth == 9)
232         out_pix_fmts = out_rgb9_pix_fmts;
233     else if (rgb && depth == 10)
234         out_pix_fmts = out_rgb10_pix_fmts;
235     else if (rgb && depth == 12)
236         out_pix_fmts = out_rgb12_pix_fmts;
237     else if (depth == 8)
238         out_pix_fmts = out_yuv8_pix_fmts;
239     else if (depth == 9)
240         out_pix_fmts = out_yuv9_pix_fmts;
241     else if (depth == 10)
242         out_pix_fmts = out_yuv10_pix_fmts;
243     else if (depth == 12)
244         out_pix_fmts = out_yuv12_pix_fmts;
245     else
246         return AVERROR(EAGAIN);
247     if ((ret = ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->in_formats)) < 0)
248         return ret;
249
250     return 0;
251 }
252
253 static int config_output(AVFilterLink *outlink)
254 {
255     VectorscopeContext *s = outlink->src->priv;
256     int i;
257
258     s->intensity = s->fintensity * (s->size - 1);
259     outlink->h = outlink->w = s->size;
260     outlink->sample_aspect_ratio = (AVRational){1,1};
261
262     s->peak_memory = av_calloc(s->size, s->size);
263     if (!s->peak_memory)
264         return AVERROR(ENOMEM);
265
266     s->peak = av_calloc(s->size, sizeof(*s->peak));
267     if (!s->peak)
268         return AVERROR(ENOMEM);
269
270     for (i = 0; i < s->size; i++)
271         s->peak[i] = s->peak_memory + s->size * i;
272
273     return 0;
274 }
275
276 static void envelope_instant16(VectorscopeContext *s, AVFrame *out)
277 {
278     const int dlinesize = out->linesize[0] / 2;
279     uint16_t *dpd = s->mode == COLOR || !s->is_yuv ? (uint16_t *)out->data[s->pd] : (uint16_t *)out->data[0];
280     const int max = s->size - 1;
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] = max;
292             }
293         }
294     }
295 }
296
297 static void envelope_peak16(VectorscopeContext *s, AVFrame *out)
298 {
299     const int dlinesize = out->linesize[0] / 2;
300     uint16_t *dpd = s->mode == COLOR || !s->is_yuv ? (uint16_t *)out->data[s->pd] : (uint16_t *)out->data[0];
301     const int max = s->size - 1;
302     int i, j;
303
304     for (i = 0; i < out->height; i++) {
305         for (j = 0; j < out->width; j++) {
306             const int pos = i * dlinesize + j;
307
308             if (dpd[pos])
309                 s->peak[i][j] = 1;
310         }
311     }
312
313     if (s->envelope == 3)
314         envelope_instant16(s, out);
315
316     for (i = 0; i < out->height; i++) {
317         for (j = 0; j < out->width; j++) {
318             const int pos = i * dlinesize + j;
319
320             if (s->peak[i][j] && (((!j || !s->peak[i][j-1]) || ((j == (out->width - 1)) || !s->peak[i][j + 1]))
321                               || ((!i || !s->peak[i-1][j]) || ((i == (out->height - 1)) || !s->peak[i + 1][j])))) {
322                 dpd[pos] = max;
323             }
324         }
325     }
326 }
327
328 static void envelope_instant(VectorscopeContext *s, AVFrame *out)
329 {
330     const int dlinesize = out->linesize[0];
331     uint8_t *dpd = s->mode == COLOR || !s->is_yuv ? out->data[s->pd] : out->data[0];
332     int i, j;
333
334     for (i = 0; i < out->height; i++) {
335         for (j = 0; j < out->width; j++) {
336             const int pos = i * dlinesize + j;
337             const int poa = (i - 1) * dlinesize + j;
338             const int pob = (i + 1) * dlinesize + j;
339
340             if (dpd[pos] && (((!j || !dpd[pos - 1]) || ((j == (out->width - 1)) || !dpd[pos + 1]))
341                          || ((!i || !dpd[poa]) || ((i == (out->height - 1)) || !dpd[pob])))) {
342                 dpd[pos] = 255;
343             }
344         }
345     }
346 }
347
348 static void envelope_peak(VectorscopeContext *s, AVFrame *out)
349 {
350     const int dlinesize = out->linesize[0];
351     uint8_t *dpd = s->mode == COLOR || !s->is_yuv ? out->data[s->pd] : out->data[0];
352     int i, j;
353
354     for (i = 0; i < out->height; i++) {
355         for (j = 0; j < out->width; j++) {
356             const int pos = i * dlinesize + j;
357
358             if (dpd[pos])
359                 s->peak[i][j] = 1;
360         }
361     }
362
363     if (s->envelope == 3)
364         envelope_instant(s, out);
365
366     for (i = 0; i < out->height; i++) {
367         for (j = 0; j < out->width; j++) {
368             const int pos = i * dlinesize + j;
369
370             if (s->peak[i][j] && (((!j || !s->peak[i][j-1]) || ((j == (out->width - 1)) || !s->peak[i][j + 1]))
371                               || ((!i || !s->peak[i-1][j]) || ((i == (out->height - 1)) || !s->peak[i + 1][j])))) {
372                 dpd[pos] = 255;
373             }
374         }
375     }
376 }
377
378 static void envelope16(VectorscopeContext *s, AVFrame *out)
379 {
380     if (!s->envelope) {
381         return;
382     } else if (s->envelope == 1) {
383         envelope_instant16(s, out);
384     } else {
385         envelope_peak16(s, out);
386     }
387 }
388
389 static void envelope(VectorscopeContext *s, AVFrame *out)
390 {
391     if (!s->envelope) {
392         return;
393     } else if (s->envelope == 1) {
394         envelope_instant(s, out);
395     } else {
396         envelope_peak(s, out);
397     }
398 }
399
400 static void vectorscope16(VectorscopeContext *s, AVFrame *in, AVFrame *out, int pd)
401 {
402     const uint16_t * const *src = (const uint16_t * const *)in->data;
403     const int slinesizex = in->linesize[s->x] / 2;
404     const int slinesizey = in->linesize[s->y] / 2;
405     const int slinesized = in->linesize[pd] / 2;
406     const int dlinesize = out->linesize[0] / 2;
407     const int intensity = s->intensity;
408     const int px = s->x, py = s->y;
409     const int h = s->planeheight[py];
410     const int w = s->planewidth[px];
411     const uint16_t *spx = src[px];
412     const uint16_t *spy = src[py];
413     const uint16_t *spd = src[pd];
414     const int hsub = s->hsub;
415     const int vsub = s->vsub;
416     uint16_t **dst = (uint16_t **)out->data;
417     uint16_t *dpx = dst[px];
418     uint16_t *dpy = dst[py];
419     uint16_t *dpd = dst[pd];
420     const int max = s->size - 1;
421     const int mid = s->size / 2;
422     const int tmin = s->tmin;
423     const int tmax = s->tmax;
424     int i, j, k;
425
426     for (k = 0; k < 4 && dst[k]; k++) {
427         for (i = 0; i < out->height ; i++)
428             for (j = 0; j < out->width; j++)
429                 AV_WN16(out->data[k] + i * out->linesize[k] + j * 2,
430                         (s->mode == COLOR || s->mode == COLOR5) && k == s->pd ? 0 : s->bg_color[k]);
431     }
432
433     switch (s->mode) {
434     case COLOR:
435     case COLOR5:
436     case GRAY:
437         if (s->is_yuv) {
438             for (i = 0; i < h; i++) {
439                 const int iwx = i * slinesizex;
440                 const int iwy = i * slinesizey;
441                 const int iwd = i * slinesized;
442                 for (j = 0; j < w; j++) {
443                     const int x = FFMIN(spx[iwx + j], max);
444                     const int y = FFMIN(spy[iwy + j], max);
445                     const int z = spd[iwd + j];
446                     const int pos = y * dlinesize + x;
447
448                     if (z < tmin || z > tmax)
449                         continue;
450
451                     dpd[pos] = FFMIN(dpd[pos] + intensity, max);
452                 }
453             }
454         } else {
455             for (i = 0; i < h; i++) {
456                 const int iwx = i * slinesizex;
457                 const int iwy = i * slinesizey;
458                 const int iwd = i * slinesized;
459                 for (j = 0; j < w; j++) {
460                     const int x = FFMIN(spx[iwx + j], max);
461                     const int y = FFMIN(spy[iwy + j], max);
462                     const int z = spd[iwd + j];
463                     const int pos = y * dlinesize + x;
464
465                     if (z < tmin || z > tmax)
466                         continue;
467
468                     dst[0][pos] = FFMIN(dst[0][pos] + intensity, max);
469                     dst[1][pos] = FFMIN(dst[1][pos] + intensity, max);
470                     dst[2][pos] = FFMIN(dst[2][pos] + intensity, max);
471                 }
472             }
473         }
474         break;
475     case COLOR2:
476         if (s->is_yuv) {
477             for (i = 0; i < h; i++) {
478                 const int iw1 = i * slinesizex;
479                 const int iw2 = i * slinesizey;
480                 const int iwd = i * slinesized;
481                 for (j = 0; j < w; j++) {
482                     const int x = FFMIN(spx[iw1 + j], max);
483                     const int y = FFMIN(spy[iw2 + j], max);
484                     const int z = spd[iwd + j];
485                     const int pos = y * dlinesize + x;
486
487                     if (z < tmin || z > tmax)
488                         continue;
489
490                     if (!dpd[pos])
491                         dpd[pos] = FFABS(mid - x) + FFABS(mid - y);
492                     dpx[pos] = x;
493                     dpy[pos] = y;
494                 }
495             }
496         } else {
497             for (i = 0; i < h; i++) {
498                 const int iw1 = i * slinesizex;
499                 const int iw2 = i * slinesizey;
500                 const int iwd = i * slinesized;
501                 for (j = 0; j < w; j++) {
502                     const int x = FFMIN(spx[iw1 + j], max);
503                     const int y = FFMIN(spy[iw2 + j], max);
504                     const int z = spd[iwd + j];
505                     const int pos = y * dlinesize + x;
506
507                     if (z < tmin || z > tmax)
508                         continue;
509
510                     if (!dpd[pos])
511                         dpd[pos] = FFMIN(x + y, max);
512                     dpx[pos] = x;
513                     dpy[pos] = y;
514                 }
515             }
516         }
517         break;
518     case COLOR3:
519         for (i = 0; i < h; i++) {
520             const int iw1 = i * slinesizex;
521             const int iw2 = i * slinesizey;
522             const int iwd = i * slinesized;
523             for (j = 0; j < w; j++) {
524                 const int x = FFMIN(spx[iw1 + j], max);
525                 const int y = FFMIN(spy[iw2 + j], max);
526                 const int z = spd[iwd + j];
527                 const int pos = y * dlinesize + x;
528
529                 if (z < tmin || z > tmax)
530                     continue;
531
532                 dpd[pos] = FFMIN(max, dpd[pos] + intensity);
533                 dpx[pos] = x;
534                 dpy[pos] = y;
535             }
536         }
537         break;
538     case COLOR4:
539         for (i = 0; i < in->height; i++) {
540             const int iwx = (i >> vsub) * slinesizex;
541             const int iwy = (i >> vsub) * slinesizey;
542             const int iwd = i * slinesized;
543             for (j = 0; j < in->width; j++) {
544                 const int x = FFMIN(spx[iwx + (j >> hsub)], max);
545                 const int y = FFMIN(spy[iwy + (j >> hsub)], max);
546                 const int z = spd[iwd + j];
547                 const int pos = y * dlinesize + x;
548
549                 if (z < tmin || z > tmax)
550                     continue;
551
552                 dpd[pos] = FFMAX(z, dpd[pos]);
553                 dpx[pos] = x;
554                 dpy[pos] = y;
555             }
556         }
557         break;
558     default:
559         av_assert0(0);
560     }
561
562     envelope16(s, out);
563
564     if (dst[3]) {
565         for (i = 0; i < out->height; i++) {
566             for (j = 0; j < out->width; j++) {
567                 int pos = i * dlinesize + j;
568
569                 if (dpd[pos])
570                     dst[3][pos] = max;
571             }
572         }
573     }
574
575     if (s->mode == COLOR) {
576         for (i = 0; i < out->height; i++) {
577             for (j = 0; j < out->width; j++) {
578                 if (!dpd[i * dlinesize + j]) {
579                     dpx[i * dlinesize + j] = j;
580                     dpy[i * dlinesize + j] = i;
581                     dpd[i * dlinesize + j] = mid;
582                 }
583             }
584         }
585     } else if (s->mode == COLOR5) {
586         for (i = 0; i < out->height; i++) {
587             for (j = 0; j < out->width; j++) {
588                 if (!dpd[i * dlinesize + j]) {
589                     dpx[i * dlinesize + j] = j;
590                     dpy[i * dlinesize + j] = i;
591                     dpd[i * dlinesize + j] = mid * M_SQRT2 - hypot(i - mid, j - mid);
592                 }
593             }
594         }
595     }
596 }
597
598 static void vectorscope8(VectorscopeContext *s, AVFrame *in, AVFrame *out, int pd)
599 {
600     const uint8_t * const *src = (const uint8_t * const *)in->data;
601     const int slinesizex = in->linesize[s->x];
602     const int slinesizey = in->linesize[s->y];
603     const int slinesized = in->linesize[pd];
604     const int dlinesize = out->linesize[0];
605     const int intensity = s->intensity;
606     const int px = s->x, py = s->y;
607     const int h = s->planeheight[py];
608     const int w = s->planewidth[px];
609     const uint8_t *spx = src[px];
610     const uint8_t *spy = src[py];
611     const uint8_t *spd = src[pd];
612     const int hsub = s->hsub;
613     const int vsub = s->vsub;
614     uint8_t **dst = out->data;
615     uint8_t *dpx = dst[px];
616     uint8_t *dpy = dst[py];
617     uint8_t *dpd = dst[pd];
618     const int tmin = s->tmin;
619     const int tmax = s->tmax;
620     int i, j, k;
621
622     for (k = 0; k < 4 && dst[k]; k++)
623         for (i = 0; i < out->height ; i++)
624             memset(dst[k] + i * out->linesize[k],
625                    (s->mode == COLOR || s->mode == COLOR5) && k == s->pd ? 0 : s->bg_color[k], out->width);
626
627     switch (s->mode) {
628     case COLOR5:
629     case COLOR:
630     case GRAY:
631         if (s->is_yuv) {
632             for (i = 0; i < h; i++) {
633                 const int iwx = i * slinesizex;
634                 const int iwy = i * slinesizey;
635                 const int iwd = i * slinesized;
636                 for (j = 0; j < w; j++) {
637                     const int x = spx[iwx + j];
638                     const int y = spy[iwy + j];
639                     const int z = spd[iwd + j];
640                     const int pos = y * dlinesize + x;
641
642                     if (z < tmin || z > tmax)
643                         continue;
644
645                     dpd[pos] = FFMIN(dpd[pos] + intensity, 255);
646                 }
647             }
648         } else {
649             for (i = 0; i < h; i++) {
650                 const int iwx = i * slinesizex;
651                 const int iwy = i * slinesizey;
652                 const int iwd = i * slinesized;
653                 for (j = 0; j < w; j++) {
654                     const int x = spx[iwx + j];
655                     const int y = spy[iwy + j];
656                     const int z = spd[iwd + j];
657                     const int pos = y * dlinesize + x;
658
659                     if (z < tmin || z > tmax)
660                         continue;
661
662                     dst[0][pos] = FFMIN(dst[0][pos] + intensity, 255);
663                     dst[1][pos] = FFMIN(dst[1][pos] + intensity, 255);
664                     dst[2][pos] = FFMIN(dst[2][pos] + intensity, 255);
665                 }
666             }
667         }
668         break;
669     case COLOR2:
670         if (s->is_yuv) {
671             for (i = 0; i < h; i++) {
672                 const int iw1 = i * slinesizex;
673                 const int iw2 = i * slinesizey;
674                 const int iwd = i * slinesized;
675                 for (j = 0; j < w; j++) {
676                     const int x = spx[iw1 + j];
677                     const int y = spy[iw2 + j];
678                     const int z = spd[iwd + j];
679                     const int pos = y * dlinesize + x;
680
681                     if (z < tmin || z > tmax)
682                         continue;
683
684                     if (!dpd[pos])
685                         dpd[pos] = FFABS(128 - x) + FFABS(128 - y);
686                     dpx[pos] = x;
687                     dpy[pos] = y;
688                 }
689             }
690         } else {
691             for (i = 0; i < h; i++) {
692                 const int iw1 = i * slinesizex;
693                 const int iw2 = i * slinesizey;
694                 const int iwd = i * slinesized;
695                 for (j = 0; j < w; j++) {
696                     const int x = spx[iw1 + j];
697                     const int y = spy[iw2 + j];
698                     const int z = spd[iwd + j];
699                     const int pos = y * dlinesize + x;
700
701                     if (z < tmin || z > tmax)
702                         continue;
703
704                     if (!dpd[pos])
705                         dpd[pos] = FFMIN(x + y, 255);
706                     dpx[pos] = x;
707                     dpy[pos] = y;
708                 }
709             }
710         }
711         break;
712     case COLOR3:
713         for (i = 0; i < h; i++) {
714             const int iw1 = i * slinesizex;
715             const int iw2 = i * slinesizey;
716             const int iwd = i * slinesized;
717             for (j = 0; j < w; j++) {
718                 const int x = spx[iw1 + j];
719                 const int y = spy[iw2 + j];
720                 const int z = spd[iwd + j];
721                 const int pos = y * dlinesize + x;
722
723                 if (z < tmin || z > tmax)
724                     continue;
725
726                 dpd[pos] = FFMIN(255, dpd[pos] + intensity);
727                 dpx[pos] = x;
728                 dpy[pos] = y;
729             }
730         }
731         break;
732     case COLOR4:
733         for (i = 0; i < in->height; i++) {
734             const int iwx = (i >> vsub) * slinesizex;
735             const int iwy = (i >> vsub) * slinesizey;
736             const int iwd = i * slinesized;
737             for (j = 0; j < in->width; j++) {
738                 const int x = spx[iwx + (j >> hsub)];
739                 const int y = spy[iwy + (j >> hsub)];
740                 const int z = spd[iwd + j];
741                 const int pos = y * dlinesize + x;
742
743                 if (z < tmin || z > tmax)
744                     continue;
745
746                 dpd[pos] = FFMAX(z, dpd[pos]);
747                 dpx[pos] = x;
748                 dpy[pos] = y;
749             }
750         }
751         break;
752     default:
753         av_assert0(0);
754     }
755
756     envelope(s, out);
757
758     if (dst[3]) {
759         for (i = 0; i < out->height; i++) {
760             for (j = 0; j < out->width; j++) {
761                 int pos = i * dlinesize + j;
762
763                 if (dpd[pos])
764                     dst[3][pos] = 255;
765             }
766         }
767     }
768
769     if (s->mode == COLOR) {
770         for (i = 0; i < out->height; i++) {
771             for (j = 0; j < out->width; j++) {
772                 if (!dpd[i * out->linesize[pd] + j]) {
773                     dpx[i * out->linesize[px] + j] = j;
774                     dpy[i * out->linesize[py] + j] = i;
775                     dpd[i * out->linesize[pd] + j] = 128;
776                 }
777             }
778         }
779     } else if (s->mode == COLOR5) {
780         for (i = 0; i < out->height; i++) {
781             for (j = 0; j < out->width; j++) {
782                 if (!dpd[i * out->linesize[pd] + j]) {
783                     dpx[i * out->linesize[px] + j] = j;
784                     dpy[i * out->linesize[py] + j] = i;
785                     dpd[i * out->linesize[pd] + j] = 128 * M_SQRT2 - hypot(i - 128, j - 128);
786                 }
787             }
788         }
789     }
790 }
791
792 const static char *positions_name[] = {
793     "R", "B", "Cy", "Yl", "G", "Mg",
794 };
795
796 const static uint16_t positions[][14][3] = {
797   {
798     {  81,  90, 240 }, {  41, 240, 110 }, { 170, 166,  16 },
799     { 210,  16, 146 }, { 145,  54,  34 }, { 106, 202, 222 },
800     { 162,  44, 142 }, { 131, 156,  44 }, { 112,  72,  58 },
801     {  84, 184, 198 }, {  65, 100, 212 }, {  35, 212, 114 },
802     { 235, 128, 128 }, { 16, 128, 128 } },
803   { {  63, 102, 240 }, {  32, 240, 118 }, { 188, 154,  16 },
804     { 219,  16, 138 }, { 173,  42,  26 }, {  78, 214, 230 },
805     {  28, 212, 120 }, {  51, 109, 212 }, {  63, 193, 204 },
806     { 133,  63,  52 }, { 145, 147,  44 }, { 168,  44, 136 },
807     { 235, 128, 128 }, { 16, 128, 128 } },
808   { {  81*2,  90*2, 240*2 }, {  41*2, 240*2, 110*2 }, { 170*2, 166*2,  16*2 },
809     { 210*2,  16*2, 146*2 }, { 145*2,  54*2,  34*2 }, { 106*2, 202*2, 222*2 },
810     { 162*2,  44*2, 142*2 }, { 131*2, 156*2,  44*2 }, { 112*2,  72*2,  58*2 },
811     {  84*2, 184*2, 198*2 }, {  65*2, 100*2, 212*2 }, {  35*2, 212*2, 114*2 },
812     { 470, 256, 256 }, { 32, 256, 256 } },
813   { {  63*2, 102*2, 240*2 }, {  32*2, 240*2, 118*2 }, { 188*2, 154*2,  16*2 },
814     { 219*2,  16*2, 138*2 }, { 173*2,  42*2,  26*2 }, {  78*2, 214*2, 230*2 },
815     {  28*2, 212*2, 120*2 }, {  51*2, 109*2, 212*2 }, {  63*2, 193*2, 204*2 },
816     { 133*2,  63*2,  52*2 }, { 145*2, 147*2,  44*2 }, { 168*2,  44*2, 136*2 },
817     { 470, 256, 256 }, { 32, 256, 256 } },
818   { {  81*4,  90*4, 240*4 }, {  41*4, 240*4, 110*4 }, { 170*4, 166*4,  16*4 },
819     { 210*4,  16*4, 146*4 }, { 145*4,  54*4,  34*4 }, { 106*4, 202*4, 222*4 },
820     { 162*4,  44*4, 142*4 }, { 131*4, 156*4,  44*4 }, { 112*4,  72*4,  58*4 },
821     {  84*4, 184*4, 198*4 }, {  65*4, 100*4, 212*4 }, {  35*4, 212*4, 114*4 },
822     { 940, 512, 512 }, { 64, 512, 512 } },
823   { {  63*4, 102*4, 240*4 }, {  32*4, 240*4, 118*4 }, { 188*4, 154*4,  16*4 },
824     { 219*4,  16*4, 138*4 }, { 173*4,  42*4,  26*4 }, {  78*4, 214*4, 230*4 },
825     {  28*4, 212*4, 120*4 }, {  51*4, 109*4, 212*4 }, {  63*4, 193*4, 204*4 },
826     { 133*4,  63*4,  52*4 }, { 145*4, 147*4,  44*4 }, { 168*4,  44*4, 136*4 },
827     { 940, 512, 512 }, { 64, 512, 512 } },
828   { {  81*8,  90*4, 240*8 }, {  41*8, 240*8, 110*8 }, { 170*8, 166*8,  16*8 },
829     { 210*8,  16*4, 146*8 }, { 145*8,  54*8,  34*8 }, { 106*8, 202*8, 222*8 },
830     { 162*8,  44*4, 142*8 }, { 131*8, 156*8,  44*8 }, { 112*8,  72*8,  58*8 },
831     {  84*8, 184*4, 198*8 }, {  65*8, 100*8, 212*8 }, {  35*8, 212*8, 114*8 },
832     { 1880, 1024, 1024 }, { 128, 1024, 1024 } },
833   { {  63*8, 102*8, 240*8 }, {  32*8, 240*8, 118*8 }, { 188*8, 154*8,  16*8 },
834     { 219*8,  16*8, 138*8 }, { 173*8,  42*8,  26*8 }, {  78*8, 214*8, 230*8 },
835     {  28*8, 212*8, 120*8 }, {  51*8, 109*8, 212*8 }, {  63*8, 193*8, 204*8 },
836     { 133*8,  63*8,  52*8 }, { 145*8, 147*8,  44*8 }, { 168*8,  44*8, 136*8 },
837     { 1880, 1024, 1024 }, { 128, 1024, 1024 } },
838   { {  81*16,  90*16, 240*16 }, {  41*16, 240*16, 110*16 }, { 170*16, 166*16,  16*16 },
839     { 210*16,  16*16, 146*16 }, { 145*16,  54*16,  34*16 }, { 106*16, 202*16, 222*16 },
840     { 162*16,  44*16, 142*16 }, { 131*16, 156*16,  44*16 }, { 112*16,  72*16,  58*16 },
841     {  84*16, 184*16, 198*16 }, {  65*16, 100*16, 212*16 }, {  35*16, 212*16, 114*16 },
842     { 3760, 2048, 2048 }, { 256, 2048, 2048 } },
843   { {  63*16, 102*16, 240*16 }, {  32*16, 240*16, 118*16 }, { 188*16, 154*16,  16*16 },
844     { 219*16,  16*16, 138*16 }, { 173*16,  42*16,  26*16 }, {  78*16, 214*16, 230*16 },
845     {  28*16, 212*16, 120*16 }, {  51*16, 109*16, 212*16 }, {  63*16, 193*16, 204*16 },
846     { 133*16,  63*16,  52*16 }, { 145*16, 147*16,  44*16 }, { 168*16,  44*16, 136*16 },
847     { 3760, 2048, 2048 }, { 256, 2048, 2048 } },
848 };
849
850 static void draw_dots(uint8_t *dst, int L, int v, float o)
851 {
852     const float f = 1. - o;
853     const float V = o * v;
854     int l = L * 2;
855
856     dst[ l - 3] = dst[ l - 3] * f + V;
857     dst[ l + 3] = dst[ l + 3] * f + V;
858     dst[-l - 3] = dst[-l - 3] * f + V;
859     dst[-l + 3] = dst[-l + 3] * f + V;
860
861     l += L;
862
863     dst[ l - 3] = dst[ l - 3] * f + V;
864     dst[ l + 3] = dst[ l + 3] * f + V;
865     dst[ l - 2] = dst[ l - 2] * f + V;
866     dst[ l + 2] = dst[ l + 2] * f + V;
867     dst[-l - 3] = dst[-l - 3] * f + V;
868     dst[-l + 3] = dst[-l + 3] * f + V;
869     dst[-l - 2] = dst[-l - 2] * f + V;
870     dst[-l + 2] = dst[-l + 2] * f + V;
871 }
872
873 static void draw_dots16(uint16_t *dst, int L, int v, float o)
874 {
875     const float f = 1. - o;
876     const float V = o * v;
877     int l = L * 2;
878
879     dst[ l - 3] = dst[ l - 3] * f + V;
880     dst[ l + 3] = dst[ l + 3] * f + V;
881     dst[-l - 3] = dst[-l - 3] * f + V;
882     dst[-l + 3] = dst[-l + 3] * f + V;
883
884     l += L;
885
886     dst[ l - 3] = dst[ l - 3] * f + V;
887     dst[ l + 3] = dst[ l + 3] * f + V;
888     dst[ l - 2] = dst[ l - 2] * f + V;
889     dst[ l + 2] = dst[ l + 2] * f + V;
890     dst[-l - 3] = dst[-l - 3] * f + V;
891     dst[-l + 3] = dst[-l + 3] * f + V;
892     dst[-l - 2] = dst[-l - 2] * f + V;
893     dst[-l + 2] = dst[-l + 2] * f + V;
894 }
895
896 static void none_graticule(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P)
897 {
898 }
899
900 static void draw_htext(AVFrame *out, int x, int y, float o1, float o2, const char *txt, const uint8_t color[4])
901 {
902     const uint8_t *font;
903     int font_height;
904     int i, plane;
905
906     font = avpriv_cga_font,   font_height =  8;
907
908     for (plane = 0; plane < 4 && out->data[plane]; plane++) {
909         for (i = 0; txt[i]; i++) {
910             int char_y, mask;
911             int v = color[plane];
912
913             uint8_t *p = out->data[plane] + y * out->linesize[plane] + (x + i * 8);
914             for (char_y = font_height - 1; char_y >= 0; char_y--) {
915                 for (mask = 0x80; mask; mask >>= 1) {
916                     if (font[txt[i] * font_height + char_y] & mask)
917                         p[0] = p[0] * o2 + v * o1;
918                     p++;
919                 }
920                 p += out->linesize[plane] - 8;
921             }
922         }
923     }
924 }
925
926 static void draw_htext16(AVFrame *out, int x, int y, float o1, float o2, const char *txt, const uint16_t color[4])
927 {
928     const uint8_t *font;
929     int font_height;
930     int i, plane;
931
932     font = avpriv_cga_font,   font_height =  8;
933
934     for (plane = 0; plane < 4 && out->data[plane]; plane++) {
935         for (i = 0; txt[i]; i++) {
936             int char_y, mask;
937             int v = color[plane];
938
939             uint16_t *p = (uint16_t *)(out->data[plane] + y * out->linesize[plane]) + (x + i * 8);
940             for (char_y = font_height - 1; char_y >= 0; char_y--) {
941                 for (mask = 0x80; mask; mask >>= 1) {
942                     if (font[txt[i] * font_height + char_y] & mask)
943                         p[0] = p[0] * o2 + v * o1;
944                     p++;
945                 }
946                 p += out->linesize[plane] / 2 - 8;
947             }
948         }
949     }
950 }
951
952 static void color_graticule16(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P)
953 {
954     const int max = s->size - 1;
955     const float o = s->opacity;
956     int i;
957
958     for (i = 0; i < 12; i++) {
959         int x = positions[P][i][X];
960         int y = positions[P][i][Y];
961         int d = positions[P][i][D];
962
963         draw_dots16((uint16_t *)(out->data[D] + y * out->linesize[D] + x * 2), out->linesize[D] / 2, d, o);
964         draw_dots16((uint16_t *)(out->data[X] + y * out->linesize[X] + x * 2), out->linesize[X] / 2, x, o);
965         draw_dots16((uint16_t *)(out->data[Y] + y * out->linesize[Y] + x * 2), out->linesize[Y] / 2, y, o);
966         if (out->data[3])
967             draw_dots16((uint16_t *)(out->data[3] + y * out->linesize[3] + x * 2), out->linesize[3] / 2, max, o);
968     }
969
970     if (s->flags & 1) {
971         int x = positions[P][12][X];
972         int y = positions[P][12][Y];
973         int d = positions[P][12][D];
974
975         draw_dots16((uint16_t *)(out->data[D] + y * out->linesize[D] + x * 2), out->linesize[D] / 2, d, o);
976         draw_dots16((uint16_t *)(out->data[X] + y * out->linesize[X] + x * 2), out->linesize[X] / 2, x, o);
977         draw_dots16((uint16_t *)(out->data[Y] + y * out->linesize[Y] + x * 2), out->linesize[Y] / 2, y, o);
978         if (out->data[3])
979             draw_dots16((uint16_t *)(out->data[3] + y * out->linesize[3] + x * 2), out->linesize[3] / 2, max, o);
980     }
981
982     if (s->flags & 2) {
983         int x = positions[P][13][X];
984         int y = positions[P][13][Y];
985         int d = positions[P][13][D];
986
987         draw_dots16((uint16_t *)(out->data[D] + y * out->linesize[D] + x * 2), out->linesize[D] / 2, d, o);
988         draw_dots16((uint16_t *)(out->data[X] + y * out->linesize[X] + x * 2), out->linesize[X] / 2, x, o);
989         draw_dots16((uint16_t *)(out->data[Y] + y * out->linesize[Y] + x * 2), out->linesize[Y] / 2, y, o);
990         if (out->data[3])
991             draw_dots16((uint16_t *)(out->data[3] + y * out->linesize[3] + x * 2), out->linesize[3] / 2, max, o);
992     }
993
994     for (i = 0; i < 6 && s->flags & 4; i++) {
995         uint16_t color[4] = { 0, 0, 0, 0 };
996         int x = positions[P][i][X];
997         int y = positions[P][i][Y];
998         int d = positions[P][i][D];
999
1000         color[D] = d;
1001         color[X] = x;
1002         color[Y] = y;
1003         color[3] = max;
1004
1005         if (x > max / 2)
1006             x += 8;
1007         else
1008             x -= 14;
1009         if (y > max / 2)
1010             y += 8;
1011         else
1012             y -= 14;
1013
1014         x = av_clip(x, 0, out->width - 9);
1015         y = av_clip(y, 0, out->height - 9);
1016         draw_htext16(out, x, y, o, 1. - o, positions_name[i], color);
1017     }
1018 }
1019
1020 static void color_graticule(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P)
1021 {
1022     const float o = s->opacity;
1023     int i;
1024
1025     for (i = 0; i < 12; i++) {
1026         int x = positions[P][i][X];
1027         int y = positions[P][i][Y];
1028         int d = positions[P][i][D];
1029
1030         draw_dots(out->data[D] + y * out->linesize[D] + x, out->linesize[D], d, o);
1031         draw_dots(out->data[X] + y * out->linesize[X] + x, out->linesize[X], x, o);
1032         draw_dots(out->data[Y] + y * out->linesize[Y] + x, out->linesize[Y], y, o);
1033         if (out->data[3])
1034             draw_dots(out->data[3] + y * out->linesize[3] + x, out->linesize[3], 255, o);
1035     }
1036
1037     if (s->flags & 1) {
1038         int x = positions[P][12][X];
1039         int y = positions[P][12][Y];
1040         int d = positions[P][12][D];
1041
1042         draw_dots(out->data[D] + y * out->linesize[D] + x, out->linesize[D], d, o);
1043         draw_dots(out->data[X] + y * out->linesize[X] + x, out->linesize[X], x, o);
1044         draw_dots(out->data[Y] + y * out->linesize[Y] + x, out->linesize[Y], y, o);
1045         if (out->data[3])
1046             draw_dots(out->data[3] + y * out->linesize[3] + x, out->linesize[3], 255, o);
1047     }
1048
1049     if (s->flags & 2) {
1050         int x = positions[P][13][X];
1051         int y = positions[P][13][Y];
1052         int d = positions[P][12][D];
1053
1054         draw_dots(out->data[D] + y * out->linesize[D] + x, out->linesize[D], d, o);
1055         draw_dots(out->data[X] + y * out->linesize[X] + x, out->linesize[X], x, o);
1056         draw_dots(out->data[Y] + y * out->linesize[Y] + x, out->linesize[Y], y, o);
1057         if (out->data[3])
1058             draw_dots(out->data[3] + y * out->linesize[3] + x, out->linesize[3], 255, o);
1059     }
1060
1061     for (i = 0; i < 6 && s->flags & 4; i++) {
1062         uint8_t color[4] = { 0, 0, 0, 255 };
1063         int x = positions[P][i][X];
1064         int y = positions[P][i][Y];
1065         int d = positions[P][i][D];
1066
1067         color[D] = d;
1068         color[X] = x;
1069         color[Y] = y;
1070
1071         if (x > 128)
1072             x += 8;
1073         else
1074             x -= 14;
1075         if (y > 128)
1076             y += 8;
1077         else
1078             y -= 14;
1079
1080         x = av_clip(x, 0, out->width - 9);
1081         y = av_clip(y, 0, out->height - 9);
1082         draw_htext(out, x, y, o, 1. - o, positions_name[i], color);
1083     }
1084 }
1085
1086 static void green_graticule16(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P)
1087 {
1088     const int max = s->size - 1;
1089     const float o = s->opacity;
1090     const int m = s->mult;
1091     int i;
1092
1093     for (i = 0; i < 12; i++) {
1094         int x = positions[P][i][X];
1095         int y = positions[P][i][Y];
1096
1097         draw_dots16((uint16_t *)(out->data[0] + y * out->linesize[0] + x * 2), out->linesize[0] / 2, 128 * m, o);
1098         draw_dots16((uint16_t *)(out->data[1] + y * out->linesize[1] + x * 2), out->linesize[1] / 2, 0, o);
1099         draw_dots16((uint16_t *)(out->data[2] + y * out->linesize[2] + x * 2), out->linesize[2] / 2, 0, o);
1100         if (out->data[3])
1101             draw_dots16((uint16_t *)(out->data[3] + y * out->linesize[3] + x * 2), out->linesize[3] / 2, max, o);
1102     }
1103
1104     if (s->flags & 1) {
1105         int x = positions[P][12][X];
1106         int y = positions[P][12][Y];
1107
1108         draw_dots16((uint16_t *)(out->data[0] + y * out->linesize[0] + x * 2), out->linesize[0] / 2, 128 * m, o);
1109         draw_dots16((uint16_t *)(out->data[1] + y * out->linesize[1] + x * 2), out->linesize[1] / 2, 0, o);
1110         draw_dots16((uint16_t *)(out->data[2] + y * out->linesize[2] + x * 2), out->linesize[2] / 2, 0, o);
1111         if (out->data[3])
1112             draw_dots16((uint16_t *)(out->data[3] + y * out->linesize[3] + x * 2), out->linesize[3] / 2, max, o);
1113     }
1114
1115     if (s->flags & 2) {
1116         int x = positions[P][13][X];
1117         int y = positions[P][13][Y];
1118
1119         draw_dots16((uint16_t *)(out->data[0] + y * out->linesize[0] + x * 2), out->linesize[0] / 2, 128 * m, o);
1120         draw_dots16((uint16_t *)(out->data[1] + y * out->linesize[1] + x * 2), out->linesize[1] / 2, 0, o);
1121         draw_dots16((uint16_t *)(out->data[2] + y * out->linesize[2] + x * 2), out->linesize[2] / 2, 0, o);
1122         if (out->data[3])
1123             draw_dots16((uint16_t *)(out->data[3] + y * out->linesize[3] + x * 2), out->linesize[3] / 2, max, o);
1124     }
1125
1126     for (i = 0; i < 6 && s->flags & 4; i++) {
1127         const uint16_t color[4] = { 128 * m, 0, 0, max };
1128         int x = positions[P][i][X];
1129         int y = positions[P][i][Y];
1130
1131         if (x > max / 2)
1132             x += 8;
1133         else
1134             x -= 14;
1135         if (y > max / 2)
1136             y += 8;
1137         else
1138             y -= 14;
1139
1140         x = av_clip(x, 0, out->width - 9);
1141         y = av_clip(y, 0, out->height - 9);
1142         draw_htext16(out, x, y, o, 1. - o, positions_name[i], color);
1143     }
1144 }
1145
1146 static void green_graticule(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P)
1147 {
1148     const float o = s->opacity;
1149     int i;
1150
1151     for (i = 0; i < 12; i++) {
1152         int x = positions[P][i][X];
1153         int y = positions[P][i][Y];
1154
1155         draw_dots(out->data[0] + y * out->linesize[0] + x, out->linesize[0], 128, o);
1156         draw_dots(out->data[1] + y * out->linesize[1] + x, out->linesize[1], 0, o);
1157         draw_dots(out->data[2] + y * out->linesize[2] + x, out->linesize[2], 0, o);
1158         if (out->data[3])
1159             draw_dots(out->data[3] + y * out->linesize[3] + x, out->linesize[3], 255, o);
1160     }
1161
1162     if (s->flags & 1) {
1163         int x = positions[P][12][X];
1164         int y = positions[P][12][Y];
1165
1166         draw_dots(out->data[0] + y * out->linesize[0] + x, out->linesize[0], 128, o);
1167         draw_dots(out->data[1] + y * out->linesize[1] + x, out->linesize[1], 0, o);
1168         draw_dots(out->data[2] + y * out->linesize[2] + x, out->linesize[2], 0, o);
1169         if (out->data[3])
1170             draw_dots(out->data[3] + y * out->linesize[3] + x, out->linesize[3], 255, o);
1171     }
1172
1173     if (s->flags & 2) {
1174         int x = positions[P][13][X];
1175         int y = positions[P][13][Y];
1176
1177         draw_dots(out->data[0] + y * out->linesize[0] + x, out->linesize[0], 128, o);
1178         draw_dots(out->data[1] + y * out->linesize[1] + x, out->linesize[1], 0, o);
1179         draw_dots(out->data[2] + y * out->linesize[2] + x, out->linesize[2], 0, o);
1180         if (out->data[3])
1181             draw_dots(out->data[3] + y * out->linesize[3] + x, out->linesize[3], 255, o);
1182     }
1183
1184     for (i = 0; i < 6 && s->flags & 4; i++) {
1185         const uint8_t color[4] = { 128, 0, 0, 255 };
1186         int x = positions[P][i][X];
1187         int y = positions[P][i][Y];
1188
1189         if (x > 128)
1190             x += 8;
1191         else
1192             x -= 14;
1193         if (y > 128)
1194             y += 8;
1195         else
1196             y -= 14;
1197
1198         x = av_clip(x, 0, out->width - 9);
1199         y = av_clip(y, 0, out->height - 9);
1200         draw_htext(out, x, y, o, 1. - o, positions_name[i], color);
1201     }
1202 }
1203
1204 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
1205 {
1206     AVFilterContext *ctx  = inlink->dst;
1207     VectorscopeContext *s = ctx->priv;
1208     AVFilterLink *outlink = ctx->outputs[0];
1209     AVFrame *out;
1210     int plane;
1211
1212     if (s->colorspace) {
1213         s->cs = (s->depth - 8) * 2 + s->colorspace - 1;
1214     } else {
1215         switch (in->colorspace) {
1216         case AVCOL_SPC_SMPTE170M:
1217         case AVCOL_SPC_BT470BG:
1218             s->cs = (s->depth - 8) * 2 + 0;
1219             break;
1220         case AVCOL_SPC_BT709:
1221         default:
1222             s->cs = (s->depth - 8) * 2 + 1;
1223         }
1224     }
1225
1226     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
1227     if (!out) {
1228         av_frame_free(&in);
1229         return AVERROR(ENOMEM);
1230     }
1231     av_frame_copy_props(out, in);
1232
1233     s->vectorscope(s, in, out, s->pd);
1234     s->graticulef(s, out, s->x, s->y, s->pd, s->cs);
1235
1236     for (plane = 0; plane < 4; plane++) {
1237         if (out->data[plane]) {
1238             out->data[plane]    += (s->size - 1) * out->linesize[plane];
1239             out->linesize[plane] = -out->linesize[plane];
1240         }
1241     }
1242
1243     av_frame_free(&in);
1244     return ff_filter_frame(outlink, out);
1245 }
1246
1247 static int config_input(AVFilterLink *inlink)
1248 {
1249     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
1250     AVFilterContext *ctx = inlink->dst;
1251     VectorscopeContext *s = ctx->priv;
1252
1253     s->is_yuv = !(desc->flags & AV_PIX_FMT_FLAG_RGB);
1254     s->size = 1 << desc->comp[0].depth;
1255     s->mult = s->size / 256;
1256     s->depth = desc->comp[0].depth;
1257     s->tmin = s->lthreshold * (s->size - 1);
1258     s->tmax = s->hthreshold * (s->size - 1);
1259
1260     if (s->tmin > s->tmax) {
1261         av_log(ctx, AV_LOG_ERROR, "low threshold should be less than high threshold\n");
1262         return AVERROR(EINVAL);
1263     }
1264
1265     if (s->mode == GRAY && s->is_yuv)
1266         s->pd = 0;
1267     else {
1268         if ((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1))
1269             s->pd = 0;
1270         else if ((s->x == 0 && s->y == 2) || (s->x == 2 && s->y == 0))
1271             s->pd = 1;
1272         else if ((s->x == 0 && s->y == 1) || (s->x == 1 && s->y == 0))
1273             s->pd = 2;
1274     }
1275
1276     if (s->size == 256)
1277         s->vectorscope = vectorscope8;
1278     else
1279         s->vectorscope = vectorscope16;
1280
1281     s->graticulef = none_graticule;
1282
1283     if (s->is_yuv && s->size == 256) {
1284         if (s->graticule == 1)
1285             s->graticulef = green_graticule;
1286         else if (s->graticule == 2)
1287             s->graticulef = color_graticule;
1288     } else if (s->is_yuv) {
1289         if (s->graticule == 1)
1290             s->graticulef = green_graticule16;
1291         else if (s->graticule == 2)
1292             s->graticulef = color_graticule16;
1293     }
1294
1295     s->bg_color[3] = s->bgopacity * (s->size - 1);
1296
1297     switch (inlink->format) {
1298     case AV_PIX_FMT_GBRP12:
1299     case AV_PIX_FMT_GBRP10:
1300     case AV_PIX_FMT_GBRP9:
1301     case AV_PIX_FMT_GBRAP:
1302     case AV_PIX_FMT_GBRP:
1303         s->bg_color[0] = 0;
1304         s->bg_color[1] = 0;
1305         s->bg_color[2] = 0;
1306         break;
1307     default:
1308         s->bg_color[0] = 0;
1309         s->bg_color[1] = s->size / 2 - 1;
1310         s->bg_color[2] = s->size / 2 - 1;
1311     }
1312
1313     s->hsub = desc->log2_chroma_w;
1314     s->vsub = desc->log2_chroma_h;
1315     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
1316     s->planeheight[0] = s->planeheight[3] = inlink->h;
1317     s->planewidth[1]  = s->planewidth[2]  = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
1318     s->planewidth[0]  = s->planewidth[3]  = inlink->w;
1319
1320     return 0;
1321 }
1322
1323 static av_cold void uninit(AVFilterContext *ctx)
1324 {
1325     VectorscopeContext *s = ctx->priv;
1326
1327     av_freep(&s->peak);
1328     av_freep(&s->peak_memory);
1329 }
1330
1331 static const AVFilterPad inputs[] = {
1332     {
1333         .name         = "default",
1334         .type         = AVMEDIA_TYPE_VIDEO,
1335         .filter_frame = filter_frame,
1336         .config_props = config_input,
1337     },
1338     { NULL }
1339 };
1340
1341 static const AVFilterPad outputs[] = {
1342     {
1343         .name         = "default",
1344         .type         = AVMEDIA_TYPE_VIDEO,
1345         .config_props = config_output,
1346     },
1347     { NULL }
1348 };
1349
1350 AVFilter ff_vf_vectorscope = {
1351     .name          = "vectorscope",
1352     .description   = NULL_IF_CONFIG_SMALL("Video vectorscope."),
1353     .priv_size     = sizeof(VectorscopeContext),
1354     .priv_class    = &vectorscope_class,
1355     .query_formats = query_formats,
1356     .uninit        = uninit,
1357     .inputs        = inputs,
1358     .outputs       = outputs,
1359 };