FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_negate.c
Date: 2024-04-19 17:50:32
Exec Total Coverage
Lines: 57 153 37.3%
Functions: 4 8 50.0%
Branches: 18 110 16.4%

Line Branch Exec Source
1 /*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include "libavutil/common.h"
20 #include "libavutil/imgutils.h"
21 #include "libavutil/opt.h"
22 #include "libavutil/pixdesc.h"
23 #include "avfilter.h"
24 #include "drawutils.h"
25 #include "internal.h"
26 #include "video.h"
27
28 #define COMP_R 0x01
29 #define COMP_G 0x02
30 #define COMP_B 0x04
31 #define COMP_A 0x08
32 #define COMP_Y 0x10
33 #define COMP_U 0x20
34 #define COMP_V 0x40
35
36 typedef struct ThreadData {
37 AVFrame *in;
38 AVFrame *out;
39 } ThreadData;
40
41 typedef struct NegateContext {
42 const AVClass *class;
43 int negate_alpha;
44 int max;
45 int requested_components;
46 int components;
47 int planes;
48 int step;
49 int nb_planes;
50 int linesize[4];
51 int width[4];
52 int height[4];
53 uint8_t rgba_map[4];
54
55 void (*negate)(const uint8_t *src, uint8_t *dst,
56 ptrdiff_t slinesize, ptrdiff_t dlinesize,
57 int w, int h, int max, int step,
58 int components);
59 } NegateContext;
60
61 #define OFFSET(x) offsetof(NegateContext, x)
62 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
63
64 static const AVOption negate_options[] = {
65 { "components", "set components to negate", OFFSET(requested_components), AV_OPT_TYPE_FLAGS, {.i64=0x77}, 1, 0xff, FLAGS, .unit = "flags"},
66 { "y", "set luma component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_Y}, 0, 0, FLAGS, .unit = "flags"},
67 { "u", "set u component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_U}, 0, 0, FLAGS, .unit = "flags"},
68 { "v", "set v component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_V}, 0, 0, FLAGS, .unit = "flags"},
69 { "r", "set red component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_R}, 0, 0, FLAGS, .unit = "flags"},
70 { "g", "set green component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_G}, 0, 0, FLAGS, .unit = "flags"},
71 { "b", "set blue component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_B}, 0, 0, FLAGS, .unit = "flags"},
72 { "a", "set alpha component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_A}, 0, 0, FLAGS, .unit = "flags"},
73 { "negate_alpha", NULL, OFFSET(negate_alpha), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
74 { NULL }
75 };
76
77 AVFILTER_DEFINE_CLASS(negate);
78
79 static const enum AVPixelFormat pix_fmts[] = {
80 AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P,
81 AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV440P,
82 AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P,
83 AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
84 AV_PIX_FMT_YUVJ440P,
85 AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV420P9,
86 AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV440P10,
87 AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV440P12,
88 AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,
89 AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV420P16,
90 AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA420P10,
91 AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA422P12,
92 AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA420P16,
93 AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA,
94 AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA,
95 AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
96 AV_PIX_FMT_RGB48, AV_PIX_FMT_RGBA64,
97 AV_PIX_FMT_BGR48, AV_PIX_FMT_BGRA64,
98 AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
99 AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
100 AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP14,
101 AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14,
102 AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP12,
103 AV_PIX_FMT_GBRAP16,
104 AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10,
105 AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
106 AV_PIX_FMT_NONE
107 };
108
109 1350 static void negate8(const uint8_t *src, uint8_t *dst,
110 ptrdiff_t slinesize, ptrdiff_t dlinesize,
111 int w, int h, int max, int step,
112 int components)
113 {
114
2/2
✓ Branch 0 taken 28800 times.
✓ Branch 1 taken 1350 times.
30150 for (int y = 0; y < h; y++) {
115
2/2
✓ Branch 0 taken 7603200 times.
✓ Branch 1 taken 28800 times.
7632000 for (int x = 0; x < w; x++)
116 7603200 dst[x] = 255 - src[x];
117
118 28800 dst += dlinesize;
119 28800 src += slinesize;
120 }
121 1350 }
122
123 static void negate_packed8(const uint8_t *ssrc, uint8_t *ddst,
124 ptrdiff_t slinesize, ptrdiff_t dlinesize,
125 int w, int h, int max, int step,
126 int components)
127 {
128 for (int y = 0; y < h; y++) {
129 const uint8_t *src = ssrc + y * slinesize;
130 uint8_t *dst = ddst + y * dlinesize;
131
132 for (int x = 0; x < w; x++) {
133 switch (step) {
134 case 4: dst[3] = components & 8 ? 255 - src[3] : src[3];
135 case 3: dst[2] = components & 4 ? 255 - src[2] : src[2];
136 case 2: dst[1] = components & 2 ? 255 - src[1] : src[1];
137 default: dst[0] = components & 1 ? 255 - src[0] : src[0];
138 }
139
140 src += step;
141 dst += step;
142 }
143 }
144 }
145
146 static void negate16(const uint8_t *ssrc, uint8_t *ddst,
147 ptrdiff_t slinesize, ptrdiff_t dlinesize,
148 int w, int h, int max, int step,
149 int components)
150 {
151 const uint16_t *src = (const uint16_t *)ssrc;
152 uint16_t *dst = (uint16_t *)ddst;
153
154 dlinesize /= 2;
155 slinesize /= 2;
156
157 for (int y = 0; y < h; y++) {
158 for (int x = 0; x < w; x++)
159 dst[x] = max - src[x];
160
161 dst += dlinesize;
162 src += slinesize;
163 }
164 }
165
166 static void negate_packed16(const uint8_t *ssrc, uint8_t *ddst,
167 ptrdiff_t slinesize, ptrdiff_t dlinesize,
168 int w, int h, int max, int step,
169 int components)
170 {
171 for (int y = 0; y < h; y++) {
172 const uint16_t *src = (const uint16_t *)(ssrc + y * slinesize);
173 uint16_t *dst = (uint16_t *)(ddst + y * dlinesize);
174
175 for (int x = 0; x < w; x++) {
176 switch (step) {
177 case 4: dst[3] = components & 8 ? max - src[3] : src[3];
178 case 3: dst[2] = components & 4 ? max - src[2] : src[2];
179 case 2: dst[1] = components & 2 ? max - src[1] : src[1];
180 default: dst[0] = components & 1 ? max - src[0] : src[0];
181 }
182
183 src += step;
184 dst += step;
185 }
186 }
187 }
188
189 1 static int config_input(AVFilterLink *inlink)
190 {
191 1 AVFilterContext *ctx = inlink->dst;
192 1 NegateContext *s = ctx->priv;
193 1 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
194 int depth, vsub, hsub, ret, is_packed;
195 int comp_avail;
196
197
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 s->planes = s->negate_alpha ? 0xF : 0x7;
198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 is_packed = !(desc->flags & AV_PIX_FMT_FLAG_PLANAR) &&
199 (desc->nb_components > 1);
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (s->requested_components != 0x77) {
201 comp_avail = ((desc->flags & AV_PIX_FMT_FLAG_RGB) ? COMP_R|COMP_G|COMP_B :
202 COMP_Y |
203 ((desc->nb_components > 2) ? COMP_U|COMP_V : 0)) |
204 ((desc->flags & AV_PIX_FMT_FLAG_ALPHA) ? COMP_A : 0);
205 if (s->requested_components & ~comp_avail) {
206 av_log(ctx, AV_LOG_ERROR, "Requested components not available.\n");
207 return AVERROR(EINVAL);
208 }
209
210 s->planes = 0;
211 if (!(desc->flags & AV_PIX_FMT_FLAG_RGB)) {
212 if (s->requested_components & COMP_Y)
213 s->planes |= 1;
214 if (s->requested_components & COMP_U)
215 s->planes |= 2;
216 if (s->requested_components & COMP_V)
217 s->planes |= 4;
218 if (s->requested_components & COMP_A)
219 s->planes |= 8;
220 } else {
221 if (s->requested_components & COMP_R)
222 s->planes |= 4;
223 if (s->requested_components & COMP_G)
224 s->planes |= 1;
225 if (s->requested_components & COMP_B)
226 s->planes |= 2;
227 if (s->requested_components & COMP_A)
228 s->planes |= 8;
229 }
230 }
231 1 s->nb_planes = av_pix_fmt_count_planes(inlink->format);
232
233 1 s->components = 0;
234
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (desc->flags & AV_PIX_FMT_FLAG_RGB) {
235 ff_fill_rgba_map(s->rgba_map, inlink->format);
236
237 if (s->requested_components & COMP_R)
238 s->components |= 1 << s->rgba_map[0];
239 if (s->requested_components & COMP_G)
240 s->components |= 1 << s->rgba_map[1];
241 if (s->requested_components & COMP_B)
242 s->components |= 1 << s->rgba_map[2];
243 if (s->requested_components & COMP_A)
244 s->components |= 1 << s->rgba_map[3];
245 }
246
247
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0)
248 return ret;
249
250 1 depth = desc->comp[0].depth;
251 1 hsub = desc->log2_chroma_w;
252 1 vsub = desc->log2_chroma_h;
253 1 s->height[1] = s->height[2] = AV_CEIL_RSHIFT(inlink->h, vsub);
254 1 s->height[0] = s->height[3] = inlink->h;
255 1 s->width[1] = s->width[2] = AV_CEIL_RSHIFT(inlink->w, hsub);
256 1 s->width[0] = s->width[3] = inlink->w;
257
258
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 s->negate = depth <= 8 ? negate8 : negate16;
259
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (is_packed) {
260 s->negate = depth <= 8 ? negate_packed8 : negate_packed16;
261 s->planes = 1;
262 }
263 1 s->max = (1 << depth) - 1;
264 1 s->step = av_get_bits_per_pixel(desc) >> 3;
265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (depth > 8)
266 s->step = s->step >> 1;
267
268 1 return 0;
269 }
270
271 450 static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
272 {
273 450 NegateContext *s = ctx->priv;
274 450 ThreadData *td = arg;
275 450 AVFrame *in = td->in;
276 450 AVFrame *out = td->out;
277
278
2/2
✓ Branch 0 taken 1350 times.
✓ Branch 1 taken 450 times.
1800 for (int p = 0; p < s->nb_planes; p++) {
279 1350 const int h = s->height[p];
280 1350 const int slice_start = (h * jobnr) / nb_jobs;
281 1350 const int slice_end = (h * (jobnr+1)) / nb_jobs;
282
283
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1350 times.
1350 if (!((1 << p) & s->planes)) {
284 if (out != in)
285 av_image_copy_plane(out->data[p] + slice_start * out->linesize[p],
286 out->linesize[p],
287 in->data[p] + slice_start * in->linesize[p],
288 in->linesize[p],
289 s->linesize[p], slice_end - slice_start);
290 continue;
291 }
292
293 1350 s->negate(in->data[p] + slice_start * in->linesize[p],
294 1350 out->data[p] + slice_start * out->linesize[p],
295 1350 in->linesize[p], out->linesize[p],
296 s->width[p], slice_end - slice_start,
297 s->max, s->step, s->components);
298 }
299
300 450 return 0;
301 }
302
303 50 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
304 {
305 50 AVFilterContext *ctx = inlink->dst;
306 50 NegateContext *s = ctx->priv;
307 50 AVFilterLink *outlink = ctx->outputs[0];
308 ThreadData td;
309 AVFrame *out;
310
311
1/2
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
50 if (av_frame_is_writable(in)) {
312 50 out = in;
313 } else {
314 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
315 if (!out) {
316 av_frame_free(&in);
317 return AVERROR(ENOMEM);
318 }
319 av_frame_copy_props(out, in);
320 }
321
322 50 td.out = out;
323 50 td.in = in;
324 50 ff_filter_execute(ctx, filter_slice, &td, NULL,
325
1/2
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
50 FFMIN(s->height[2], ff_filter_get_nb_threads(ctx)));
326
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (out != in)
327 av_frame_free(&in);
328
329 50 return ff_filter_frame(outlink, out);
330 }
331
332 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
333 char *res, int res_len, int flags)
334 {
335 NegateContext *s = ctx->priv;
336 int old_planes = s->planes;
337 int ret;
338
339 ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
340 if (ret < 0)
341 return ret;
342
343 ret = config_input(ctx->inputs[0]);
344 if (ret < 0)
345 s->planes = old_planes;
346 return ret;
347 }
348
349 static const AVFilterPad inputs[] = {
350 {
351 .name = "default",
352 .type = AVMEDIA_TYPE_VIDEO,
353 .filter_frame = filter_frame,
354 .config_props = config_input,
355 },
356 };
357
358 const AVFilter ff_vf_negate = {
359 .name = "negate",
360 .description = NULL_IF_CONFIG_SMALL("Negate input video."),
361 .priv_size = sizeof(NegateContext),
362 .priv_class = &negate_class,
363 FILTER_INPUTS(inputs),
364 FILTER_OUTPUTS(ff_video_default_filterpad),
365 FILTER_PIXFMTS_ARRAY(pix_fmts),
366 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
367 .process_command = process_command,
368 };
369