1 /*
2 * Copyright (c) 2013 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
31
34
63 };
64
65 static const char *
const var_names[] = {
"X",
"Y",
"W",
"H",
"SW",
"SH",
"T",
"N",
"A",
"B",
"TOP",
"BOTTOM",
NULL };
66 enum {
VAR_X,
VAR_Y,
VAR_W,
VAR_H,
VAR_SW,
VAR_SH,
VAR_T,
VAR_N,
VAR_A,
VAR_B,
VAR_TOP,
VAR_BOTTOM,
VAR_VARS_NB };
67
74 const uint8_t *bottom,
int bottom_linesize,
79
88
92 int hsub,
vsub;
///< chroma subsampling values
97
102
103 #define COMMON_OPTIONS \
104 { "c0_mode", "set component #0 blend mode", OFFSET(params[0].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},\
105 { "c1_mode", "set component #1 blend mode", OFFSET(params[1].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},\
106 { "c2_mode", "set component #2 blend mode", OFFSET(params[2].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},\
107 { "c3_mode", "set component #3 blend mode", OFFSET(params[3].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},\
108 { "all_mode", "set blend mode for all components", OFFSET(all_mode), AV_OPT_TYPE_INT, {.i64=-1},-1, BLEND_NB-1, FLAGS, "mode"},\
109 { "addition", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_ADDITION}, 0, 0, FLAGS, "mode" },\
110 { "and", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AND}, 0, 0, FLAGS, "mode" },\
111 { "average", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AVERAGE}, 0, 0, FLAGS, "mode" },\
112 { "burn", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_BURN}, 0, 0, FLAGS, "mode" },\
113 { "darken", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DARKEN}, 0, 0, FLAGS, "mode" },\
114 { "difference", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE}, 0, 0, FLAGS, "mode" },\
115 { "difference128", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE128}, 0, 0, FLAGS, "mode" },\
116 { "divide", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIVIDE}, 0, 0, FLAGS, "mode" },\
117 { "dodge", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DODGE}, 0, 0, FLAGS, "mode" },\
118 { "exclusion", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_EXCLUSION}, 0, 0, FLAGS, "mode" },\
119 { "hardlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDLIGHT}, 0, 0, FLAGS, "mode" },\
120 { "lighten", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_LIGHTEN}, 0, 0, FLAGS, "mode" },\
121 { "multiply", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_MULTIPLY}, 0, 0, FLAGS, "mode" },\
122 { "negation", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NEGATION}, 0, 0, FLAGS, "mode" },\
123 { "normal", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NORMAL}, 0, 0, FLAGS, "mode" },\
124 { "or", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OR}, 0, 0, FLAGS, "mode" },\
125 { "overlay", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OVERLAY}, 0, 0, FLAGS, "mode" },\
126 { "phoenix", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PHOENIX}, 0, 0, FLAGS, "mode" },\
127 { "pinlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PINLIGHT}, 0, 0, FLAGS, "mode" },\
128 { "reflect", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_REFLECT}, 0, 0, FLAGS, "mode" },\
129 { "screen", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SCREEN}, 0, 0, FLAGS, "mode" },\
130 { "softlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SOFTLIGHT}, 0, 0, FLAGS, "mode" },\
131 { "subtract", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SUBTRACT}, 0, 0, FLAGS, "mode" },\
132 { "vividlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_VIVIDLIGHT}, 0, 0, FLAGS, "mode" },\
133 { "xor", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_XOR}, 0, 0, FLAGS, "mode" },\
134 { "c0_expr", "set color component #0 expression", OFFSET(params[0].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
135 { "c1_expr", "set color component #1 expression", OFFSET(params[1].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
136 { "c2_expr", "set color component #2 expression", OFFSET(params[2].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
137 { "c3_expr", "set color component #3 expression", OFFSET(params[3].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
138 { "all_expr", "set expression for all color components", OFFSET(all_expr), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
139 { "c0_opacity", "set color component #0 opacity", OFFSET(params[0].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\
140 { "c1_opacity", "set color component #1 opacity", OFFSET(params[1].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\
141 { "c2_opacity", "set color component #2 opacity", OFFSET(params[2].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\
142 { "c3_opacity", "set color component #3 opacity", OFFSET(params[3].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\
143 { "all_opacity", "set opacity for all color components", OFFSET(all_opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}
144
145 #define OFFSET(x) offsetof(BlendContext, x)
146 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
147
150 {
"shortest",
"force termination when the shortest input terminates",
OFFSET(dinput.shortest),
AV_OPT_TYPE_INT, {.i64=0}, 0, 1,
FLAGS },
153 };
154
156
158 const uint8_t *bottom,
int bottom_linesize,
159 uint8_t *dst,
int dst_linesize,
162 {
164 }
165
166 #define DEFINE_BLEND(name, expr) \
167 static void blend_## name(const uint8_t *top, int top_linesize, \
168 const uint8_t *bottom, int bottom_linesize, \
169 uint8_t *dst, int dst_linesize, \
170 int width, int start, int end, \
171 FilterParams *param, double *values) \
172 { \
173 double opacity = param->opacity; \
174 int i, j; \
175 \
176 for (i = start; i < end; i++) { \
177 for (j = 0; j < width; j++) { \
178 dst[j] = top[j] + ((expr) - top[j]) * opacity; \
179 } \
180 dst += dst_linesize; \
181 top += top_linesize; \
182 bottom += bottom_linesize; \
183 } \
184 }
185
188
189 #define MULTIPLY(x, a, b) ((x) * (((a) * (b)) / 255))
190 #define SCREEN(x, a, b) (255 - (x) * ((255 - (a)) * (255 - (b)) / 255))
191 #define BURN(a, b) (((a) == 0) ? (a) : FFMAX(0, 255 - ((255 - (b)) << 8) / (a)))
192 #define DODGE(a, b) (((a) == 255) ? (a) : FFMIN(255, (((b) << 8) / (255 - (a)))))
193
203 DEFINE_BLEND(hardlight, (
B < 128) ? MULTIPLY(2,
B, A) : SCREEN(2, B, A))
209 DEFINE_BLEND(softlight, (A > 127) ? B + (255 - B) * (A - 127.5) / 127.5 * (0.5 -
FFABS(B - 127.5) / 255): B - B * ((127.5 - A) / 127.5) * (0.5 -
FFABS(B - 127.5)/255))
218
219 static
void blend_expr(const
uint8_t *top,
int top_linesize,
220 const
uint8_t *bottom,
int bottom_linesize,
221 uint8_t *dst,
int dst_linesize,
224 {
227
228 for (y = start; y <
end; y++) {
230 for (x = 0; x <
width; x++) {
235 }
236 dst += dst_linesize;
237 top += top_linesize;
238 bottom += bottom_linesize;
239 }
240 }
241
243 {
245 int slice_start = (td->
h * jobnr ) / nb_jobs;
246 int slice_end = (td->
h * (jobnr+1)) / nb_jobs;
251
258
265 td->
w, slice_start, slice_end, td->
param, &values[0]);
266 return 0;
267 }
268
271 {
276 int plane;
277
279 if (!dst_buf)
280 return top_buf;
282
283 for (plane = 0; plane < b->
nb_planes; plane++) {
284 int hsub = plane == 1 || plane == 2 ? b->
hsub : 0;
285 int vsub = plane == 1 || plane == 2 ? b->
vsub : 0;
289 ThreadData td = { .top = top_buf, .bottom = bottom_buf, .dst = dst_buf,
290 .w = outw, .h = outh, .param = param, .plane = plane,
291 .inlink = inlink };
292
294 }
295
298
299 return dst_buf;
300 }
301
303 {
306
308
311
316
317 switch (param->
mode) {
343 }
344
349 }
353 if (ret < 0)
355 param->
blend = blend_expr;
356 }
357 }
358
360 return 0;
361 }
362
364 {
370 };
371
373 return 0;
374 }
375
377 {
379 int i;
380
383
386 }
387
388 #if CONFIG_BLEND_FILTER
389
391 {
398
402 }
403 if (toplink->
w != bottomlink->
w ||
404 toplink->
h != bottomlink->
h ||
408 "(size %dx%d, SAR %d:%d) do not match the corresponding "
409 "second input link %s parameters (%dx%d, SAR %d:%d)\n",
417 }
418
419 outlink->
w = toplink->
w;
420 outlink->
h = toplink->
h;
424
428
431
432 return 0;
433 }
434
436 {
439 }
440
442 {
445 }
446
448 {
452 },{
453 .name = "bottom",
456 },
458 };
459
461 {
466 },
468 };
469
479 .priv_class = &blend_class,
481 };
482
483 #endif
484
485 #if CONFIG_TBLEND_FILTER
486
488 {
493
498
499 return 0;
500 }
501
503 {
506
512 }
514 return 0;
515 }
516
517 static const AVOption tblend_options[] = {
520 };
521
523
525 {
528 .filter_frame = tblend_filter_frame,
529 },
531 };
532
534 {
537 .config_props = tblend_config_output,
538 },
540 };
541
546 .priv_class = &tblend_class,
553 };
554
555 #endif