FFmpeg coverage


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