FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_colorkey.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 28 34 82.4%
Functions: 4 7 57.1%
Branches: 9 34 26.5%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2015 Timo Rothenpieler <timo@rothenpieler.org>
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 "config_components.h"
22
23 #include "libavutil/opt.h"
24 #include "libavutil/pixdesc.h"
25 #include "avfilter.h"
26 #include "drawutils.h"
27 #include "filters.h"
28
29 typedef struct ColorkeyContext {
30 const AVClass *class;
31
32 /* color offsets rgba */
33 uint8_t co[4];
34
35 uint8_t colorkey_rgba[4];
36 float similarity;
37 float blend;
38 double scale;
39 int depth;
40 int max;
41
42 int (*do_slice)(AVFilterContext *ctx, void *arg,
43 int jobnr, int nb_jobs);
44 } ColorkeyContext;
45
46 65536 static int do_colorkey_pixel(const uint8_t *colorkey_rgba, int r, int g, int b,
47 float similarity, float iblend, int max, double scale)
48 {
49 double dr, dg, db, diff;
50
51 65536 dr = r * scale - colorkey_rgba[0];
52 65536 dg = g * scale - colorkey_rgba[1];
53 65536 db = b * scale - colorkey_rgba[2];
54
55 65536 diff = sqrt((dr * dr + dg * dg + db * db) / (255.0 * 255.0 * 3.0));
56
57
1/2
✓ Branch 0 taken 65536 times.
✗ Branch 1 not taken.
65536 if (iblend < 10000.0) {
58 65536 return av_clipd((diff - similarity) * iblend, 0.0, 1.0) * max;
59 } else {
60 return (diff > similarity) ? max : 0;
61 }
62 }
63
64 #define COLORKEY_SLICE(name, type) \
65 static int do_colorkey_slice##name(AVFilterContext *avctx, \
66 void *arg, \
67 int jobnr, int nb_jobs) \
68 { \
69 AVFrame *frame = arg; \
70 const int slice_start = (frame->height * jobnr) / nb_jobs; \
71 const int slice_end = (frame->height * (jobnr + 1)) / nb_jobs; \
72 ColorkeyContext *ctx = avctx->priv; \
73 const float similarity = ctx->similarity; \
74 const float iblend = 1.f / ctx->blend; \
75 const uint8_t *colorkey_rgba = ctx->colorkey_rgba; \
76 const uint8_t *co = ctx->co; \
77 const double scale = ctx->scale; \
78 const int max = ctx->max; \
79 \
80 for (int y = slice_start; y < slice_end; y++) { \
81 type *dst = (type *)(frame->data[0] + y * frame->linesize[0]);\
82 \
83 for (int x = 0; x < frame->width; x++) { \
84 const int o = x * 4; \
85 \
86 dst[o + co[3]] = do_colorkey_pixel(colorkey_rgba, \
87 dst[o + co[0]], \
88 dst[o + co[1]], \
89 dst[o + co[2]], \
90 similarity, iblend, max, scale); \
91 } \
92 } \
93 \
94 return 0; \
95 }
96
97
4/4
✓ Branch 1 taken 65536 times.
✓ Branch 2 taken 256 times.
✓ Branch 3 taken 256 times.
✓ Branch 4 taken 9 times.
65801 COLORKEY_SLICE(8, uint8_t)
98 COLORKEY_SLICE(16, uint16_t)
99
100 #define COLORHOLD_SLICE(name, type, htype) \
101 static int do_colorhold_slice##name(AVFilterContext *avctx, void *arg, \
102 int jobnr, int nb_jobs) \
103 { \
104 AVFrame *frame = arg; \
105 const int slice_start = (frame->height * jobnr) / nb_jobs; \
106 const int slice_end = (frame->height * (jobnr + 1)) / nb_jobs; \
107 ColorkeyContext *ctx = avctx->priv; \
108 const int depth = ctx->depth; \
109 const int max = ctx->max; \
110 const int half = max / 2; \
111 const uint8_t *co = ctx->co; \
112 const uint8_t *colorkey_rgba = ctx->colorkey_rgba; \
113 const float similarity = ctx->similarity; \
114 const float iblend = 1.f / ctx->blend; \
115 const double scale = ctx->scale; \
116 \
117 for (int y = slice_start; y < slice_end; ++y) { \
118 type *dst = (type *)(frame->data[0] + y * frame->linesize[0]); \
119 \
120 for (int x = 0; x < frame->width; ++x) { \
121 int o, t, r, g, b; \
122 \
123 o = x * 4; \
124 r = dst[o + co[0]]; \
125 g = dst[o + co[1]]; \
126 b = dst[o + co[2]]; \
127 \
128 t = do_colorkey_pixel(colorkey_rgba, r, g, b, \
129 similarity, iblend, max, scale); \
130 \
131 if (t > 0) { \
132 htype a = (r + g + b) / 3; \
133 htype rt = max - t; \
134 \
135 dst[o + co[0]] = (a * t + r * rt + half) >> depth; \
136 dst[o + co[1]] = (a * t + g * rt + half) >> depth; \
137 dst[o + co[2]] = (a * t + b * rt + half) >> depth; \
138 } \
139 } \
140 } \
141 \
142 return 0; \
143 }
144
145 COLORHOLD_SLICE(8, uint8_t, int)
146 COLORHOLD_SLICE(16, uint16_t, int64_t)
147
148 1 static int filter_frame(AVFilterLink *link, AVFrame *frame)
149 {
150 1 AVFilterContext *avctx = link->dst;
151 1 ColorkeyContext *ctx = avctx->priv;
152 int res;
153
154
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (res = ff_filter_execute(avctx, ctx->do_slice, frame, NULL,
155
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 FFMIN(frame->height, ff_filter_get_nb_threads(avctx))))
156 return res;
157
158 1 return ff_filter_frame(avctx->outputs[0], frame);
159 }
160
161 1 static av_cold int config_output(AVFilterLink *outlink)
162 {
163 1 AVFilterContext *avctx = outlink->src;
164 1 ColorkeyContext *ctx = avctx->priv;
165 1 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->inputs[0]->format);
166
167 1 ctx->depth = desc->comp[0].depth;
168 1 ctx->max = (1 << ctx->depth) - 1;
169 1 ctx->scale = 255.0 / ctx->max;
170 1 outlink->w = avctx->inputs[0]->w;
171 1 outlink->h = avctx->inputs[0]->h;
172 1 outlink->time_base = avctx->inputs[0]->time_base;
173 1 ff_fill_rgba_map(ctx->co, outlink->format);
174
175
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!strcmp(avctx->filter->name, "colorkey")) {
176
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ctx->do_slice = ctx->max == 255 ? do_colorkey_slice8 : do_colorkey_slice16;
177 } else {
178 ctx->do_slice = ctx->max == 255 ? do_colorhold_slice8 : do_colorhold_slice16;
179 }
180
181 1 return 0;
182 }
183
184 static const enum AVPixelFormat pixel_fmts[] = {
185 AV_PIX_FMT_ARGB,
186 AV_PIX_FMT_RGBA,
187 AV_PIX_FMT_ABGR,
188 AV_PIX_FMT_BGRA,
189 AV_PIX_FMT_RGBA64,
190 AV_PIX_FMT_BGRA64,
191 AV_PIX_FMT_NONE
192 };
193
194 static const AVFilterPad colorkey_inputs[] = {
195 {
196 .name = "default",
197 .type = AVMEDIA_TYPE_VIDEO,
198 .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
199 .filter_frame = filter_frame,
200 },
201 };
202
203 static const AVFilterPad colorkey_outputs[] = {
204 {
205 .name = "default",
206 .type = AVMEDIA_TYPE_VIDEO,
207 .config_props = config_output,
208 },
209 };
210
211 #define OFFSET(x) offsetof(ColorkeyContext, x)
212 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
213
214 #if CONFIG_COLORKEY_FILTER
215
216 static const AVOption colorkey_options[] = {
217 { "color", "set the colorkey key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, 0, 0, FLAGS },
218 { "similarity", "set the colorkey similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.00001, 1.0, FLAGS },
219 { "blend", "set the colorkey key blend value", OFFSET(blend), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, 0.0, 1.0, FLAGS },
220 { NULL }
221 };
222
223 AVFILTER_DEFINE_CLASS(colorkey);
224
225 const AVFilter ff_vf_colorkey = {
226 .name = "colorkey",
227 .description = NULL_IF_CONFIG_SMALL("Turns a certain color into transparency. Operates on RGB colors."),
228 .priv_size = sizeof(ColorkeyContext),
229 .priv_class = &colorkey_class,
230 FILTER_INPUTS(colorkey_inputs),
231 FILTER_OUTPUTS(colorkey_outputs),
232 FILTER_PIXFMTS_ARRAY(pixel_fmts),
233 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
234 .process_command = ff_filter_process_command,
235 };
236
237 #endif /* CONFIG_COLORKEY_FILTER */
238 #if CONFIG_COLORHOLD_FILTER
239
240 static const AVOption colorhold_options[] = {
241 { "color", "set the colorhold key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, 0, 0, FLAGS },
242 { "similarity", "set the colorhold similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.00001, 1.0, FLAGS },
243 { "blend", "set the colorhold blend value", OFFSET(blend), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, 0.0, 1.0, FLAGS },
244 { NULL }
245 };
246
247 AVFILTER_DEFINE_CLASS(colorhold);
248
249 const AVFilter ff_vf_colorhold = {
250 .name = "colorhold",
251 .description = NULL_IF_CONFIG_SMALL("Turns a certain color range into gray. Operates on RGB colors."),
252 .priv_size = sizeof(ColorkeyContext),
253 .priv_class = &colorhold_class,
254 FILTER_INPUTS(colorkey_inputs),
255 FILTER_OUTPUTS(colorkey_outputs),
256 FILTER_PIXFMTS_ARRAY(pixel_fmts),
257 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
258 .process_command = ff_filter_process_command,
259 };
260
261 #endif /* CONFIG_COLORHOLD_FILTER */
262