FFmpeg coverage


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