Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.git] / gst / videomixer / blend.c
1 /* 
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>
8  *
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.
13  *
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.
18  *
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., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "blend.h"
30 #include "blendorc.h"
31
32 #include <string.h>
33
34 #include <gst/video/video.h>
35
36 #define BLEND(D,S,alpha) (((D) * (256 - (alpha)) + (S) * (alpha)) >> 8)
37
38 GST_DEBUG_CATEGORY_STATIC (gst_videomixer_blend_debug);
39 #define GST_CAT_DEFAULT gst_videomixer_blend_debug
40
41 /* Below are the implementations of everything */
42
43 /* A32 is for AYUV, ARGB and BGRA */
44 #define BLEND_A32(name, method, LOOP)           \
45 static void \
46 method##_ ##name (const guint8 * src, gint xpos, gint ypos, \
47     gint src_width, gint src_height, gdouble src_alpha, \
48     guint8 * dest, gint dest_width, gint dest_height) \
49 { \
50   guint s_alpha; \
51   gint src_stride, dest_stride; \
52   \
53   src_stride = src_width * 4; \
54   dest_stride = dest_width * 4; \
55   \
56   s_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
57   \
58   /* If it's completely transparent... we just return */ \
59   if (G_UNLIKELY (s_alpha == 0)) \
60     return; \
61   \
62   /* adjust src pointers for negative sizes */ \
63   if (xpos < 0) { \
64     src += -xpos * 4; \
65     src_width -= -xpos; \
66     xpos = 0; \
67   } \
68   if (ypos < 0) { \
69     src += -ypos * src_stride; \
70     src_height -= -ypos; \
71     ypos = 0; \
72   } \
73   /* adjust width/height if the src is bigger than dest */ \
74   if (xpos + src_width > dest_width) { \
75     src_width = dest_width - xpos; \
76   } \
77   if (ypos + src_height > dest_height) { \
78     src_height = dest_height - ypos; \
79   } \
80   \
81   dest = dest + 4 * xpos + (ypos * dest_stride); \
82   \
83   LOOP (dest, src, src_height, src_width, src_stride, dest_stride, s_alpha); \
84 }
85
86 #define BLEND_A32_LOOP(name, method)                    \
87 static inline void \
88 _##method##_loop_##name (guint8 * dest, const guint8 * src, gint src_height, \
89     gint src_width, gint src_stride, gint dest_stride, guint s_alpha) \
90 { \
91   s_alpha = MIN (255, s_alpha); \
92   orc_##method##_##name (dest, dest_stride, src, src_stride, \
93       s_alpha, src_width, src_height); \
94 }
95
96 BLEND_A32_LOOP (argb, blend);
97 BLEND_A32_LOOP (bgra, blend);
98 BLEND_A32_LOOP (argb, overlay);
99 BLEND_A32_LOOP (bgra, overlay);
100
101 #if G_BYTE_ORDER == LITTLE_ENDIAN
102 BLEND_A32 (argb, blend, _blend_loop_argb);
103 BLEND_A32 (bgra, blend, _blend_loop_bgra);
104 BLEND_A32 (argb, overlay, _overlay_loop_argb);
105 BLEND_A32 (bgra, overlay, _overlay_loop_bgra);
106 #else
107 BLEND_A32 (argb, blend, _blend_loop_bgra);
108 BLEND_A32 (bgra, blend, _blend_loop_argb);
109 BLEND_A32 (argb, overlay, _overlay_loop_bgra);
110 BLEND_A32 (bgra, overlay, _overlay_loop_argb);
111 #endif
112
113 #define A32_CHECKER_C(name, RGB, A, C1, C2, C3) \
114 static void \
115 fill_checker_##name##_c (guint8 * dest, gint width, gint height) \
116 { \
117   gint i, j; \
118   gint val; \
119   static const gint tab[] = { 80, 160, 80, 160 }; \
120   \
121   if (!RGB) { \
122     for (i = 0; i < height; i++) { \
123       for (j = 0; j < width; j++) { \
124         dest[A] = 0xff; \
125         dest[C1] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
126         dest[C2] = 128; \
127         dest[C3] = 128; \
128         dest += 4; \
129       } \
130     } \
131   } else { \
132     for (i = 0; i < height; i++) { \
133       for (j = 0; j < width; j++) { \
134         val = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
135         dest[A] = 0xFF; \
136         dest[C1] = val; \
137         dest[C2] = val; \
138         dest[C3] = val; \
139         dest += 4; \
140       } \
141     } \
142   } \
143 }
144
145 A32_CHECKER_C (argb, TRUE, 0, 1, 2, 3);
146 A32_CHECKER_C (bgra, TRUE, 3, 2, 1, 0);
147 A32_CHECKER_C (ayuv, FALSE, 0, 1, 2, 3);
148
149 #define YUV_TO_R(Y,U,V) (CLAMP (1.164 * (Y - 16) + 1.596 * (V - 128), 0, 255))
150 #define YUV_TO_G(Y,U,V) (CLAMP (1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128), 0, 255))
151 #define YUV_TO_B(Y,U,V) (CLAMP (1.164 * (Y - 16) + 2.018 * (U - 128), 0, 255))
152
153 #define A32_COLOR(name, RGB, A, C1, C2, C3) \
154 static void \
155 fill_color_##name (guint8 * dest, gint width, gint height, gint Y, gint U, gint V) \
156 { \
157   gint c1, c2, c3; \
158   guint32 val; \
159   \
160   if (RGB) { \
161     c1 = YUV_TO_R (Y, U, V); \
162     c2 = YUV_TO_G (Y, U, V); \
163     c3 = YUV_TO_B (Y, U, V); \
164   } else { \
165     c1 = Y; \
166     c2 = U; \
167     c3 = V; \
168   } \
169   val = GUINT32_FROM_BE ((0xff << A) | (c1 << C1) | (c2 << C2) | (c3 << C3)); \
170   \
171   orc_splat_u32 ((guint32 *) dest, val, height * width); \
172 }
173
174 A32_COLOR (argb, TRUE, 24, 16, 8, 0);
175 A32_COLOR (bgra, TRUE, 0, 8, 16, 24);
176 A32_COLOR (abgr, TRUE, 24, 0, 8, 16);
177 A32_COLOR (rgba, TRUE, 0, 24, 16, 8);
178 A32_COLOR (ayuv, FALSE, 24, 16, 8, 0);
179
180 /* Y444, Y42B, I420, YV12, Y41B */
181 #define PLANAR_YUV_BLEND(format_name,format_enum,x_round,y_round,MEMCPY,BLENDLOOP) \
182 inline static void \
183 _blend_##format_name (const guint8 * src, guint8 * dest, \
184     gint src_stride, gint dest_stride, gint src_width, gint src_height, \
185     gdouble src_alpha) \
186 { \
187   gint i; \
188   gint b_alpha; \
189   \
190   /* If it's completely transparent... we just return */ \
191   if (G_UNLIKELY (src_alpha == 0.0)) { \
192     GST_INFO ("Fast copy (alpha == 0.0)"); \
193     return; \
194   } \
195   \
196   /* If it's completely opaque, we do a fast copy */ \
197   if (G_UNLIKELY (src_alpha == 1.0)) { \
198     GST_INFO ("Fast copy (alpha == 1.0)"); \
199     for (i = 0; i < src_height; i++) { \
200       MEMCPY (dest, src, src_width); \
201       src += src_stride; \
202       dest += dest_stride; \
203     } \
204     return; \
205   } \
206   \
207   b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
208   \
209   BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width, src_height); \
210 } \
211 \
212 static void \
213 blend_##format_name (const guint8 * src, gint xpos, gint ypos, \
214     gint src_width, gint src_height, gdouble src_alpha, \
215     guint8 * dest, gint dest_width, gint dest_height) \
216 { \
217   const guint8 *b_src; \
218   guint8 *b_dest; \
219   gint b_src_width = src_width; \
220   gint b_src_height = src_height; \
221   gint xoffset = 0; \
222   gint yoffset = 0; \
223   gint src_comp_rowstride, dest_comp_rowstride; \
224   gint src_comp_height; \
225   gint src_comp_width; \
226   gint comp_ypos, comp_xpos; \
227   gint comp_yoffset, comp_xoffset; \
228   \
229   xpos = x_round (xpos); \
230   ypos = y_round (ypos); \
231   \
232   /* adjust src pointers for negative sizes */ \
233   if (xpos < 0) { \
234     xoffset = -xpos; \
235     b_src_width -= -xpos; \
236     xpos = 0; \
237   } \
238   if (ypos < 0) { \
239     yoffset += -ypos; \
240     b_src_height -= -ypos; \
241     ypos = 0; \
242   } \
243   /* If x or y offset are larger then the source it's outside of the picture */ \
244   if (xoffset > src_width || yoffset > src_width) { \
245     return; \
246   } \
247   \
248   /* adjust width/height if the src is bigger than dest */ \
249   if (xpos + src_width > dest_width) { \
250     b_src_width = dest_width - xpos; \
251   } \
252   if (ypos + src_height > dest_height) { \
253     b_src_height = dest_height - ypos; \
254   } \
255   if (b_src_width < 0 || b_src_height < 0) { \
256     return; \
257   } \
258   \
259   /* First mix Y, then U, then V */ \
260   b_src = src + gst_video_format_get_component_offset (format_enum, 0, src_width, src_height); \
261   b_dest = dest + gst_video_format_get_component_offset (format_enum, 0, dest_width, dest_height); \
262   src_comp_rowstride = gst_video_format_get_row_stride (format_enum, 0, src_width); \
263   dest_comp_rowstride = gst_video_format_get_row_stride (format_enum, 0, dest_width); \
264   src_comp_height = gst_video_format_get_component_height (format_enum, 0, b_src_height); \
265   src_comp_width = gst_video_format_get_component_width (format_enum, 0, b_src_width); \
266   comp_xpos = (xpos == 0) ? 0 : gst_video_format_get_component_width (format_enum, 0, xpos); \
267   comp_ypos = (ypos == 0) ? 0 : gst_video_format_get_component_height (format_enum, 0, ypos); \
268   comp_xoffset = (xoffset == 0) ? 0 : gst_video_format_get_component_width (format_enum, 0, xoffset); \
269   comp_yoffset = (yoffset == 0) ? 0 : gst_video_format_get_component_height (format_enum, 0, yoffset); \
270   _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
271       b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
272       src_comp_rowstride, \
273       dest_comp_rowstride, src_comp_width, src_comp_height, \
274       src_alpha); \
275   \
276   b_src = src + gst_video_format_get_component_offset (format_enum, 1, src_width, src_height); \
277   b_dest = dest + gst_video_format_get_component_offset (format_enum, 1, dest_width, dest_height); \
278   src_comp_rowstride = gst_video_format_get_row_stride (format_enum, 1, src_width); \
279   dest_comp_rowstride = gst_video_format_get_row_stride (format_enum, 1, dest_width); \
280   src_comp_height = gst_video_format_get_component_height (format_enum, 1, b_src_height); \
281   src_comp_width = gst_video_format_get_component_width (format_enum, 1, b_src_width); \
282   comp_xpos = (xpos == 0) ? 0 : gst_video_format_get_component_width (format_enum, 1, xpos); \
283   comp_ypos = (ypos == 0) ? 0 : gst_video_format_get_component_height (format_enum, 1, ypos); \
284   comp_xoffset = (xoffset == 0) ? 0 : gst_video_format_get_component_width (format_enum, 1, xoffset); \
285   comp_yoffset = (yoffset == 0) ? 0 : gst_video_format_get_component_height (format_enum, 1, yoffset); \
286   _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \
287       b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \
288       src_comp_rowstride, \
289       dest_comp_rowstride, src_comp_width, src_comp_height, \
290       src_alpha); \
291   \
292   b_src = src + gst_video_format_get_component_offset (format_enum, 2, src_width, src_height); \
293   b_dest = dest + gst_video_format_get_component_offset (format_enum, 2, dest_width, dest_height); \
294   src_comp_rowstride = gst_video_format_get_row_stride (format_enum, 2, src_width); \
295   dest_comp_rowstride = gst_video_format_get_row_stride (format_enum, 2, dest_width); \
296   src_comp_height = gst_video_format_get_component_height (format_enum, 2, b_src_height); \
297   src_comp_width = gst_video_format_get_component_width (format_enum, 2, b_src_width); \
298   comp_xpos = (xpos == 0) ? 0 : gst_video_format_get_component_width (format_enum, 2, xpos); \
299   comp_ypos = (ypos == 0) ? 0 : gst_video_format_get_component_height (format_enum, 2, ypos); \
300   comp_xoffset = (xoffset == 0) ? 0 : gst_video_format_get_component_width (format_enum, 2, xoffset); \
301   comp_yoffset = (yoffset == 0) ? 0 : gst_video_format_get_component_height (format_enum, 2, 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, \
306       src_alpha); \
307 }
308
309 #define PLANAR_YUV_FILL_CHECKER(format_name, format_enum, MEMSET) \
310 static void \
311 fill_checker_##format_name (guint8 * dest, gint width, gint height) \
312 { \
313   gint i, j; \
314   static const int tab[] = { 80, 160, 80, 160 }; \
315   guint8 *p; \
316   gint comp_width, comp_height; \
317   gint rowstride; \
318   \
319   p = dest + gst_video_format_get_component_offset (format_enum, 0, width, height); \
320   comp_width = gst_video_format_get_component_width (format_enum, 0, width); \
321   comp_height = gst_video_format_get_component_height (format_enum, 0, height); \
322   rowstride = gst_video_format_get_row_stride (format_enum, 0, width); \
323   \
324   for (i = 0; i < comp_height; i++) { \
325     for (j = 0; j < comp_width; j++) { \
326       *p++ = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
327     } \
328     p += rowstride - comp_width; \
329   } \
330   \
331   p = dest + gst_video_format_get_component_offset (format_enum, 1, width, height); \
332   comp_width = gst_video_format_get_component_width (format_enum, 1, width); \
333   comp_height = gst_video_format_get_component_height (format_enum, 1, height); \
334   rowstride = gst_video_format_get_row_stride (format_enum, 1, width); \
335   \
336   for (i = 0; i < comp_height; i++) { \
337     MEMSET (p, 0x80, comp_width); \
338     p += rowstride; \
339   } \
340   \
341   p = dest + gst_video_format_get_component_offset (format_enum, 2, width, height); \
342   comp_width = gst_video_format_get_component_width (format_enum, 2, width); \
343   comp_height = gst_video_format_get_component_height (format_enum, 2, height); \
344   rowstride = gst_video_format_get_row_stride (format_enum, 2, width); \
345   \
346   for (i = 0; i < comp_height; i++) { \
347     MEMSET (p, 0x80, comp_width); \
348     p += rowstride; \
349   } \
350 }
351
352 #define PLANAR_YUV_FILL_COLOR(format_name,format_enum,MEMSET) \
353 static void \
354 fill_color_##format_name (guint8 * dest, gint width, gint height, \
355     gint colY, gint colU, gint colV) \
356 { \
357   guint8 *p; \
358   gint comp_width, comp_height; \
359   gint rowstride; \
360   gint i; \
361   \
362   p = dest + gst_video_format_get_component_offset (format_enum, 0, width, height); \
363   comp_width = gst_video_format_get_component_width (format_enum, 0, width); \
364   comp_height = gst_video_format_get_component_height (format_enum, 0, height); \
365   rowstride = gst_video_format_get_row_stride (format_enum, 0, width); \
366   \
367   for (i = 0; i < comp_height; i++) { \
368     MEMSET (p, colY, comp_width); \
369     p += rowstride; \
370   } \
371   \
372   p = dest + gst_video_format_get_component_offset (format_enum, 1, width, height); \
373   comp_width = gst_video_format_get_component_width (format_enum, 1, width); \
374   comp_height = gst_video_format_get_component_height (format_enum, 1, height); \
375   rowstride = gst_video_format_get_row_stride (format_enum, 1, width); \
376   \
377   for (i = 0; i < comp_height; i++) { \
378     MEMSET (p, colU, comp_width); \
379     p += rowstride; \
380   } \
381   \
382   p = dest + gst_video_format_get_component_offset (format_enum, 2, width, height); \
383   comp_width = gst_video_format_get_component_width (format_enum, 2, width); \
384   comp_height = gst_video_format_get_component_height (format_enum, 2, height); \
385   rowstride = gst_video_format_get_row_stride (format_enum, 2, width); \
386   \
387   for (i = 0; i < comp_height; i++) { \
388     MEMSET (p, colV, comp_width); \
389     p += rowstride; \
390   } \
391 }
392
393 #define GST_ROUND_UP_1(x) (x)
394
395 PLANAR_YUV_BLEND (i420, GST_VIDEO_FORMAT_I420, GST_ROUND_UP_2,
396     GST_ROUND_UP_2, memcpy, orc_blend_u8);
397 PLANAR_YUV_FILL_CHECKER (i420, GST_VIDEO_FORMAT_I420, memset);
398 PLANAR_YUV_FILL_COLOR (i420, GST_VIDEO_FORMAT_I420, memset);
399 PLANAR_YUV_FILL_COLOR (yv12, GST_VIDEO_FORMAT_YV12, memset);
400 PLANAR_YUV_BLEND (y444, GST_VIDEO_FORMAT_Y444, GST_ROUND_UP_1,
401     GST_ROUND_UP_1, memcpy, orc_blend_u8);
402 PLANAR_YUV_FILL_CHECKER (y444, GST_VIDEO_FORMAT_Y444, memset);
403 PLANAR_YUV_FILL_COLOR (y444, GST_VIDEO_FORMAT_Y444, memset);
404 PLANAR_YUV_BLEND (y42b, GST_VIDEO_FORMAT_Y42B, GST_ROUND_UP_2,
405     GST_ROUND_UP_1, memcpy, orc_blend_u8);
406 PLANAR_YUV_FILL_CHECKER (y42b, GST_VIDEO_FORMAT_Y42B, memset);
407 PLANAR_YUV_FILL_COLOR (y42b, GST_VIDEO_FORMAT_Y42B, memset);
408 PLANAR_YUV_BLEND (y41b, GST_VIDEO_FORMAT_Y41B, GST_ROUND_UP_4,
409     GST_ROUND_UP_1, memcpy, orc_blend_u8);
410 PLANAR_YUV_FILL_CHECKER (y41b, GST_VIDEO_FORMAT_Y41B, memset);
411 PLANAR_YUV_FILL_COLOR (y41b, GST_VIDEO_FORMAT_Y41B, memset);
412
413 /* RGB, BGR, xRGB, xBGR, RGBx, BGRx */
414
415 #define RGB_BLEND(name, bpp, MEMCPY, BLENDLOOP) \
416 static void \
417 blend_##name (const guint8 * src, gint xpos, gint ypos, \
418     gint src_width, gint src_height, gdouble src_alpha, \
419     guint8 * dest, gint dest_width, gint dest_height) \
420 { \
421   gint b_alpha; \
422   gint i; \
423   gint src_stride, dest_stride; \
424   \
425   src_stride = GST_ROUND_UP_4 (src_width * bpp); \
426   dest_stride = GST_ROUND_UP_4 (dest_width * bpp); \
427   \
428   b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
429   \
430   /* adjust src pointers for negative sizes */ \
431   if (xpos < 0) { \
432     src += -xpos * bpp; \
433     src_width -= -xpos; \
434     xpos = 0; \
435   } \
436   if (ypos < 0) { \
437     src += -ypos * src_stride; \
438     src_height -= -ypos; \
439     ypos = 0; \
440   } \
441   /* adjust width/height if the src is bigger than dest */ \
442   if (xpos + src_width > dest_width) { \
443     src_width = dest_width - xpos; \
444   } \
445   if (ypos + src_height > dest_height) { \
446     src_height = dest_height - ypos; \
447   } \
448   \
449   dest = dest + bpp * xpos + (ypos * dest_stride); \
450   /* If it's completely transparent... we just return */ \
451   if (G_UNLIKELY (src_alpha == 0.0)) { \
452     GST_INFO ("Fast copy (alpha == 0.0)"); \
453     return; \
454   } \
455   \
456   /* If it's completely opaque, we do a fast copy */ \
457   if (G_UNLIKELY (src_alpha == 1.0)) { \
458     GST_INFO ("Fast copy (alpha == 1.0)"); \
459     for (i = 0; i < src_height; i++) { \
460       MEMCPY (dest, src, bpp * src_width); \
461       src += src_stride; \
462       dest += dest_stride; \
463     } \
464     return; \
465   } \
466   \
467   BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width * bpp, src_height); \
468 }
469
470 #define RGB_FILL_CHECKER_C(name, bpp, r, g, b) \
471 static void \
472 fill_checker_##name##_c (guint8 * dest, gint width, gint height) \
473 { \
474   gint i, j; \
475   static const int tab[] = { 80, 160, 80, 160 }; \
476   gint dest_add = GST_ROUND_UP_4 (width * bpp) - width * bpp; \
477   \
478   for (i = 0; i < height; i++) { \
479     for (j = 0; j < width; j++) { \
480       dest[r] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)];       /* red */ \
481       dest[g] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)];       /* green */ \
482       dest[b] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)];       /* blue */ \
483       dest += bpp; \
484     } \
485     dest += dest_add; \
486   } \
487 }
488
489 #define RGB_FILL_COLOR(name, bpp, MEMSET_RGB) \
490 static void \
491 fill_color_##name (guint8 * dest, gint width, gint height, \
492     gint colY, gint colU, gint colV) \
493 { \
494   gint red, green, blue; \
495   gint i; \
496   gint dest_stride = GST_ROUND_UP_4 (width * bpp); \
497   \
498   red = YUV_TO_R (colY, colU, colV); \
499   green = YUV_TO_G (colY, colU, colV); \
500   blue = YUV_TO_B (colY, colU, colV); \
501   \
502   for (i = 0; i < height; i++) { \
503     MEMSET_RGB (dest, red, green, blue, width); \
504     dest += dest_stride; \
505   } \
506 }
507
508 #define MEMSET_RGB_C(name, r, g, b) \
509 static inline void \
510 _memset_##name##_c (guint8* dest, gint red, gint green, gint blue, gint width) { \
511   gint j; \
512   \
513   for (j = 0; j < width; j++) { \
514     dest[r] = red; \
515     dest[g] = green; \
516     dest[b] = blue; \
517     dest += 3; \
518   } \
519 }
520
521 #define MEMSET_XRGB(name, r, g, b) \
522 static inline void \
523 _memset_##name (guint8* dest, gint red, gint green, gint blue, gint width) { \
524   guint32 val; \
525   \
526   val = GUINT32_FROM_BE ((red << r) | (green << g) | (blue << b)); \
527   orc_splat_u32 ((guint32 *) dest, val, width); \
528 }
529
530 #define _orc_memcpy_u32(dest,src,len) orc_memcpy_u32((guint32 *) dest, (const guint32 *) src, len/4)
531
532 RGB_BLEND (rgb, 3, memcpy, orc_blend_u8);
533 RGB_FILL_CHECKER_C (rgb, 3, 0, 1, 2);
534 MEMSET_RGB_C (rgb, 0, 1, 2);
535 RGB_FILL_COLOR (rgb_c, 3, _memset_rgb_c);
536
537 MEMSET_RGB_C (bgr, 2, 1, 0);
538 RGB_FILL_COLOR (bgr_c, 3, _memset_bgr_c);
539
540 RGB_BLEND (xrgb, 4, _orc_memcpy_u32, orc_blend_u8);
541 RGB_FILL_CHECKER_C (xrgb, 4, 1, 2, 3);
542 MEMSET_XRGB (xrgb, 24, 16, 0);
543 RGB_FILL_COLOR (xrgb, 4, _memset_xrgb);
544
545 MEMSET_XRGB (xbgr, 0, 16, 24);
546 RGB_FILL_COLOR (xbgr, 4, _memset_xbgr);
547
548 MEMSET_XRGB (rgbx, 24, 16, 8);
549 RGB_FILL_COLOR (rgbx, 4, _memset_rgbx);
550
551 MEMSET_XRGB (bgrx, 8, 16, 24);
552 RGB_FILL_COLOR (bgrx, 4, _memset_bgrx);
553
554 /* YUY2, YVYU, UYVY */
555
556 #define PACKED_422_BLEND(name, MEMCPY, BLENDLOOP) \
557 static void \
558 blend_##name (const guint8 * src, gint xpos, gint ypos, \
559     gint src_width, gint src_height, gdouble src_alpha, \
560     guint8 * dest, gint dest_width, gint dest_height) \
561 { \
562   gint b_alpha; \
563   gint i; \
564   gint src_stride, dest_stride; \
565   \
566   src_stride = GST_ROUND_UP_4 (src_width * 2); \
567   dest_stride = GST_ROUND_UP_4 (dest_width * 2); \
568   \
569   b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \
570   \
571   xpos = GST_ROUND_UP_2 (xpos); \
572   \
573   /* adjust src pointers for negative sizes */ \
574   if (xpos < 0) { \
575     src += -xpos * 2; \
576     src_width -= -xpos; \
577     xpos = 0; \
578   } \
579   if (ypos < 0) { \
580     src += -ypos * src_stride; \
581     src_height -= -ypos; \
582     ypos = 0; \
583   } \
584   \
585   /* adjust width/height if the src is bigger than dest */ \
586   if (xpos + src_width > dest_width) { \
587     src_width = dest_width - xpos; \
588   } \
589   if (ypos + src_height > dest_height) { \
590     src_height = dest_height - ypos; \
591   } \
592   \
593   dest = dest + 2 * xpos + (ypos * dest_stride); \
594   /* If it's completely transparent... we just return */ \
595   if (G_UNLIKELY (src_alpha == 0.0)) { \
596     GST_INFO ("Fast copy (alpha == 0.0)"); \
597     return; \
598   } \
599   \
600   /* If it's completely opaque, we do a fast copy */ \
601   if (G_UNLIKELY (src_alpha == 1.0)) { \
602     GST_INFO ("Fast copy (alpha == 1.0)"); \
603     for (i = 0; i < src_height; i++) { \
604       MEMCPY (dest, src, 2 * src_width); \
605       src += src_stride; \
606       dest += dest_stride; \
607     } \
608     return; \
609   } \
610   \
611   BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, 2 * src_width, src_height); \
612 }
613
614 #define PACKED_422_FILL_CHECKER_C(name, Y1, U, Y2, V) \
615 static void \
616 fill_checker_##name##_c (guint8 * dest, gint width, gint height) \
617 { \
618   gint i, j; \
619   static const int tab[] = { 80, 160, 80, 160 }; \
620   gint dest_add; \
621   \
622   width = GST_ROUND_UP_2 (width); \
623   dest_add = GST_ROUND_UP_4 (width * 2) - width * 2; \
624   width /= 2; \
625   \
626   for (i = 0; i < height; i++) { \
627     for (j = 0; j < width; j++) { \
628       dest[Y1] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
629       dest[Y2] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \
630       dest[U] = 128; \
631       dest[V] = 128; \
632       dest += 4; \
633     } \
634     dest += dest_add; \
635   } \
636 }
637
638 #define PACKED_422_FILL_COLOR(name, Y1, U, Y2, V) \
639 static void \
640 fill_color_##name (guint8 * dest, gint width, gint height, \
641     gint colY, gint colU, gint colV) \
642 { \
643   gint i; \
644   gint dest_stride; \
645   guint32 val; \
646   \
647   width = GST_ROUND_UP_2 (width); \
648   dest_stride = GST_ROUND_UP_4 (width * 2); \
649   width /= 2; \
650   \
651   val = GUINT32_FROM_BE ((colY << Y1) | (colY << Y2) | (colU << U) | (colV << V)); \
652   \
653   for (i = 0; i < height; i++) { \
654     orc_splat_u32 ((guint32 *) dest, val, width); \
655     dest += dest_stride; \
656   } \
657 }
658
659 PACKED_422_BLEND (yuy2, memcpy, orc_blend_u8);
660 PACKED_422_FILL_CHECKER_C (yuy2, 0, 1, 2, 3);
661 PACKED_422_FILL_CHECKER_C (uyvy, 1, 0, 3, 2);
662 PACKED_422_FILL_COLOR (yuy2, 24, 16, 8, 0);
663 PACKED_422_FILL_COLOR (yvyu, 24, 0, 8, 16);
664 PACKED_422_FILL_COLOR (uyvy, 16, 24, 0, 8);
665
666 /* Init function */
667 BlendFunction gst_video_mixer_blend_argb;
668 BlendFunction gst_video_mixer_blend_bgra;
669 BlendFunction gst_video_mixer_overlay_argb;
670 BlendFunction gst_video_mixer_overlay_bgra;
671 /* AYUV/ABGR is equal to ARGB, RGBA is equal to BGRA */
672 BlendFunction gst_video_mixer_blend_y444;
673 BlendFunction gst_video_mixer_blend_y42b;
674 BlendFunction gst_video_mixer_blend_i420;
675 /* I420 is equal to YV12 */
676 BlendFunction gst_video_mixer_blend_y41b;
677 BlendFunction gst_video_mixer_blend_rgb;
678 /* BGR is equal to RGB */
679 BlendFunction gst_video_mixer_blend_rgbx;
680 /* BGRx, xRGB, xBGR are equal to RGBx */
681 BlendFunction gst_video_mixer_blend_yuy2;
682 /* YVYU and UYVY are equal to YUY2 */
683
684 FillCheckerFunction gst_video_mixer_fill_checker_argb;
685 FillCheckerFunction gst_video_mixer_fill_checker_bgra;
686 /* ABGR is equal to ARGB, RGBA is equal to BGRA */
687 FillCheckerFunction gst_video_mixer_fill_checker_ayuv;
688 FillCheckerFunction gst_video_mixer_fill_checker_y444;
689 FillCheckerFunction gst_video_mixer_fill_checker_y42b;
690 FillCheckerFunction gst_video_mixer_fill_checker_i420;
691 /* I420 is equal to YV12 */
692 FillCheckerFunction gst_video_mixer_fill_checker_y41b;
693 FillCheckerFunction gst_video_mixer_fill_checker_rgb;
694 /* BGR is equal to RGB */
695 FillCheckerFunction gst_video_mixer_fill_checker_xrgb;
696 /* BGRx, xRGB, xBGR are equal to RGBx */
697 FillCheckerFunction gst_video_mixer_fill_checker_yuy2;
698 /* YVYU is equal to YUY2 */
699 FillCheckerFunction gst_video_mixer_fill_checker_uyvy;
700
701 FillColorFunction gst_video_mixer_fill_color_argb;
702 FillColorFunction gst_video_mixer_fill_color_bgra;
703 FillColorFunction gst_video_mixer_fill_color_abgr;
704 FillColorFunction gst_video_mixer_fill_color_rgba;
705 FillColorFunction gst_video_mixer_fill_color_ayuv;
706 FillColorFunction gst_video_mixer_fill_color_y444;
707 FillColorFunction gst_video_mixer_fill_color_y42b;
708 FillColorFunction gst_video_mixer_fill_color_i420;
709 FillColorFunction gst_video_mixer_fill_color_yv12;
710 FillColorFunction gst_video_mixer_fill_color_y41b;
711 FillColorFunction gst_video_mixer_fill_color_rgb;
712 FillColorFunction gst_video_mixer_fill_color_bgr;
713 FillColorFunction gst_video_mixer_fill_color_xrgb;
714 FillColorFunction gst_video_mixer_fill_color_xbgr;
715 FillColorFunction gst_video_mixer_fill_color_rgbx;
716 FillColorFunction gst_video_mixer_fill_color_bgrx;
717 FillColorFunction gst_video_mixer_fill_color_yuy2;
718 FillColorFunction gst_video_mixer_fill_color_yvyu;
719 FillColorFunction gst_video_mixer_fill_color_uyvy;
720
721 void
722 gst_video_mixer_init_blend (void)
723 {
724   GST_DEBUG_CATEGORY_INIT (gst_videomixer_blend_debug, "videomixer_blend", 0,
725       "video mixer blending functions");
726
727   gst_video_mixer_blend_argb = blend_argb;
728   gst_video_mixer_blend_bgra = blend_bgra;
729   gst_video_mixer_overlay_argb = overlay_argb;
730   gst_video_mixer_overlay_bgra = overlay_bgra;
731   gst_video_mixer_blend_i420 = blend_i420;
732   gst_video_mixer_blend_y444 = blend_y444;
733   gst_video_mixer_blend_y42b = blend_y42b;
734   gst_video_mixer_blend_y41b = blend_y41b;
735   gst_video_mixer_blend_rgb = blend_rgb;
736   gst_video_mixer_blend_xrgb = blend_xrgb;
737   gst_video_mixer_blend_yuy2 = blend_yuy2;
738
739   gst_video_mixer_fill_checker_argb = fill_checker_argb_c;
740   gst_video_mixer_fill_checker_bgra = fill_checker_bgra_c;
741   gst_video_mixer_fill_checker_ayuv = fill_checker_ayuv_c;
742   gst_video_mixer_fill_checker_i420 = fill_checker_i420;
743   gst_video_mixer_fill_checker_y444 = fill_checker_y444;
744   gst_video_mixer_fill_checker_y42b = fill_checker_y42b;
745   gst_video_mixer_fill_checker_y41b = fill_checker_y41b;
746   gst_video_mixer_fill_checker_rgb = fill_checker_rgb_c;
747   gst_video_mixer_fill_checker_xrgb = fill_checker_xrgb_c;
748   gst_video_mixer_fill_checker_yuy2 = fill_checker_yuy2_c;
749   gst_video_mixer_fill_checker_uyvy = fill_checker_uyvy_c;
750
751   gst_video_mixer_fill_color_argb = fill_color_argb;
752   gst_video_mixer_fill_color_bgra = fill_color_bgra;
753   gst_video_mixer_fill_color_abgr = fill_color_abgr;
754   gst_video_mixer_fill_color_rgba = fill_color_rgba;
755   gst_video_mixer_fill_color_ayuv = fill_color_ayuv;
756   gst_video_mixer_fill_color_i420 = fill_color_i420;
757   gst_video_mixer_fill_color_yv12 = fill_color_yv12;
758   gst_video_mixer_fill_color_y444 = fill_color_y444;
759   gst_video_mixer_fill_color_y42b = fill_color_y42b;
760   gst_video_mixer_fill_color_y41b = fill_color_y41b;
761   gst_video_mixer_fill_color_rgb = fill_color_rgb_c;
762   gst_video_mixer_fill_color_bgr = fill_color_bgr_c;
763   gst_video_mixer_fill_color_xrgb = fill_color_xrgb;
764   gst_video_mixer_fill_color_xbgr = fill_color_xbgr;
765   gst_video_mixer_fill_color_rgbx = fill_color_rgbx;
766   gst_video_mixer_fill_color_bgrx = fill_color_bgrx;
767   gst_video_mixer_fill_color_yuy2 = fill_color_yuy2;
768   gst_video_mixer_fill_color_yvyu = fill_color_yvyu;
769   gst_video_mixer_fill_color_uyvy = fill_color_uyvy;
770 }