2 * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
3 * Copyright (C) 2006 Mindfruit Bv.
4 * Author: Sjoerd Simons <sjoerd@luon.net>
5 * Author: Alex Ugarte <alexugarte@gmail.com>
6 * Copyright (C) 2009 Alex Ugarte <augarte@vicomtech.org>
7 * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
34 #include <gst/video/video.h>
36 #define BLEND(D,S,alpha) (((D) * (256 - (alpha)) + (S) * (alpha)) >> 8)
38 GST_DEBUG_CATEGORY_STATIC (gst_videomixer_blend_debug);
39 #define GST_CAT_DEFAULT gst_videomixer_blend_debug
41 /* Below are the implementations of everything */
43 /* A32 is for AYUV, ARGB and BGRA */
44 #define BLEND_A32(name, method, LOOP) \
46 method##_ ##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
47 gdouble src_alpha, GstVideoFrame * destframe) \
50 gint src_stride, dest_stride; \
51 gint dest_width, dest_height; \
53 gint src_width, src_height; \
55 src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
56 src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
57 src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \
58 src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
59 dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \
60 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
61 dest_width = GST_VIDEO_FRAME_COMP_WIDTH (destframe, 0); \
62 dest_height = GST_VIDEO_FRAME_COMP_HEIGHT (destframe, 0); \
64 s_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
66 /* If it's completely transparent... we just return */ \
67 if (G_UNLIKELY (s_alpha == 0)) \
70 /* adjust src pointers for negative sizes */ \
77 src += -ypos * src_stride; \
78 src_height -= -ypos; \
81 /* adjust width/height if the src is bigger than dest */ \
82 if (xpos + src_width > dest_width) { \
83 src_width = dest_width - xpos; \
85 if (ypos + src_height > dest_height) { \
86 src_height = dest_height - ypos; \
89 dest = dest + 4 * xpos + (ypos * dest_stride); \
91 LOOP (dest, src, src_height, src_width, src_stride, dest_stride, s_alpha); \
94 #define BLEND_A32_LOOP(name, method) \
96 _##method##_loop_##name (guint8 * dest, const guint8 * src, gint src_height, \
97 gint src_width, gint src_stride, gint dest_stride, guint s_alpha) \
99 s_alpha = MIN (255, s_alpha); \
100 video_mixer_orc_##method##_##name (dest, dest_stride, src, src_stride, \
101 s_alpha, src_width, src_height); \
104 BLEND_A32_LOOP (argb, blend);
105 BLEND_A32_LOOP (bgra, blend);
106 BLEND_A32_LOOP (argb, overlay);
107 BLEND_A32_LOOP (bgra, overlay);
109 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
110 BLEND_A32 (argb, blend, _blend_loop_argb);
111 BLEND_A32 (bgra, blend, _blend_loop_bgra);
112 BLEND_A32 (argb, overlay, _overlay_loop_argb);
113 BLEND_A32 (bgra, overlay, _overlay_loop_bgra);
115 BLEND_A32 (argb, blend, _blend_loop_bgra);
116 BLEND_A32 (bgra, blend, _blend_loop_argb);
117 BLEND_A32 (argb, overlay, _overlay_loop_bgra);
118 BLEND_A32 (bgra, overlay, _overlay_loop_argb);
121 #define A32_CHECKER_C(name, RGB, A, C1, C2, C3) \
123 fill_checker_##name##_c (GstVideoFrame * frame) \
127 static const gint tab[] = { 80, 160, 80, 160 }; \
128 gint width, height; \
131 dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
132 width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
133 height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
136 for (i = 0; i < height; i++) { \
137 for (j = 0; j < width; j++) { \
139 dest[C1] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
146 for (i = 0; i < height; i++) { \
147 for (j = 0; j < width; j++) { \
148 val = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
159 A32_CHECKER_C (argb, TRUE, 0, 1, 2, 3);
160 A32_CHECKER_C (bgra, TRUE, 3, 2, 1, 0);
161 A32_CHECKER_C (ayuv, FALSE, 0, 1, 2, 3);
163 #define YUV_TO_R(Y,U,V) (CLAMP (1.164 * (Y - 16) + 1.596 * (V - 128), 0, 255))
164 #define YUV_TO_G(Y,U,V) (CLAMP (1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128), 0, 255))
165 #define YUV_TO_B(Y,U,V) (CLAMP (1.164 * (Y - 16) + 2.018 * (U - 128), 0, 255))
167 #define A32_COLOR(name, RGB, A, C1, C2, C3) \
169 fill_color_##name (GstVideoFrame * frame, gint Y, gint U, gint V) \
173 gint width, height; \
176 dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
177 width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
178 height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
181 c1 = YUV_TO_R (Y, U, V); \
182 c2 = YUV_TO_G (Y, U, V); \
183 c3 = YUV_TO_B (Y, U, V); \
189 val = GUINT32_FROM_BE ((0xff << A) | (c1 << C1) | (c2 << C2) | (c3 << C3)); \
191 video_mixer_orc_splat_u32 ((guint32 *) dest, val, height * width); \
194 A32_COLOR (argb, TRUE, 24, 16, 8, 0);
195 A32_COLOR (bgra, TRUE, 0, 8, 16, 24);
196 A32_COLOR (abgr, TRUE, 24, 0, 8, 16);
197 A32_COLOR (rgba, TRUE, 0, 24, 16, 8);
198 A32_COLOR (ayuv, FALSE, 24, 16, 8, 0);
200 /* Y444, Y42B, I420, YV12, Y41B */
201 #define PLANAR_YUV_BLEND(format_name,format_enum,x_round,y_round,MEMCPY,BLENDLOOP) \
203 _blend_##format_name (const guint8 * src, guint8 * dest, \
204 gint src_stride, gint dest_stride, gint src_width, gint src_height, \
210 /* If it's completely transparent... we just return */ \
211 if (G_UNLIKELY (src_alpha == 0.0)) { \
212 GST_INFO ("Fast copy (alpha == 0.0)"); \
216 /* If it's completely opaque, we do a fast copy */ \
217 if (G_UNLIKELY (src_alpha == 1.0)) { \
218 GST_INFO ("Fast copy (alpha == 1.0)"); \
219 for (i = 0; i < src_height; i++) { \
220 MEMCPY (dest, src, src_width); \
222 dest += dest_stride; \
227 b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
229 BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width, src_height); \
233 blend_##format_name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
234 gdouble src_alpha, GstVideoFrame * destframe) \
236 const guint8 *b_src; \
242 gint src_comp_rowstride, dest_comp_rowstride; \
243 gint src_comp_height; \
244 gint src_comp_width; \
245 gint comp_ypos, comp_xpos; \
246 gint comp_yoffset, comp_xoffset; \
247 gint dest_width, dest_height; \
248 const GstVideoFormatInfo *info; \
249 gint src_width, src_height; \
251 src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
252 src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
254 info = srcframe->info.finfo; \
255 dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
256 dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
258 xpos = x_round (xpos); \
259 ypos = y_round (ypos); \
261 b_src_width = src_width; \
262 b_src_height = src_height; \
264 /* adjust src pointers for negative sizes */ \
267 b_src_width -= -xpos; \
272 b_src_height -= -ypos; \
275 /* If x or y offset are larger then the source it's outside of the picture */ \
276 if (xoffset > src_width || yoffset > src_height) { \
280 /* adjust width/height if the src is bigger than dest */ \
281 if (xpos + src_width > dest_width) { \
282 b_src_width = dest_width - xpos; \
284 if (ypos + src_height > dest_height) { \
285 b_src_height = dest_height - ypos; \
287 if (b_src_width < 0 || b_src_height < 0) { \
291 /* First mix Y, then U, then V */ \
292 b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 0); \
293 b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 0); \
294 src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
295 dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
296 src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 0, b_src_width); \
297 src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, b_src_height); \
298 comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xpos); \
299 comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, ypos); \
300 comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xoffset); \
301 comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, yoffset); \
302 _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
303 b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
304 src_comp_rowstride, \
305 dest_comp_rowstride, src_comp_width, src_comp_height, \
308 b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 1); \
309 b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 1); \
310 src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 1); \
311 dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 1); \
312 src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 1, b_src_width); \
313 src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, b_src_height); \
314 comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xpos); \
315 comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, ypos); \
316 comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xoffset); \
317 comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, yoffset); \
318 _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
319 b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
320 src_comp_rowstride, \
321 dest_comp_rowstride, src_comp_width, src_comp_height, \
324 b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 2); \
325 b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 2); \
326 src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 2); \
327 dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 2); \
328 src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 2, b_src_width); \
329 src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 2, b_src_height); \
330 comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 2, xpos); \
331 comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 2, ypos); \
332 comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 2, xoffset); \
333 comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 2, yoffset); \
334 _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
335 b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
336 src_comp_rowstride, \
337 dest_comp_rowstride, src_comp_width, src_comp_height, \
341 #define PLANAR_YUV_FILL_CHECKER(format_name, format_enum, MEMSET) \
343 fill_checker_##format_name (GstVideoFrame * frame) \
346 static const int tab[] = { 80, 160, 80, 160 }; \
348 gint comp_width, comp_height; \
351 p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
352 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
353 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
354 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
356 for (i = 0; i < comp_height; i++) { \
357 for (j = 0; j < comp_width; j++) { \
358 *p++ = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
360 p += rowstride - comp_width; \
363 p = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \
364 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
365 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \
366 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
368 for (i = 0; i < comp_height; i++) { \
369 MEMSET (p, 0x80, comp_width); \
373 p = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \
374 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 2); \
375 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 2); \
376 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); \
378 for (i = 0; i < comp_height; i++) { \
379 MEMSET (p, 0x80, comp_width); \
384 #define PLANAR_YUV_FILL_COLOR(format_name,format_enum,MEMSET) \
386 fill_color_##format_name (GstVideoFrame * frame, \
387 gint colY, gint colU, gint colV) \
390 gint comp_width, comp_height; \
394 p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
395 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
396 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
397 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
399 for (i = 0; i < comp_height; i++) { \
400 MEMSET (p, colY, comp_width); \
404 p = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \
405 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
406 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \
407 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
409 for (i = 0; i < comp_height; i++) { \
410 MEMSET (p, colU, comp_width); \
414 p = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \
415 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 2); \
416 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 2); \
417 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); \
419 for (i = 0; i < comp_height; i++) { \
420 MEMSET (p, colV, comp_width); \
425 #define GST_ROUND_UP_1(x) (x)
427 PLANAR_YUV_BLEND (i420, GST_VIDEO_FORMAT_I420, GST_ROUND_UP_2,
428 GST_ROUND_UP_2, memcpy, video_mixer_orc_blend_u8);
429 PLANAR_YUV_FILL_CHECKER (i420, GST_VIDEO_FORMAT_I420, memset);
430 PLANAR_YUV_FILL_COLOR (i420, GST_VIDEO_FORMAT_I420, memset);
431 PLANAR_YUV_FILL_COLOR (yv12, GST_VIDEO_FORMAT_YV12, memset);
432 PLANAR_YUV_BLEND (y444, GST_VIDEO_FORMAT_Y444, GST_ROUND_UP_1,
433 GST_ROUND_UP_1, memcpy, video_mixer_orc_blend_u8);
434 PLANAR_YUV_FILL_CHECKER (y444, GST_VIDEO_FORMAT_Y444, memset);
435 PLANAR_YUV_FILL_COLOR (y444, GST_VIDEO_FORMAT_Y444, memset);
436 PLANAR_YUV_BLEND (y42b, GST_VIDEO_FORMAT_Y42B, GST_ROUND_UP_2,
437 GST_ROUND_UP_1, memcpy, video_mixer_orc_blend_u8);
438 PLANAR_YUV_FILL_CHECKER (y42b, GST_VIDEO_FORMAT_Y42B, memset);
439 PLANAR_YUV_FILL_COLOR (y42b, GST_VIDEO_FORMAT_Y42B, memset);
440 PLANAR_YUV_BLEND (y41b, GST_VIDEO_FORMAT_Y41B, GST_ROUND_UP_4,
441 GST_ROUND_UP_1, memcpy, video_mixer_orc_blend_u8);
442 PLANAR_YUV_FILL_CHECKER (y41b, GST_VIDEO_FORMAT_Y41B, memset);
443 PLANAR_YUV_FILL_COLOR (y41b, GST_VIDEO_FORMAT_Y41B, memset);
446 #define NV_YUV_BLEND(format_name,first_component,MEMCPY,BLENDLOOP) \
448 _blend_##format_name (const guint8 * src, guint8 * dest, \
449 gint src_stride, gint dest_stride, gint src_width, gint src_height, \
455 /* If it's completely transparent... we just return */ \
456 if (G_UNLIKELY (src_alpha == 0.0)) { \
457 GST_INFO ("Fast copy (alpha == 0.0)"); \
461 /* If it's completely opaque, we do a fast copy */ \
462 if (G_UNLIKELY (src_alpha == 1.0)) { \
463 GST_INFO ("Fast copy (alpha == 1.0)"); \
464 for (i = 0; i < src_height; i++) { \
465 MEMCPY (dest, src, src_width); \
467 dest += dest_stride; \
472 b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
474 BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width, src_height); \
478 blend_##format_name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
479 gdouble src_alpha, GstVideoFrame * destframe) \
481 const guint8 *b_src; \
487 gint src_comp_rowstride, dest_comp_rowstride; \
488 gint src_comp_height; \
489 gint src_comp_width; \
490 gint comp_ypos, comp_xpos; \
491 gint comp_yoffset, comp_xoffset; \
492 gint dest_width, dest_height; \
493 const GstVideoFormatInfo *info; \
494 gint src_width, src_height; \
496 src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
497 src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
499 info = srcframe->info.finfo; \
500 dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
501 dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
503 xpos = GST_ROUND_UP_2 (xpos); \
504 ypos = GST_ROUND_UP_2 (ypos); \
506 b_src_width = src_width; \
507 b_src_height = src_height; \
509 /* adjust src pointers for negative sizes */ \
512 b_src_width -= -xpos; \
517 b_src_height -= -ypos; \
520 /* If x or y offset are larger then the source it's outside of the picture */ \
521 if (xoffset > src_width || yoffset > src_height) { \
525 /* adjust width/height if the src is bigger than dest */ \
526 if (xpos + src_width > dest_width) { \
527 b_src_width = dest_width - xpos; \
529 if (ypos + src_height > dest_height) { \
530 b_src_height = dest_height - ypos; \
532 if (b_src_width < 0 || b_src_height < 0) { \
536 /* First mix Y, then UV */ \
537 b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 0); \
538 b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 0); \
539 src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
540 dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
541 src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 0, b_src_width); \
542 src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, b_src_height); \
543 comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xpos); \
544 comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, ypos); \
545 comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xoffset); \
546 comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, yoffset); \
547 _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
548 b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
549 src_comp_rowstride, \
550 dest_comp_rowstride, src_comp_width, src_comp_height, \
553 b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, first_component); \
554 b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, first_component); \
555 src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 1); \
556 dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 1); \
557 src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 1, b_src_width); \
558 src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, b_src_height); \
559 comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xpos); \
560 comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, ypos); \
561 comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xoffset); \
562 comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, yoffset); \
563 _blend_##format_name (b_src + comp_xoffset * 2 + comp_yoffset * src_comp_rowstride, \
564 b_dest + comp_xpos * 2 + comp_ypos * dest_comp_rowstride, \
565 src_comp_rowstride, \
566 dest_comp_rowstride, 2 * src_comp_width, src_comp_height, \
570 #define NV_YUV_FILL_CHECKER(format_name, first_component, MEMSET) \
572 fill_checker_##format_name (GstVideoFrame * frame) \
575 static const int tab[] = { 80, 160, 80, 160 }; \
577 gint comp_width, comp_height; \
580 p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
581 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
582 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
583 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
585 for (i = 0; i < comp_height; i++) { \
586 for (j = 0; j < comp_width; j++) { \
587 *p++ = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
589 p += rowstride - comp_width; \
592 p = GST_VIDEO_FRAME_COMP_DATA (frame, first_component); \
593 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
594 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \
595 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
597 for (i = 0; i < comp_height; i++) { \
598 MEMSET (p, 0x80, comp_width * 2); \
603 #define NV_YUV_FILL_COLOR(format_name,MEMSET) \
605 fill_color_##format_name (GstVideoFrame * frame, \
606 gint colY, gint colU, gint colV) \
609 gint comp_width, comp_height; \
613 y = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
614 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
615 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
616 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
618 for (i = 0; i < comp_height; i++) { \
619 MEMSET (y, colY, comp_width); \
623 u = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \
624 v = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \
625 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
626 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \
627 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
629 for (i = 0; i < comp_height; i++) { \
630 for (j = 0; j < comp_width; j++) { \
639 NV_YUV_BLEND (nv12, 1, memcpy, video_mixer_orc_blend_u8);
640 NV_YUV_FILL_CHECKER (nv12, 1, memset);
641 NV_YUV_FILL_COLOR (nv12, memset);
642 NV_YUV_BLEND (nv21, 2, memcpy, video_mixer_orc_blend_u8);
643 NV_YUV_FILL_CHECKER (nv21, 2, memset);
645 /* RGB, BGR, xRGB, xBGR, RGBx, BGRx */
647 #define RGB_BLEND(name, bpp, MEMCPY, BLENDLOOP) \
649 blend_##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
650 gdouble src_alpha, GstVideoFrame * destframe) \
654 gint src_stride, dest_stride; \
655 gint dest_width, dest_height; \
656 guint8 *dest, *src; \
657 gint src_width, src_height; \
659 src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
660 src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
662 src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \
663 dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \
665 dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
666 dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
668 src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
669 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
671 b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
673 /* adjust src pointers for negative sizes */ \
675 src += -xpos * bpp; \
676 src_width -= -xpos; \
680 src += -ypos * src_stride; \
681 src_height -= -ypos; \
684 /* adjust width/height if the src is bigger than dest */ \
685 if (xpos + src_width > dest_width) { \
686 src_width = dest_width - xpos; \
688 if (ypos + src_height > dest_height) { \
689 src_height = dest_height - ypos; \
692 dest = dest + bpp * xpos + (ypos * dest_stride); \
693 /* If it's completely transparent... we just return */ \
694 if (G_UNLIKELY (src_alpha == 0.0)) { \
695 GST_INFO ("Fast copy (alpha == 0.0)"); \
699 /* If it's completely opaque, we do a fast copy */ \
700 if (G_UNLIKELY (src_alpha == 1.0)) { \
701 GST_INFO ("Fast copy (alpha == 1.0)"); \
702 for (i = 0; i < src_height; i++) { \
703 MEMCPY (dest, src, bpp * src_width); \
705 dest += dest_stride; \
710 BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width * bpp, src_height); \
713 #define RGB_FILL_CHECKER_C(name, bpp, r, g, b) \
715 fill_checker_##name##_c (GstVideoFrame * frame) \
718 static const int tab[] = { 80, 160, 80, 160 }; \
719 gint stride, dest_add, width, height; \
722 width = GST_VIDEO_FRAME_WIDTH (frame); \
723 height = GST_VIDEO_FRAME_HEIGHT (frame); \
724 dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
725 stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
726 dest_add = stride - width * bpp; \
728 for (i = 0; i < height; i++) { \
729 for (j = 0; j < width; j++) { \
730 dest[r] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; /* red */ \
731 dest[g] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; /* green */ \
732 dest[b] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; /* blue */ \
739 #define RGB_FILL_COLOR(name, bpp, MEMSET_RGB) \
741 fill_color_##name (GstVideoFrame * frame, \
742 gint colY, gint colU, gint colV) \
744 gint red, green, blue; \
747 gint width, height; \
750 width = GST_VIDEO_FRAME_WIDTH (frame); \
751 height = GST_VIDEO_FRAME_HEIGHT (frame); \
752 dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
753 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
755 red = YUV_TO_R (colY, colU, colV); \
756 green = YUV_TO_G (colY, colU, colV); \
757 blue = YUV_TO_B (colY, colU, colV); \
759 for (i = 0; i < height; i++) { \
760 MEMSET_RGB (dest, red, green, blue, width); \
761 dest += dest_stride; \
765 #define MEMSET_RGB_C(name, r, g, b) \
767 _memset_##name##_c (guint8* dest, gint red, gint green, gint blue, gint width) { \
770 for (j = 0; j < width; j++) { \
778 #define MEMSET_XRGB(name, r, g, b) \
780 _memset_##name (guint8* dest, gint red, gint green, gint blue, gint width) { \
783 val = GUINT32_FROM_BE ((red << r) | (green << g) | (blue << b)); \
784 video_mixer_orc_splat_u32 ((guint32 *) dest, val, width); \
787 #define _orc_memcpy_u32(dest,src,len) video_mixer_orc_memcpy_u32((guint32 *) dest, (const guint32 *) src, len/4)
789 RGB_BLEND (rgb, 3, memcpy, video_mixer_orc_blend_u8);
790 RGB_FILL_CHECKER_C (rgb, 3, 0, 1, 2);
791 MEMSET_RGB_C (rgb, 0, 1, 2);
792 RGB_FILL_COLOR (rgb_c, 3, _memset_rgb_c);
794 MEMSET_RGB_C (bgr, 2, 1, 0);
795 RGB_FILL_COLOR (bgr_c, 3, _memset_bgr_c);
797 RGB_BLEND (xrgb, 4, _orc_memcpy_u32, video_mixer_orc_blend_u8);
798 RGB_FILL_CHECKER_C (xrgb, 4, 1, 2, 3);
799 MEMSET_XRGB (xrgb, 24, 16, 0);
800 RGB_FILL_COLOR (xrgb, 4, _memset_xrgb);
802 MEMSET_XRGB (xbgr, 0, 16, 24);
803 RGB_FILL_COLOR (xbgr, 4, _memset_xbgr);
805 MEMSET_XRGB (rgbx, 24, 16, 8);
806 RGB_FILL_COLOR (rgbx, 4, _memset_rgbx);
808 MEMSET_XRGB (bgrx, 8, 16, 24);
809 RGB_FILL_COLOR (bgrx, 4, _memset_bgrx);
811 /* YUY2, YVYU, UYVY */
813 #define PACKED_422_BLEND(name, MEMCPY, BLENDLOOP) \
815 blend_##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
816 gdouble src_alpha, GstVideoFrame * destframe) \
820 gint src_stride, dest_stride; \
821 gint dest_width, dest_height; \
822 guint8 *src, *dest; \
823 gint src_width, src_height; \
825 src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
826 src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
828 dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
829 dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
831 src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \
832 dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \
834 src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
835 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
837 b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
839 xpos = GST_ROUND_UP_2 (xpos); \
841 /* adjust src pointers for negative sizes */ \
844 src_width -= -xpos; \
848 src += -ypos * src_stride; \
849 src_height -= -ypos; \
853 /* adjust width/height if the src is bigger than dest */ \
854 if (xpos + src_width > dest_width) { \
855 src_width = dest_width - xpos; \
857 if (ypos + src_height > dest_height) { \
858 src_height = dest_height - ypos; \
861 dest = dest + 2 * xpos + (ypos * dest_stride); \
862 /* If it's completely transparent... we just return */ \
863 if (G_UNLIKELY (src_alpha == 0.0)) { \
864 GST_INFO ("Fast copy (alpha == 0.0)"); \
868 /* If it's completely opaque, we do a fast copy */ \
869 if (G_UNLIKELY (src_alpha == 1.0)) { \
870 GST_INFO ("Fast copy (alpha == 1.0)"); \
871 for (i = 0; i < src_height; i++) { \
872 MEMCPY (dest, src, 2 * src_width); \
874 dest += dest_stride; \
879 BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, 2 * src_width, src_height); \
882 #define PACKED_422_FILL_CHECKER_C(name, Y1, U, Y2, V) \
884 fill_checker_##name##_c (GstVideoFrame * frame) \
887 static const int tab[] = { 80, 160, 80, 160 }; \
889 gint width, height; \
892 width = GST_VIDEO_FRAME_WIDTH (frame); \
893 width = GST_ROUND_UP_2 (width); \
894 height = GST_VIDEO_FRAME_HEIGHT (frame); \
895 dest = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
896 dest_add = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0) - width * 2; \
899 for (i = 0; i < height; i++) { \
900 for (j = 0; j < width; j++) { \
901 dest[Y1] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
902 dest[Y2] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
911 #define PACKED_422_FILL_COLOR(name, Y1, U, Y2, V) \
913 fill_color_##name (GstVideoFrame * frame, \
914 gint colY, gint colU, gint colV) \
919 gint width, height; \
922 width = GST_VIDEO_FRAME_WIDTH (frame); \
923 width = GST_ROUND_UP_2 (width); \
924 height = GST_VIDEO_FRAME_HEIGHT (frame); \
925 dest = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
926 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
929 val = GUINT32_FROM_BE ((colY << Y1) | (colY << Y2) | (colU << U) | (colV << V)); \
931 for (i = 0; i < height; i++) { \
932 video_mixer_orc_splat_u32 ((guint32 *) dest, val, width); \
933 dest += dest_stride; \
937 PACKED_422_BLEND (yuy2, memcpy, video_mixer_orc_blend_u8);
938 PACKED_422_FILL_CHECKER_C (yuy2, 0, 1, 2, 3);
939 PACKED_422_FILL_CHECKER_C (uyvy, 1, 0, 3, 2);
940 PACKED_422_FILL_COLOR (yuy2, 24, 16, 8, 0);
941 PACKED_422_FILL_COLOR (yvyu, 24, 0, 8, 16);
942 PACKED_422_FILL_COLOR (uyvy, 16, 24, 0, 8);
945 BlendFunction gst_video_mixer_blend_argb;
946 BlendFunction gst_video_mixer_blend_bgra;
947 BlendFunction gst_video_mixer_overlay_argb;
948 BlendFunction gst_video_mixer_overlay_bgra;
949 /* AYUV/ABGR is equal to ARGB, RGBA is equal to BGRA */
950 BlendFunction gst_video_mixer_blend_y444;
951 BlendFunction gst_video_mixer_blend_y42b;
952 BlendFunction gst_video_mixer_blend_i420;
953 /* I420 is equal to YV12 */
954 BlendFunction gst_video_mixer_blend_nv12;
955 BlendFunction gst_video_mixer_blend_nv21;
956 BlendFunction gst_video_mixer_blend_y41b;
957 BlendFunction gst_video_mixer_blend_rgb;
958 /* BGR is equal to RGB */
959 BlendFunction gst_video_mixer_blend_rgbx;
960 /* BGRx, xRGB, xBGR are equal to RGBx */
961 BlendFunction gst_video_mixer_blend_yuy2;
962 /* YVYU and UYVY are equal to YUY2 */
964 FillCheckerFunction gst_video_mixer_fill_checker_argb;
965 FillCheckerFunction gst_video_mixer_fill_checker_bgra;
966 /* ABGR is equal to ARGB, RGBA is equal to BGRA */
967 FillCheckerFunction gst_video_mixer_fill_checker_ayuv;
968 FillCheckerFunction gst_video_mixer_fill_checker_y444;
969 FillCheckerFunction gst_video_mixer_fill_checker_y42b;
970 FillCheckerFunction gst_video_mixer_fill_checker_i420;
971 /* I420 is equal to YV12 */
972 FillCheckerFunction gst_video_mixer_fill_checker_nv12;
973 FillCheckerFunction gst_video_mixer_fill_checker_nv21;
974 FillCheckerFunction gst_video_mixer_fill_checker_y41b;
975 FillCheckerFunction gst_video_mixer_fill_checker_rgb;
976 /* BGR is equal to RGB */
977 FillCheckerFunction gst_video_mixer_fill_checker_xrgb;
978 /* BGRx, xRGB, xBGR are equal to RGBx */
979 FillCheckerFunction gst_video_mixer_fill_checker_yuy2;
980 /* YVYU is equal to YUY2 */
981 FillCheckerFunction gst_video_mixer_fill_checker_uyvy;
983 FillColorFunction gst_video_mixer_fill_color_argb;
984 FillColorFunction gst_video_mixer_fill_color_bgra;
985 FillColorFunction gst_video_mixer_fill_color_abgr;
986 FillColorFunction gst_video_mixer_fill_color_rgba;
987 FillColorFunction gst_video_mixer_fill_color_ayuv;
988 FillColorFunction gst_video_mixer_fill_color_y444;
989 FillColorFunction gst_video_mixer_fill_color_y42b;
990 FillColorFunction gst_video_mixer_fill_color_i420;
991 FillColorFunction gst_video_mixer_fill_color_yv12;
992 FillColorFunction gst_video_mixer_fill_color_nv12;
993 /* NV21 is equal to NV12 */
994 FillColorFunction gst_video_mixer_fill_color_y41b;
995 FillColorFunction gst_video_mixer_fill_color_rgb;
996 FillColorFunction gst_video_mixer_fill_color_bgr;
997 FillColorFunction gst_video_mixer_fill_color_xrgb;
998 FillColorFunction gst_video_mixer_fill_color_xbgr;
999 FillColorFunction gst_video_mixer_fill_color_rgbx;
1000 FillColorFunction gst_video_mixer_fill_color_bgrx;
1001 FillColorFunction gst_video_mixer_fill_color_yuy2;
1002 FillColorFunction gst_video_mixer_fill_color_yvyu;
1003 FillColorFunction gst_video_mixer_fill_color_uyvy;
1006 gst_video_mixer_init_blend (void)
1008 GST_DEBUG_CATEGORY_INIT (gst_videomixer_blend_debug, "videomixer_blend", 0,
1009 "video mixer blending functions");
1011 gst_video_mixer_blend_argb = blend_argb;
1012 gst_video_mixer_blend_bgra = blend_bgra;
1013 gst_video_mixer_overlay_argb = overlay_argb;
1014 gst_video_mixer_overlay_bgra = overlay_bgra;
1015 gst_video_mixer_blend_i420 = blend_i420;
1016 gst_video_mixer_blend_nv12 = blend_nv12;
1017 gst_video_mixer_blend_nv21 = blend_nv21;
1018 gst_video_mixer_blend_y444 = blend_y444;
1019 gst_video_mixer_blend_y42b = blend_y42b;
1020 gst_video_mixer_blend_y41b = blend_y41b;
1021 gst_video_mixer_blend_rgb = blend_rgb;
1022 gst_video_mixer_blend_xrgb = blend_xrgb;
1023 gst_video_mixer_blend_yuy2 = blend_yuy2;
1025 gst_video_mixer_fill_checker_argb = fill_checker_argb_c;
1026 gst_video_mixer_fill_checker_bgra = fill_checker_bgra_c;
1027 gst_video_mixer_fill_checker_ayuv = fill_checker_ayuv_c;
1028 gst_video_mixer_fill_checker_i420 = fill_checker_i420;
1029 gst_video_mixer_fill_checker_nv12 = fill_checker_nv12;
1030 gst_video_mixer_fill_checker_nv21 = fill_checker_nv21;
1031 gst_video_mixer_fill_checker_y444 = fill_checker_y444;
1032 gst_video_mixer_fill_checker_y42b = fill_checker_y42b;
1033 gst_video_mixer_fill_checker_y41b = fill_checker_y41b;
1034 gst_video_mixer_fill_checker_rgb = fill_checker_rgb_c;
1035 gst_video_mixer_fill_checker_xrgb = fill_checker_xrgb_c;
1036 gst_video_mixer_fill_checker_yuy2 = fill_checker_yuy2_c;
1037 gst_video_mixer_fill_checker_uyvy = fill_checker_uyvy_c;
1039 gst_video_mixer_fill_color_argb = fill_color_argb;
1040 gst_video_mixer_fill_color_bgra = fill_color_bgra;
1041 gst_video_mixer_fill_color_abgr = fill_color_abgr;
1042 gst_video_mixer_fill_color_rgba = fill_color_rgba;
1043 gst_video_mixer_fill_color_ayuv = fill_color_ayuv;
1044 gst_video_mixer_fill_color_i420 = fill_color_i420;
1045 gst_video_mixer_fill_color_yv12 = fill_color_yv12;
1046 gst_video_mixer_fill_color_nv12 = fill_color_nv12;
1047 gst_video_mixer_fill_color_y444 = fill_color_y444;
1048 gst_video_mixer_fill_color_y42b = fill_color_y42b;
1049 gst_video_mixer_fill_color_y41b = fill_color_y41b;
1050 gst_video_mixer_fill_color_rgb = fill_color_rgb_c;
1051 gst_video_mixer_fill_color_bgr = fill_color_bgr_c;
1052 gst_video_mixer_fill_color_xrgb = fill_color_xrgb;
1053 gst_video_mixer_fill_color_xbgr = fill_color_xbgr;
1054 gst_video_mixer_fill_color_rgbx = fill_color_rgbx;
1055 gst_video_mixer_fill_color_bgrx = fill_color_bgrx;
1056 gst_video_mixer_fill_color_yuy2 = fill_color_yuy2;
1057 gst_video_mixer_fill_color_yvyu = fill_color_yvyu;
1058 gst_video_mixer_fill_color_uyvy = fill_color_uyvy;