blob: 987bc66bd49a04a20e1fb52c497ae779283927fd
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, |
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, |
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, |
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 (av_frame_get_colorspace(in)) { |
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 | }; |
1360 |