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.
30 #include "videomixerorc.h"
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 if (src_height > 0 && src_width > 0) { \
90 dest = dest + 4 * xpos + (ypos * dest_stride); \
92 LOOP (dest, src, src_height, src_width, src_stride, dest_stride, s_alpha); \
96 #define BLEND_A32_LOOP(name, method) \
98 _##method##_loop_##name (guint8 * dest, const guint8 * src, gint src_height, \
99 gint src_width, gint src_stride, gint dest_stride, guint s_alpha) \
101 s_alpha = MIN (255, s_alpha); \
102 video_mixer_orc_##method##_##name (dest, dest_stride, src, src_stride, \
103 s_alpha, src_width, src_height); \
106 BLEND_A32_LOOP (argb, blend);
107 BLEND_A32_LOOP (bgra, blend);
108 BLEND_A32_LOOP (argb, overlay);
109 BLEND_A32_LOOP (bgra, overlay);
111 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
112 BLEND_A32 (argb, blend, _blend_loop_argb);
113 BLEND_A32 (bgra, blend, _blend_loop_bgra);
114 BLEND_A32 (argb, overlay, _overlay_loop_argb);
115 BLEND_A32 (bgra, overlay, _overlay_loop_bgra);
117 BLEND_A32 (argb, blend, _blend_loop_bgra);
118 BLEND_A32 (bgra, blend, _blend_loop_argb);
119 BLEND_A32 (argb, overlay, _overlay_loop_bgra);
120 BLEND_A32 (bgra, overlay, _overlay_loop_argb);
123 #define A32_CHECKER_C(name, RGB, A, C1, C2, C3) \
125 fill_checker_##name##_c (GstVideoFrame * frame) \
129 static const gint tab[] = { 80, 160, 80, 160 }; \
130 gint width, height; \
133 dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
134 width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
135 height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
138 for (i = 0; i < height; i++) { \
139 for (j = 0; j < width; j++) { \
141 dest[C1] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
148 for (i = 0; i < height; i++) { \
149 for (j = 0; j < width; j++) { \
150 val = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
161 A32_CHECKER_C (argb, TRUE, 0, 1, 2, 3);
162 A32_CHECKER_C (bgra, TRUE, 3, 2, 1, 0);
163 A32_CHECKER_C (ayuv, FALSE, 0, 1, 2, 3);
165 #define YUV_TO_R(Y,U,V) (CLAMP (1.164 * (Y - 16) + 1.596 * (V - 128), 0, 255))
166 #define YUV_TO_G(Y,U,V) (CLAMP (1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128), 0, 255))
167 #define YUV_TO_B(Y,U,V) (CLAMP (1.164 * (Y - 16) + 2.018 * (U - 128), 0, 255))
169 #define A32_COLOR(name, RGB, A, C1, C2, C3) \
171 fill_color_##name (GstVideoFrame * frame, gint Y, gint U, gint V) \
175 gint width, height; \
178 dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
179 width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
180 height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
183 c1 = YUV_TO_R (Y, U, V); \
184 c2 = YUV_TO_G (Y, U, V); \
185 c3 = YUV_TO_B (Y, U, V); \
191 val = GUINT32_FROM_BE ((0xff << A) | (c1 << C1) | (c2 << C2) | (c3 << C3)); \
193 video_mixer_orc_splat_u32 ((guint32 *) dest, val, height * width); \
196 A32_COLOR (argb, TRUE, 24, 16, 8, 0);
197 A32_COLOR (bgra, TRUE, 0, 8, 16, 24);
198 A32_COLOR (abgr, TRUE, 24, 0, 8, 16);
199 A32_COLOR (rgba, TRUE, 0, 24, 16, 8);
200 A32_COLOR (ayuv, FALSE, 24, 16, 8, 0);
202 /* Y444, Y42B, I420, YV12, Y41B */
203 #define PLANAR_YUV_BLEND(format_name,format_enum,x_round,y_round,MEMCPY,BLENDLOOP) \
205 _blend_##format_name (const guint8 * src, guint8 * dest, \
206 gint src_stride, gint dest_stride, gint src_width, gint src_height, \
212 /* If it's completely transparent... we just return */ \
213 if (G_UNLIKELY (src_alpha == 0.0)) { \
214 GST_INFO ("Fast copy (alpha == 0.0)"); \
218 /* If it's completely opaque, we do a fast copy */ \
219 if (G_UNLIKELY (src_alpha == 1.0)) { \
220 GST_INFO ("Fast copy (alpha == 1.0)"); \
221 for (i = 0; i < src_height; i++) { \
222 MEMCPY (dest, src, src_width); \
224 dest += dest_stride; \
229 b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
231 BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width, src_height); \
235 blend_##format_name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
236 gdouble src_alpha, GstVideoFrame * destframe) \
238 const guint8 *b_src; \
244 gint src_comp_rowstride, dest_comp_rowstride; \
245 gint src_comp_height; \
246 gint src_comp_width; \
247 gint comp_ypos, comp_xpos; \
248 gint comp_yoffset, comp_xoffset; \
249 gint dest_width, dest_height; \
250 const GstVideoFormatInfo *info; \
251 gint src_width, src_height; \
253 src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
254 src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
256 info = srcframe->info.finfo; \
257 dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
258 dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
260 xpos = x_round (xpos); \
261 ypos = y_round (ypos); \
263 b_src_width = src_width; \
264 b_src_height = src_height; \
266 /* adjust src pointers for negative sizes */ \
269 b_src_width -= -xpos; \
274 b_src_height -= -ypos; \
277 /* If x or y offset are larger then the source it's outside of the picture */ \
278 if (xoffset >= src_width || yoffset >= src_height) { \
282 /* adjust width/height if the src is bigger than dest */ \
283 if (xpos + b_src_width > dest_width) { \
284 b_src_width = dest_width - xpos; \
286 if (ypos + b_src_height > dest_height) { \
287 b_src_height = dest_height - ypos; \
289 if (b_src_width <= 0 || b_src_height <= 0) { \
293 /* First mix Y, then U, then V */ \
294 b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 0); \
295 b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 0); \
296 src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
297 dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
298 src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 0, b_src_width); \
299 src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, b_src_height); \
300 comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xpos); \
301 comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, ypos); \
302 comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xoffset); \
303 comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, yoffset); \
304 _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
305 b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
306 src_comp_rowstride, \
307 dest_comp_rowstride, src_comp_width, src_comp_height, \
310 b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 1); \
311 b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 1); \
312 src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 1); \
313 dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 1); \
314 src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 1, b_src_width); \
315 src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, b_src_height); \
316 comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xpos); \
317 comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, ypos); \
318 comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xoffset); \
319 comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, yoffset); \
320 _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
321 b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
322 src_comp_rowstride, \
323 dest_comp_rowstride, src_comp_width, src_comp_height, \
326 b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 2); \
327 b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 2); \
328 src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 2); \
329 dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 2); \
330 src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 2, b_src_width); \
331 src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 2, b_src_height); \
332 comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 2, xpos); \
333 comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 2, ypos); \
334 comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 2, xoffset); \
335 comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 2, yoffset); \
336 _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
337 b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
338 src_comp_rowstride, \
339 dest_comp_rowstride, src_comp_width, src_comp_height, \
343 #define PLANAR_YUV_FILL_CHECKER(format_name, format_enum, MEMSET) \
345 fill_checker_##format_name (GstVideoFrame * frame) \
348 static const int tab[] = { 80, 160, 80, 160 }; \
350 gint comp_width, comp_height; \
353 p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
354 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
355 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
356 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
358 for (i = 0; i < comp_height; i++) { \
359 for (j = 0; j < comp_width; j++) { \
360 *p++ = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
362 p += rowstride - comp_width; \
365 p = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \
366 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
367 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \
368 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
370 for (i = 0; i < comp_height; i++) { \
371 MEMSET (p, 0x80, comp_width); \
375 p = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \
376 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 2); \
377 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 2); \
378 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); \
380 for (i = 0; i < comp_height; i++) { \
381 MEMSET (p, 0x80, comp_width); \
386 #define PLANAR_YUV_FILL_COLOR(format_name,format_enum,MEMSET) \
388 fill_color_##format_name (GstVideoFrame * frame, \
389 gint colY, gint colU, gint colV) \
392 gint comp_width, comp_height; \
396 p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
397 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
398 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
399 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
401 for (i = 0; i < comp_height; i++) { \
402 MEMSET (p, colY, comp_width); \
406 p = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \
407 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
408 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \
409 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
411 for (i = 0; i < comp_height; i++) { \
412 MEMSET (p, colU, comp_width); \
416 p = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \
417 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 2); \
418 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 2); \
419 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); \
421 for (i = 0; i < comp_height; i++) { \
422 MEMSET (p, colV, comp_width); \
427 #define GST_ROUND_UP_1(x) (x)
429 PLANAR_YUV_BLEND (i420, GST_VIDEO_FORMAT_I420, GST_ROUND_UP_2,
430 GST_ROUND_UP_2, memcpy, video_mixer_orc_blend_u8);
431 PLANAR_YUV_FILL_CHECKER (i420, GST_VIDEO_FORMAT_I420, memset);
432 PLANAR_YUV_FILL_COLOR (i420, GST_VIDEO_FORMAT_I420, memset);
433 PLANAR_YUV_FILL_COLOR (yv12, GST_VIDEO_FORMAT_YV12, memset);
434 PLANAR_YUV_BLEND (y444, GST_VIDEO_FORMAT_Y444, GST_ROUND_UP_1,
435 GST_ROUND_UP_1, memcpy, video_mixer_orc_blend_u8);
436 PLANAR_YUV_FILL_CHECKER (y444, GST_VIDEO_FORMAT_Y444, memset);
437 PLANAR_YUV_FILL_COLOR (y444, GST_VIDEO_FORMAT_Y444, memset);
438 PLANAR_YUV_BLEND (y42b, GST_VIDEO_FORMAT_Y42B, GST_ROUND_UP_2,
439 GST_ROUND_UP_1, memcpy, video_mixer_orc_blend_u8);
440 PLANAR_YUV_FILL_CHECKER (y42b, GST_VIDEO_FORMAT_Y42B, memset);
441 PLANAR_YUV_FILL_COLOR (y42b, GST_VIDEO_FORMAT_Y42B, memset);
442 PLANAR_YUV_BLEND (y41b, GST_VIDEO_FORMAT_Y41B, GST_ROUND_UP_4,
443 GST_ROUND_UP_1, memcpy, video_mixer_orc_blend_u8);
444 PLANAR_YUV_FILL_CHECKER (y41b, GST_VIDEO_FORMAT_Y41B, memset);
445 PLANAR_YUV_FILL_COLOR (y41b, GST_VIDEO_FORMAT_Y41B, memset);
448 #define NV_YUV_BLEND(format_name,MEMCPY,BLENDLOOP) \
450 _blend_##format_name (const guint8 * src, guint8 * dest, \
451 gint src_stride, gint dest_stride, gint src_width, gint src_height, \
457 /* If it's completely transparent... we just return */ \
458 if (G_UNLIKELY (src_alpha == 0.0)) { \
459 GST_INFO ("Fast copy (alpha == 0.0)"); \
463 /* If it's completely opaque, we do a fast copy */ \
464 if (G_UNLIKELY (src_alpha == 1.0)) { \
465 GST_INFO ("Fast copy (alpha == 1.0)"); \
466 for (i = 0; i < src_height; i++) { \
467 MEMCPY (dest, src, src_width); \
469 dest += dest_stride; \
474 b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
476 BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width, src_height); \
480 blend_##format_name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
481 gdouble src_alpha, GstVideoFrame * destframe) \
483 const guint8 *b_src; \
489 gint src_comp_rowstride, dest_comp_rowstride; \
490 gint src_comp_height; \
491 gint src_comp_width; \
492 gint comp_ypos, comp_xpos; \
493 gint comp_yoffset, comp_xoffset; \
494 gint dest_width, dest_height; \
495 const GstVideoFormatInfo *info; \
496 gint src_width, src_height; \
498 src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
499 src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
501 info = srcframe->info.finfo; \
502 dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
503 dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
505 xpos = GST_ROUND_UP_2 (xpos); \
506 ypos = GST_ROUND_UP_2 (ypos); \
508 b_src_width = src_width; \
509 b_src_height = src_height; \
511 /* adjust src pointers for negative sizes */ \
514 b_src_width -= -xpos; \
519 b_src_height -= -ypos; \
522 /* If x or y offset are larger then the source it's outside of the picture */ \
523 if (xoffset > src_width || yoffset > src_height) { \
527 /* adjust width/height if the src is bigger than dest */ \
528 if (xpos + src_width > dest_width) { \
529 b_src_width = dest_width - xpos; \
531 if (ypos + src_height > dest_height) { \
532 b_src_height = dest_height - ypos; \
534 if (b_src_width < 0 || b_src_height < 0) { \
538 /* First mix Y, then UV */ \
539 b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 0); \
540 b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 0); \
541 src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
542 dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
543 src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 0, b_src_width); \
544 src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, b_src_height); \
545 comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xpos); \
546 comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, ypos); \
547 comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xoffset); \
548 comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, yoffset); \
549 _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
550 b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
551 src_comp_rowstride, \
552 dest_comp_rowstride, src_comp_width, src_comp_height, \
555 b_src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 1); \
556 b_dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 1); \
557 src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 1); \
558 dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 1); \
559 src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 1, b_src_width); \
560 src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, b_src_height); \
561 comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xpos); \
562 comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, ypos); \
563 comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xoffset); \
564 comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, yoffset); \
565 _blend_##format_name (b_src + comp_xoffset * 2 + comp_yoffset * src_comp_rowstride, \
566 b_dest + comp_xpos * 2 + comp_ypos * dest_comp_rowstride, \
567 src_comp_rowstride, \
568 dest_comp_rowstride, 2 * src_comp_width, src_comp_height, \
572 #define NV_YUV_FILL_CHECKER(format_name, MEMSET) \
574 fill_checker_##format_name (GstVideoFrame * frame) \
577 static const int tab[] = { 80, 160, 80, 160 }; \
579 gint comp_width, comp_height; \
582 p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
583 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
584 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
585 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
587 for (i = 0; i < comp_height; i++) { \
588 for (j = 0; j < comp_width; j++) { \
589 *p++ = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
591 p += rowstride - comp_width; \
594 p = GST_VIDEO_FRAME_PLANE_DATA (frame, 1); \
595 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
596 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \
597 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
599 for (i = 0; i < comp_height; i++) { \
600 MEMSET (p, 0x80, comp_width * 2); \
605 #define NV_YUV_FILL_COLOR(format_name,MEMSET) \
607 fill_color_##format_name (GstVideoFrame * frame, \
608 gint colY, gint colU, gint colV) \
611 gint comp_width, comp_height; \
615 y = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \
616 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \
617 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \
618 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
620 for (i = 0; i < comp_height; i++) { \
621 MEMSET (y, colY, comp_width); \
625 u = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \
626 v = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \
627 comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \
628 comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \
629 rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \
631 for (i = 0; i < comp_height; i++) { \
632 for (j = 0; j < comp_width; j++) { \
641 NV_YUV_BLEND (nv12, memcpy, video_mixer_orc_blend_u8);
642 NV_YUV_FILL_CHECKER (nv12, memset);
643 NV_YUV_FILL_COLOR (nv12, memset);
644 NV_YUV_BLEND (nv21, memcpy, video_mixer_orc_blend_u8);
645 NV_YUV_FILL_CHECKER (nv21, memset);
647 /* RGB, BGR, xRGB, xBGR, RGBx, BGRx */
649 #define RGB_BLEND(name, bpp, MEMCPY, BLENDLOOP) \
651 blend_##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
652 gdouble src_alpha, GstVideoFrame * destframe) \
656 gint src_stride, dest_stride; \
657 gint dest_width, dest_height; \
658 guint8 *dest, *src; \
659 gint src_width, src_height; \
661 src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
662 src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
664 src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \
665 dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \
667 dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
668 dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
670 src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
671 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
673 b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
675 /* adjust src pointers for negative sizes */ \
677 src += -xpos * bpp; \
678 src_width -= -xpos; \
682 src += -ypos * src_stride; \
683 src_height -= -ypos; \
686 /* adjust width/height if the src is bigger than dest */ \
687 if (xpos + src_width > dest_width) { \
688 src_width = dest_width - xpos; \
690 if (ypos + src_height > dest_height) { \
691 src_height = dest_height - ypos; \
694 dest = dest + bpp * xpos + (ypos * dest_stride); \
695 /* If it's completely transparent... we just return */ \
696 if (G_UNLIKELY (src_alpha == 0.0)) { \
697 GST_INFO ("Fast copy (alpha == 0.0)"); \
701 /* If it's completely opaque, we do a fast copy */ \
702 if (G_UNLIKELY (src_alpha == 1.0)) { \
703 GST_INFO ("Fast copy (alpha == 1.0)"); \
704 for (i = 0; i < src_height; i++) { \
705 MEMCPY (dest, src, bpp * src_width); \
707 dest += dest_stride; \
712 BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width * bpp, src_height); \
715 #define RGB_FILL_CHECKER_C(name, bpp, r, g, b) \
717 fill_checker_##name##_c (GstVideoFrame * frame) \
720 static const int tab[] = { 80, 160, 80, 160 }; \
721 gint stride, dest_add, width, height; \
724 width = GST_VIDEO_FRAME_WIDTH (frame); \
725 height = GST_VIDEO_FRAME_HEIGHT (frame); \
726 dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
727 stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
728 dest_add = stride - width * bpp; \
730 for (i = 0; i < height; i++) { \
731 for (j = 0; j < width; j++) { \
732 dest[r] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; /* red */ \
733 dest[g] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; /* green */ \
734 dest[b] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; /* blue */ \
741 #define RGB_FILL_COLOR(name, bpp, MEMSET_RGB) \
743 fill_color_##name (GstVideoFrame * frame, \
744 gint colY, gint colU, gint colV) \
746 gint red, green, blue; \
749 gint width, height; \
752 width = GST_VIDEO_FRAME_WIDTH (frame); \
753 height = GST_VIDEO_FRAME_HEIGHT (frame); \
754 dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
755 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
757 red = YUV_TO_R (colY, colU, colV); \
758 green = YUV_TO_G (colY, colU, colV); \
759 blue = YUV_TO_B (colY, colU, colV); \
761 for (i = 0; i < height; i++) { \
762 MEMSET_RGB (dest, red, green, blue, width); \
763 dest += dest_stride; \
767 #define MEMSET_RGB_C(name, r, g, b) \
769 _memset_##name##_c (guint8* dest, gint red, gint green, gint blue, gint width) { \
772 for (j = 0; j < width; j++) { \
780 #define MEMSET_XRGB(name, r, g, b) \
782 _memset_##name (guint8* dest, gint red, gint green, gint blue, gint width) { \
785 val = GUINT32_FROM_BE ((red << r) | (green << g) | (blue << b)); \
786 video_mixer_orc_splat_u32 ((guint32 *) dest, val, width); \
789 #define _orc_memcpy_u32(dest,src,len) video_mixer_orc_memcpy_u32((guint32 *) dest, (const guint32 *) src, len/4)
791 RGB_BLEND (rgb, 3, memcpy, video_mixer_orc_blend_u8);
792 RGB_FILL_CHECKER_C (rgb, 3, 0, 1, 2);
793 MEMSET_RGB_C (rgb, 0, 1, 2);
794 RGB_FILL_COLOR (rgb_c, 3, _memset_rgb_c);
796 MEMSET_RGB_C (bgr, 2, 1, 0);
797 RGB_FILL_COLOR (bgr_c, 3, _memset_bgr_c);
799 RGB_BLEND (xrgb, 4, _orc_memcpy_u32, video_mixer_orc_blend_u8);
800 RGB_FILL_CHECKER_C (xrgb, 4, 1, 2, 3);
801 MEMSET_XRGB (xrgb, 24, 16, 0);
802 RGB_FILL_COLOR (xrgb, 4, _memset_xrgb);
804 MEMSET_XRGB (xbgr, 0, 16, 24);
805 RGB_FILL_COLOR (xbgr, 4, _memset_xbgr);
807 MEMSET_XRGB (rgbx, 24, 16, 8);
808 RGB_FILL_COLOR (rgbx, 4, _memset_rgbx);
810 MEMSET_XRGB (bgrx, 8, 16, 24);
811 RGB_FILL_COLOR (bgrx, 4, _memset_bgrx);
813 /* YUY2, YVYU, UYVY */
815 #define PACKED_422_BLEND(name, MEMCPY, BLENDLOOP) \
817 blend_##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \
818 gdouble src_alpha, GstVideoFrame * destframe) \
822 gint src_stride, dest_stride; \
823 gint dest_width, dest_height; \
824 guint8 *src, *dest; \
825 gint src_width, src_height; \
827 src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \
828 src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \
830 dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \
831 dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \
833 src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \
834 dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \
836 src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \
837 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \
839 b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
841 xpos = GST_ROUND_UP_2 (xpos); \
843 /* adjust src pointers for negative sizes */ \
846 src_width -= -xpos; \
850 src += -ypos * src_stride; \
851 src_height -= -ypos; \
855 /* adjust width/height if the src is bigger than dest */ \
856 if (xpos + src_width > dest_width) { \
857 src_width = dest_width - xpos; \
859 if (ypos + src_height > dest_height) { \
860 src_height = dest_height - ypos; \
863 dest = dest + 2 * xpos + (ypos * dest_stride); \
864 /* If it's completely transparent... we just return */ \
865 if (G_UNLIKELY (src_alpha == 0.0)) { \
866 GST_INFO ("Fast copy (alpha == 0.0)"); \
870 /* If it's completely opaque, we do a fast copy */ \
871 if (G_UNLIKELY (src_alpha == 1.0)) { \
872 GST_INFO ("Fast copy (alpha == 1.0)"); \
873 for (i = 0; i < src_height; i++) { \
874 MEMCPY (dest, src, 2 * src_width); \
876 dest += dest_stride; \
881 BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, 2 * src_width, src_height); \
884 #define PACKED_422_FILL_CHECKER_C(name, Y1, U, Y2, V) \
886 fill_checker_##name##_c (GstVideoFrame * frame) \
889 static const int tab[] = { 80, 160, 80, 160 }; \
891 gint width, height; \
894 width = GST_VIDEO_FRAME_WIDTH (frame); \
895 width = GST_ROUND_UP_2 (width); \
896 height = GST_VIDEO_FRAME_HEIGHT (frame); \
897 dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
898 dest_add = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0) - width * 2; \
901 for (i = 0; i < height; i++) { \
902 for (j = 0; j < width; j++) { \
903 dest[Y1] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
904 dest[Y2] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
913 #define PACKED_422_FILL_COLOR(name, Y1, U, Y2, V) \
915 fill_color_##name (GstVideoFrame * frame, \
916 gint colY, gint colU, gint colV) \
921 gint width, height; \
924 width = GST_VIDEO_FRAME_WIDTH (frame); \
925 width = GST_ROUND_UP_2 (width); \
926 height = GST_VIDEO_FRAME_HEIGHT (frame); \
927 dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \
928 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \
931 val = GUINT32_FROM_BE ((colY << Y1) | (colY << Y2) | (colU << U) | (colV << V)); \
933 for (i = 0; i < height; i++) { \
934 video_mixer_orc_splat_u32 ((guint32 *) dest, val, width); \
935 dest += dest_stride; \
939 PACKED_422_BLEND (yuy2, memcpy, video_mixer_orc_blend_u8);
940 PACKED_422_FILL_CHECKER_C (yuy2, 0, 1, 2, 3);
941 PACKED_422_FILL_CHECKER_C (uyvy, 1, 0, 3, 2);
942 PACKED_422_FILL_COLOR (yuy2, 24, 16, 8, 0);
943 PACKED_422_FILL_COLOR (yvyu, 24, 0, 8, 16);
944 PACKED_422_FILL_COLOR (uyvy, 16, 24, 0, 8);
947 BlendFunction gst_video_mixer_blend_argb;
948 BlendFunction gst_video_mixer_blend_bgra;
949 BlendFunction gst_video_mixer_overlay_argb;
950 BlendFunction gst_video_mixer_overlay_bgra;
951 /* AYUV/ABGR is equal to ARGB, RGBA is equal to BGRA */
952 BlendFunction gst_video_mixer_blend_y444;
953 BlendFunction gst_video_mixer_blend_y42b;
954 BlendFunction gst_video_mixer_blend_i420;
955 /* I420 is equal to YV12 */
956 BlendFunction gst_video_mixer_blend_nv12;
957 BlendFunction gst_video_mixer_blend_nv21;
958 BlendFunction gst_video_mixer_blend_y41b;
959 BlendFunction gst_video_mixer_blend_rgb;
960 /* BGR is equal to RGB */
961 BlendFunction gst_video_mixer_blend_rgbx;
962 /* BGRx, xRGB, xBGR are equal to RGBx */
963 BlendFunction gst_video_mixer_blend_yuy2;
964 /* YVYU and UYVY are equal to YUY2 */
966 FillCheckerFunction gst_video_mixer_fill_checker_argb;
967 FillCheckerFunction gst_video_mixer_fill_checker_bgra;
968 /* ABGR is equal to ARGB, RGBA is equal to BGRA */
969 FillCheckerFunction gst_video_mixer_fill_checker_ayuv;
970 FillCheckerFunction gst_video_mixer_fill_checker_y444;
971 FillCheckerFunction gst_video_mixer_fill_checker_y42b;
972 FillCheckerFunction gst_video_mixer_fill_checker_i420;
973 /* I420 is equal to YV12 */
974 FillCheckerFunction gst_video_mixer_fill_checker_nv12;
975 FillCheckerFunction gst_video_mixer_fill_checker_nv21;
976 FillCheckerFunction gst_video_mixer_fill_checker_y41b;
977 FillCheckerFunction gst_video_mixer_fill_checker_rgb;
978 /* BGR is equal to RGB */
979 FillCheckerFunction gst_video_mixer_fill_checker_xrgb;
980 /* BGRx, xRGB, xBGR are equal to RGBx */
981 FillCheckerFunction gst_video_mixer_fill_checker_yuy2;
982 /* YVYU is equal to YUY2 */
983 FillCheckerFunction gst_video_mixer_fill_checker_uyvy;
985 FillColorFunction gst_video_mixer_fill_color_argb;
986 FillColorFunction gst_video_mixer_fill_color_bgra;
987 FillColorFunction gst_video_mixer_fill_color_abgr;
988 FillColorFunction gst_video_mixer_fill_color_rgba;
989 FillColorFunction gst_video_mixer_fill_color_ayuv;
990 FillColorFunction gst_video_mixer_fill_color_y444;
991 FillColorFunction gst_video_mixer_fill_color_y42b;
992 FillColorFunction gst_video_mixer_fill_color_i420;
993 FillColorFunction gst_video_mixer_fill_color_yv12;
994 FillColorFunction gst_video_mixer_fill_color_nv12;
995 /* NV21 is equal to NV12 */
996 FillColorFunction gst_video_mixer_fill_color_y41b;
997 FillColorFunction gst_video_mixer_fill_color_rgb;
998 FillColorFunction gst_video_mixer_fill_color_bgr;
999 FillColorFunction gst_video_mixer_fill_color_xrgb;
1000 FillColorFunction gst_video_mixer_fill_color_xbgr;
1001 FillColorFunction gst_video_mixer_fill_color_rgbx;
1002 FillColorFunction gst_video_mixer_fill_color_bgrx;
1003 FillColorFunction gst_video_mixer_fill_color_yuy2;
1004 FillColorFunction gst_video_mixer_fill_color_yvyu;
1005 FillColorFunction gst_video_mixer_fill_color_uyvy;
1008 gst_video_mixer_init_blend (void)
1010 GST_DEBUG_CATEGORY_INIT (gst_videomixer_blend_debug, "videomixer_blend", 0,
1011 "video mixer blending functions");
1013 gst_video_mixer_blend_argb = blend_argb;
1014 gst_video_mixer_blend_bgra = blend_bgra;
1015 gst_video_mixer_overlay_argb = overlay_argb;
1016 gst_video_mixer_overlay_bgra = overlay_bgra;
1017 gst_video_mixer_blend_i420 = blend_i420;
1018 gst_video_mixer_blend_nv12 = blend_nv12;
1019 gst_video_mixer_blend_nv21 = blend_nv21;
1020 gst_video_mixer_blend_y444 = blend_y444;
1021 gst_video_mixer_blend_y42b = blend_y42b;
1022 gst_video_mixer_blend_y41b = blend_y41b;
1023 gst_video_mixer_blend_rgb = blend_rgb;
1024 gst_video_mixer_blend_xrgb = blend_xrgb;
1025 gst_video_mixer_blend_yuy2 = blend_yuy2;
1027 gst_video_mixer_fill_checker_argb = fill_checker_argb_c;
1028 gst_video_mixer_fill_checker_bgra = fill_checker_bgra_c;
1029 gst_video_mixer_fill_checker_ayuv = fill_checker_ayuv_c;
1030 gst_video_mixer_fill_checker_i420 = fill_checker_i420;
1031 gst_video_mixer_fill_checker_nv12 = fill_checker_nv12;
1032 gst_video_mixer_fill_checker_nv21 = fill_checker_nv21;
1033 gst_video_mixer_fill_checker_y444 = fill_checker_y444;
1034 gst_video_mixer_fill_checker_y42b = fill_checker_y42b;
1035 gst_video_mixer_fill_checker_y41b = fill_checker_y41b;
1036 gst_video_mixer_fill_checker_rgb = fill_checker_rgb_c;
1037 gst_video_mixer_fill_checker_xrgb = fill_checker_xrgb_c;
1038 gst_video_mixer_fill_checker_yuy2 = fill_checker_yuy2_c;
1039 gst_video_mixer_fill_checker_uyvy = fill_checker_uyvy_c;
1041 gst_video_mixer_fill_color_argb = fill_color_argb;
1042 gst_video_mixer_fill_color_bgra = fill_color_bgra;
1043 gst_video_mixer_fill_color_abgr = fill_color_abgr;
1044 gst_video_mixer_fill_color_rgba = fill_color_rgba;
1045 gst_video_mixer_fill_color_ayuv = fill_color_ayuv;
1046 gst_video_mixer_fill_color_i420 = fill_color_i420;
1047 gst_video_mixer_fill_color_yv12 = fill_color_yv12;
1048 gst_video_mixer_fill_color_nv12 = fill_color_nv12;
1049 gst_video_mixer_fill_color_y444 = fill_color_y444;
1050 gst_video_mixer_fill_color_y42b = fill_color_y42b;
1051 gst_video_mixer_fill_color_y41b = fill_color_y41b;
1052 gst_video_mixer_fill_color_rgb = fill_color_rgb_c;
1053 gst_video_mixer_fill_color_bgr = fill_color_bgr_c;
1054 gst_video_mixer_fill_color_xrgb = fill_color_xrgb;
1055 gst_video_mixer_fill_color_xbgr = fill_color_xbgr;
1056 gst_video_mixer_fill_color_rgbx = fill_color_rgbx;
1057 gst_video_mixer_fill_color_bgrx = fill_color_bgrx;
1058 gst_video_mixer_fill_color_yuy2 = fill_color_yuy2;
1059 gst_video_mixer_fill_color_yvyu = fill_color_yvyu;
1060 gst_video_mixer_fill_color_uyvy = fill_color_uyvy;