tizen 2.0 init
[framework/multimedia/gst-plugins-good0.10.git] / gst / videobox / gstvideobox.c
1 /* GStreamer
2  * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
4  * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 /**
22  * SECTION:element-videobox
23  * @see_also: #GstVideoCrop
24  *
25  * This plugin crops or enlarges the image. It takes 4 values as input, a
26  * top, bottom, left and right offset. Positive values will crop that much
27  * pixels from the respective border of the image, negative values will add
28  * that much pixels. When pixels are added, you can specify their color. 
29  * Some predefined colors are usable with an enum property.
30  * 
31  * The plugin is alpha channel aware and will try to negotiate with a format
32  * that supports alpha channels first. When alpha channel is active two
33  * other properties, alpha and border_alpha can be used to set the alpha
34  * values of the inner picture and the border respectively. an alpha value of
35  * 0.0 means total transparency, 1.0 is opaque.
36  * 
37  * The videobox plugin has many uses such as doing a mosaic of pictures, 
38  * letterboxing video, cutting out pieces of video, picture in picture, etc..
39  *
40  * Setting autocrop to true changes the behavior of the plugin so that
41  * caps determine crop properties rather than the other way around: given
42  * input and output dimensions, the crop values are selected so that the
43  * smaller frame is effectively centered in the larger frame.  This
44  * involves either cropping or padding.
45  * 
46  * If you use autocrop there is little point in setting the other
47  * properties manually because they will be overriden if the caps change,
48  * but nothing stops you from doing so.
49  * 
50  * Sample pipeline:
51  * |[
52  * gst-launch videotestsrc ! videobox autocrop=true ! \
53  *   "video/x-raw-yuv, width=600, height=400" ! ffmpegcolorspace ! ximagesink
54  * ]|
55  */
56
57 #ifdef HAVE_CONFIG_H
58 #include "config.h"
59 #endif
60
61 #include "gstvideobox.h"
62 #include "gstvideoboxorc.h"
63
64 #include <math.h>
65 #include <string.h>
66
67 #include <gst/controller/gstcontroller.h>
68
69 #include "gst/glib-compat-private.h"
70
71 GST_DEBUG_CATEGORY_STATIC (videobox_debug);
72 #define GST_CAT_DEFAULT videobox_debug
73
74 /* From videotestsrc.c */
75 static const guint8 yuv_sdtv_colors_Y[VIDEO_BOX_FILL_LAST] =
76     { 16, 145, 41, 81, 210, 235 };
77 static const guint8 yuv_sdtv_colors_U[VIDEO_BOX_FILL_LAST] =
78     { 128, 54, 240, 90, 16, 128 };
79 static const guint8 yuv_sdtv_colors_V[VIDEO_BOX_FILL_LAST] =
80     { 128, 34, 110, 240, 146, 128 };
81
82 static const guint8 yuv_hdtv_colors_Y[VIDEO_BOX_FILL_LAST] =
83     { 16, 173, 32, 63, 219, 235 };
84 static const guint8 yuv_hdtv_colors_U[VIDEO_BOX_FILL_LAST] =
85     { 128, 42, 240, 102, 16, 128 };
86 static const guint8 yuv_hdtv_colors_V[VIDEO_BOX_FILL_LAST] =
87     { 128, 26, 118, 240, 138, 128 };
88
89 static const guint8 rgb_colors_R[VIDEO_BOX_FILL_LAST] =
90     { 0, 0, 0, 255, 255, 255 };
91 static const guint8 rgb_colors_G[VIDEO_BOX_FILL_LAST] =
92     { 0, 255, 0, 0, 255, 255 };
93 static const guint8 rgb_colors_B[VIDEO_BOX_FILL_LAST] =
94     { 0, 0, 255, 0, 0, 255 };
95
96 /* Generated by -bad/ext/cog/generate_tables */
97 static const int cog_ycbcr_to_rgb_matrix_8bit_hdtv[] = {
98   298, 0, 459, -63514,
99   298, -55, -136, 19681,
100   298, 541, 0, -73988,
101 };
102
103 static const int cog_ycbcr_to_rgb_matrix_8bit_sdtv[] = {
104   298, 0, 409, -57068,
105   298, -100, -208, 34707,
106   298, 516, 0, -70870,
107 };
108
109 static const gint cog_rgb_to_ycbcr_matrix_8bit_hdtv[] = {
110   47, 157, 16, 4096,
111   -26, -87, 112, 32768,
112   112, -102, -10, 32768,
113 };
114
115 static const gint cog_rgb_to_ycbcr_matrix_8bit_sdtv[] = {
116   66, 129, 25, 4096,
117   -38, -74, 112, 32768,
118   112, -94, -18, 32768,
119 };
120
121 static const gint cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit[] = {
122   256, -30, -53, 10600,
123   0, 261, 29, -4367,
124   0, 19, 262, -3289,
125 };
126
127 static const gint cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit[] = {
128   256, 25, 49, -9536,
129   0, 253, -28, 3958,
130   0, -19, 252, 2918,
131 };
132
133 static const gint cog_identity_matrix_8bit[] = {
134   256, 0, 0, 0,
135   0, 256, 0, 0,
136   0, 0, 256, 0,
137 };
138
139 #define APPLY_MATRIX(m,o,v1,v2,v3) ((m[o*4] * v1 + m[o*4+1] * v2 + m[o*4+2] * v3 + m[o*4+3]) >> 8)
140
141 static void
142 fill_ayuv (GstVideoBoxFill fill_type, guint b_alpha, GstVideoFormat format,
143     guint8 * dest, gboolean sdtv, gint width, gint height)
144 {
145   guint32 empty_pixel;
146
147   b_alpha = CLAMP (b_alpha, 0, 255);
148
149   if (sdtv)
150     empty_pixel = GUINT32_FROM_BE ((b_alpha << 24) |
151         (yuv_sdtv_colors_Y[fill_type] << 16) |
152         (yuv_sdtv_colors_U[fill_type] << 8) | yuv_sdtv_colors_V[fill_type]);
153   else
154     empty_pixel = GUINT32_FROM_BE ((b_alpha << 24) |
155         (yuv_hdtv_colors_Y[fill_type] << 16) |
156         (yuv_hdtv_colors_U[fill_type] << 8) | yuv_hdtv_colors_V[fill_type]);
157
158   orc_splat_u32 ((guint32 *) dest, empty_pixel, width * height);
159 }
160
161 static void
162 copy_ayuv_ayuv (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
163     gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
164     gint dest_y, GstVideoFormat src_format, const guint8 * src,
165     gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
166     gint w, gint h)
167 {
168   gint i, j;
169   gint src_stride = 4 * src_width;
170   gint dest_stride = 4 * dest_width;
171
172   dest = dest + dest_y * dest_width * 4 + dest_x * 4;
173   src = src + src_y * src_width * 4 + src_x * 4;
174
175   w *= 4;
176
177   if (dest_sdtv != src_sdtv) {
178     gint matrix[12];
179     gint y, u, v;
180
181     memcpy (matrix,
182         dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
183         cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
184
185     for (i = 0; i < h; i++) {
186       for (j = 0; j < w; j += 4) {
187         /* ORC FIXME */
188         dest[j] = (src[j] * i_alpha) >> 8;
189         y = src[j + 1];
190         u = src[j + 2];
191         v = src[j + 3];
192         dest[j + 1] = APPLY_MATRIX (matrix, 0, y, u, v);
193         dest[j + 2] = APPLY_MATRIX (matrix, 1, y, u, v);
194         dest[j + 3] = APPLY_MATRIX (matrix, 2, y, u, v);
195       }
196       dest += dest_stride;
197       src += src_stride;
198     }
199   } else {
200     for (i = 0; i < h; i++) {
201       for (j = 0; j < w; j += 4) {
202         /* ORC FIXME */
203         dest[j] = (src[j] * i_alpha) >> 8;
204         dest[j + 1] = src[j + 1];
205         dest[j + 2] = src[j + 2];
206         dest[j + 3] = src[j + 3];
207       }
208       dest += dest_stride;
209       src += src_stride;
210     }
211   }
212 }
213
214 static void
215 copy_ayuv_i420 (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
216     gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
217     gint dest_y, GstVideoFormat src_format, const guint8 * src,
218     gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
219     gint w, gint h)
220 {
221   gint i, j;
222   guint8 *destY, *destY2, *destU, *destV;
223   gint dest_strideY, dest_strideUV;
224   const guint8 *src2;
225   gint src_stride;
226   gint y_idx, uv_idx;
227   gint y1, y2, y3, y4;
228   gint u1, u2, u3, u4;
229   gint v1, v2, v3, v4;
230   gint matrix[12];
231
232   dest_strideY = gst_video_format_get_row_stride (dest_format, 0, dest_width);
233   dest_strideUV = gst_video_format_get_row_stride (dest_format, 1, dest_width);
234
235   src_stride = 4 * src_width;
236
237   destY =
238       dest + gst_video_format_get_component_offset (dest_format, 0,
239       dest_width, dest_height);
240   destU =
241       dest + gst_video_format_get_component_offset (dest_format, 1,
242       dest_width, dest_height);
243   destV =
244       dest + gst_video_format_get_component_offset (dest_format, 2,
245       dest_width, dest_height);
246
247   destY = destY + dest_y * dest_strideY + dest_x;
248   destY2 = (dest_y < dest_height) ? destY + dest_strideY : destY;
249   destU = destU + (dest_y / 2) * dest_strideUV + dest_x / 2;
250   destV = destV + (dest_y / 2) * dest_strideUV + dest_x / 2;
251
252   src = src + src_y * src_stride + src_x * 4;
253   src2 = (src_y < src_height) ? src + src_stride : src;
254
255   h = dest_y + h;
256   w = dest_x + w;
257
258   if (src_sdtv != dest_sdtv)
259     memcpy (matrix,
260         dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
261         cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
262   else
263     memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
264
265   /* 1. Handle the first destination scanline specially if it
266    *    doesn't start at the macro pixel boundary, i.e. blend
267    *    with the background! */
268   if (dest_y % 2 == 1) {
269     /* 1.1. Handle the first destination pixel if it doesn't
270      *      start at the macro pixel boundary, i.e. blend with
271      *      the background! */
272     if (dest_x % 2 == 1) {
273       y1 = src[4 * 0 + 1];
274       u1 = src[4 * 0 + 2];
275       v1 = src[4 * 0 + 3];
276
277       destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
278       destU[0] =
279           CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
280           255);
281       destV[0] =
282           CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
283           255);
284
285       j = dest_x + 1;
286       y_idx = uv_idx = 1;
287     } else {
288       j = dest_x;
289       y_idx = uv_idx = 0;
290     }
291
292     /* 1.2. Copy all macro pixels from the source to the destination
293      *      but blend with the background because we're only filling
294      *      the lower part of the macro pixels. */
295     for (; j < w - 1; j += 2) {
296       y1 = src[4 * y_idx + 1];
297       y2 = src[4 * y_idx + 4 + 1];
298
299       u1 = src[4 * y_idx + 2];
300       u2 = src[4 * y_idx + 4 + 2];
301
302       v1 = src[4 * y_idx + 3];
303       v2 = src[4 * y_idx + 4 + 3];
304
305       destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
306       destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
307       destU[uv_idx] = CLAMP (
308           (2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
309                   v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
310       destV[uv_idx] = CLAMP (
311           (2 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
312                   v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
313
314       y_idx += 2;
315       uv_idx++;
316     }
317
318     /* 1.3. Now copy the last pixel if one exists and blend it
319      *      with the background because we only fill part of
320      *      the macro pixel. In case this is the last pixel of
321      *      the destination we will a larger part. */
322     if (j == w - 1 && j == dest_width - 1) {
323       y1 = src[4 * y_idx + 1];
324       u1 = src[4 * y_idx + 2];
325       v1 = src[4 * y_idx + 3];
326
327       destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
328       destU[uv_idx] = CLAMP (
329           (destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
330       destV[uv_idx] = CLAMP (
331           (destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
332     } else if (j == w - 1) {
333       y1 = src[4 * y_idx + 1];
334       u1 = src[4 * y_idx + 2];
335       v1 = src[4 * y_idx + 3];
336
337       destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
338       destU[uv_idx] = CLAMP (
339           (3 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
340           255);
341       destV[uv_idx] =
342           CLAMP ((3 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4,
343           0, 255);
344     }
345
346     destY += dest_strideY;
347     destY2 += dest_strideY;
348     destU += dest_strideUV;
349     destV += dest_strideUV;
350     src += src_stride;
351     src2 += src_stride;
352     i = dest_y + 1;
353   } else {
354     i = dest_y;
355   }
356
357   /* 2. Copy all macro pixel scanlines, the destination scanline
358    *    now starts at macro pixel boundary. */
359   for (; i < h - 1; i += 2) {
360     /* 2.1. Handle the first destination pixel if it doesn't
361      *      start at the macro pixel boundary, i.e. blend with
362      *      the background! */
363     if (dest_x % 2 == 1) {
364       y1 = src[4 * 0 + 1];
365       y2 = src2[4 * 0 + 1];
366       u1 = src[4 * 0 + 2];
367       u2 = src2[4 * 0 + 2];
368       v1 = src[4 * 0 + 3];
369       v2 = src2[4 * 0 + 3];
370
371       destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
372       destY2[0] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
373       destU[0] = CLAMP (
374           (2 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
375                   v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
376       destV[0] = CLAMP (
377           (2 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
378                   v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
379       j = dest_x + 1;
380       y_idx = uv_idx = 1;
381     } else {
382       j = dest_x;
383       y_idx = uv_idx = 0;
384     }
385
386     /* 2.2. Copy all macro pixels from the source to the destination.
387      *      All pixels now start at macro pixel boundary, i.e. no
388      *      blending with the background is necessary. */
389     for (; j < w - 1; j += 2) {
390       y1 = src[4 * y_idx + 1];
391       y2 = src[4 * y_idx + 4 + 1];
392       y3 = src2[4 * y_idx + 1];
393       y4 = src2[4 * y_idx + 4 + 1];
394
395       u1 = src[4 * y_idx + 2];
396       u2 = src[4 * y_idx + 4 + 2];
397       u3 = src2[4 * y_idx + 2];
398       u4 = src2[4 * y_idx + 4 + 2];
399
400       v1 = src[4 * y_idx + 3];
401       v2 = src[4 * y_idx + 4 + 3];
402       v3 = src2[4 * y_idx + 3];
403       v4 = src2[4 * y_idx + 4 + 3];
404
405       destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
406       destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
407       destY2[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y3, u3, v3), 0, 255);
408       destY2[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y4, u4, v4), 0, 255);
409
410       destU[uv_idx] = CLAMP (
411           (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
412                   u2, v2) + APPLY_MATRIX (matrix, 1, y3, u3,
413                   v3) + APPLY_MATRIX (matrix, 1, y4, u4, v4)) / 4, 0, 255);
414       destV[uv_idx] = CLAMP (
415           (APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
416                   u2, v2) + APPLY_MATRIX (matrix, 2, y3, u3,
417                   v3) + APPLY_MATRIX (matrix, 2, y4, u4, v4)) / 4, 0, 255);
418
419       y_idx += 2;
420       uv_idx++;
421     }
422
423     /* 2.3. Now copy the last pixel if one exists and blend it
424      *      with the background because we only fill part of
425      *      the macro pixel. In case this is the last pixel of
426      *      the destination we will a larger part. */
427     if (j == w - 1 && j == dest_width - 1) {
428       y1 = src[4 * y_idx + 1];
429       y2 = src2[4 * y_idx + 1];
430
431       u1 = src[4 * y_idx + 2];
432       u2 = src2[4 * y_idx + 2];
433
434       v1 = src[4 * y_idx + 3];
435       v2 = src2[4 * y_idx + 3];
436
437       destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
438       destY2[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
439       destU[uv_idx] = CLAMP (
440           (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
441                   u2, v2)) / 2, 0, 255);
442       destV[uv_idx] = CLAMP (
443           (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
444                   u2, v2)) / 2, 0, 255);
445     } else if (j == w - 1) {
446       y1 = src[4 * y_idx + 1];
447       y2 = src2[4 * y_idx + 1];
448
449       u1 = src[4 * y_idx + 2];
450       u2 = src2[4 * y_idx + 2];
451
452       v1 = src[4 * y_idx + 3];
453       v2 = src2[4 * y_idx + 3];
454
455       destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
456       destY2[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
457       destU[uv_idx] = CLAMP (
458           (2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
459                   v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
460       destV[uv_idx] = CLAMP (
461           (2 * destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
462                   v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
463     }
464
465     destY += 2 * dest_strideY;
466     destY2 += 2 * dest_strideY;
467     destU += dest_strideUV;
468     destV += dest_strideUV;
469     src += 2 * src_stride;
470     src2 += 2 * src_stride;
471   }
472
473   /* 3. Handle the last scanline if one exists. This again
474    *    doesn't start at macro pixel boundary but should
475    *    only fill the upper part of the macro pixels. */
476   if (i == h - 1 && i == dest_height - 1) {
477     /* 3.1. Handle the first destination pixel if it doesn't
478      *      start at the macro pixel boundary, i.e. blend with
479      *      the background! */
480     if (dest_x % 2 == 1) {
481       y1 = src[4 * 0 + 1];
482       u1 = src[4 * 0 + 2];
483       v1 = src[4 * 0 + 3];
484
485       destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
486       destU[0] =
487           CLAMP ((destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
488       destV[0] =
489           CLAMP ((destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
490
491       j = dest_x + 1;
492       y_idx = uv_idx = 1;
493     } else {
494       j = dest_x;
495       y_idx = uv_idx = 0;
496     }
497
498     /* 3.2. Copy all macro pixels from the source to the destination
499      *      but blend with the background because we're only filling
500      *      the upper part of the macro pixels. */
501     for (; j < w - 1; j += 2) {
502       y1 = src[4 * y_idx + 1];
503       y2 = src[4 * y_idx + 4 + 1];
504
505       u1 = src[4 * y_idx + 2];
506       u2 = src[4 * y_idx + 4 + 2];
507
508       v1 = src[4 * y_idx + 3];
509       v2 = src[4 * y_idx + 4 + 3];
510
511       destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
512       destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
513
514       destU[uv_idx] = CLAMP (
515           (2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
516                   v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
517       destV[uv_idx] = CLAMP (
518           (2 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
519                   v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
520
521       y_idx += 2;
522       uv_idx++;
523     }
524
525     /* 3.3. Now copy the last pixel if one exists and blend it
526      *      with the background because we only fill part of
527      *      the macro pixel. In case this is the last pixel of
528      *      the destination we will a larger part. */
529     if (j == w - 1 && j == dest_width - 1) {
530       y1 = src[4 * y_idx + 1];
531       u1 = src[4 * y_idx + 2];
532       v1 = src[4 * y_idx + 3];
533
534       destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
535       destU[uv_idx] = CLAMP (
536           (destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
537       destV[uv_idx] = CLAMP (
538           (destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
539     } else if (j == w - 1) {
540       y1 = src[4 * y_idx + 1];
541       u1 = src[4 * y_idx + 2];
542       v1 = src[4 * y_idx + 3];
543
544       destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
545       destU[uv_idx] = CLAMP (
546           (3 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
547           255);
548       destV[uv_idx] =
549           CLAMP ((3 * destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
550           0, 255);
551     }
552   } else if (i == h - 1) {
553     /* 3.1. Handle the first destination pixel if it doesn't
554      *      start at the macro pixel boundary, i.e. blend with
555      *      the background! */
556     if (dest_x % 2 == 1) {
557       y1 = src[4 * 0 + 1];
558       u1 = src[4 * 0 + 2];
559       v1 = src[4 * 0 + 3];
560
561       destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
562       destU[0] =
563           CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
564           255);
565       destV[0] =
566           CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
567           255);
568
569       j = dest_x + 1;
570       y_idx = uv_idx = 1;
571     } else {
572       j = dest_x;
573       y_idx = uv_idx = 0;
574     }
575
576     /* 3.2. Copy all macro pixels from the source to the destination
577      *      but blend with the background because we're only filling
578      *      the upper part of the macro pixels. */
579     for (; j < w - 1; j += 2) {
580       y1 = src[4 * y_idx + 1];
581       y2 = src[4 * y_idx + 4 + 1];
582
583       u1 = src[4 * y_idx + 2];
584       u2 = src[4 * y_idx + 4 + 2];
585
586       v1 = src[4 * y_idx + 3];
587       v2 = src[4 * y_idx + 4 + 3];
588
589       destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
590       destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
591
592       destU[uv_idx] = CLAMP (
593           (2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
594                   v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
595       destV[uv_idx] = CLAMP (
596           (2 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
597                   v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
598
599       y_idx += 2;
600       uv_idx++;
601     }
602
603     /* 3.3. Now copy the last pixel if one exists and blend it
604      *      with the background because we only fill part of
605      *      the macro pixel. In case this is the last pixel of
606      *      the destination we will a larger part. */
607     if (j == w - 1 && j == dest_width - 1) {
608       y1 = src[4 * y_idx + 1];
609       u1 = src[4 * y_idx + 2];
610       v1 = src[4 * y_idx + 3];
611
612       destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
613       destU[uv_idx] = CLAMP (
614           (destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
615       destV[uv_idx] = CLAMP (
616           (destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
617     } else if (j == w - 1) {
618       y1 = src[4 * y_idx + 1];
619       u1 = src[4 * y_idx + 2];
620       v1 = src[4 * y_idx + 3];
621
622       destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
623       destU[uv_idx] = CLAMP (
624           (3 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
625           255);
626       destV[uv_idx] =
627           CLAMP ((3 * destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
628           0, 255);
629     }
630   }
631 }
632
633 static void
634 fill_planar_yuv (GstVideoBoxFill fill_type, guint b_alpha,
635     GstVideoFormat format, guint8 * dest, gboolean sdtv, gint width,
636     gint height)
637 {
638   guint8 empty_pixel[3];
639   guint8 *destY, *destU, *destV;
640   gint strideY, strideUV;
641   gint heightY, heightUV;
642
643   if (sdtv) {
644     empty_pixel[0] = yuv_sdtv_colors_Y[fill_type];
645     empty_pixel[1] = yuv_sdtv_colors_U[fill_type];
646     empty_pixel[2] = yuv_sdtv_colors_V[fill_type];
647   } else {
648     empty_pixel[0] = yuv_hdtv_colors_Y[fill_type];
649     empty_pixel[1] = yuv_hdtv_colors_U[fill_type];
650     empty_pixel[2] = yuv_hdtv_colors_V[fill_type];
651   }
652
653   strideY = gst_video_format_get_row_stride (format, 0, width);
654   strideUV = gst_video_format_get_row_stride (format, 1, width);
655
656   destY =
657       dest + gst_video_format_get_component_offset (format, 0, width, height);
658   destU =
659       dest + gst_video_format_get_component_offset (format, 1, width, height);
660   destV =
661       dest + gst_video_format_get_component_offset (format, 2, width, height);
662
663   heightY = gst_video_format_get_component_height (format, 0, height);
664   heightUV = gst_video_format_get_component_height (format, 1, height);
665
666   memset (destY, empty_pixel[0], strideY * heightY);
667   memset (destU, empty_pixel[1], strideUV * heightUV);
668   memset (destV, empty_pixel[2], strideUV * heightUV);
669 }
670
671 static void
672 copy_y444_y444 (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
673     gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
674     gint dest_y, GstVideoFormat src_format, const guint8 * src,
675     gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
676     gint w, gint h)
677 {
678   gint i, j;
679   guint8 *destY, *destU, *destV;
680   const guint8 *srcY, *srcU, *srcV;
681   gint dest_stride;
682   gint src_stride;
683
684   dest_stride = gst_video_format_get_row_stride (dest_format, 0, dest_width);
685   src_stride = gst_video_format_get_row_stride (src_format, 0, src_width);
686
687   destY =
688       dest + gst_video_format_get_component_offset (dest_format, 0,
689       dest_width, dest_height);
690   destU =
691       dest + gst_video_format_get_component_offset (dest_format, 1,
692       dest_width, dest_height);
693   destV =
694       dest + gst_video_format_get_component_offset (dest_format, 2,
695       dest_width, dest_height);
696
697   srcY =
698       src + gst_video_format_get_component_offset (src_format, 0,
699       src_width, src_height);
700   srcU =
701       src + gst_video_format_get_component_offset (src_format, 1,
702       src_width, src_height);
703   srcV =
704       src + gst_video_format_get_component_offset (src_format, 2,
705       src_width, src_height);
706
707   destY = destY + dest_y * dest_stride + dest_x;
708   destU = destU + dest_y * dest_stride + dest_x;
709   destV = destV + dest_y * dest_stride + dest_x;
710
711   srcY = srcY + src_y * src_stride + src_x;
712   srcU = srcU + src_y * src_stride + src_x;
713   srcV = srcV + src_y * src_stride + src_x;
714
715   if (src_sdtv != dest_sdtv) {
716     gint matrix[12];
717     gint y, u, v;
718
719     memcpy (matrix,
720         dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
721         cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
722
723     for (i = 0; i < h; i++) {
724       for (j = 0; j < w; j++) {
725         y = APPLY_MATRIX (matrix, 0, srcY[j], srcU[j], srcV[j]);
726         u = APPLY_MATRIX (matrix, 1, srcY[j], srcU[j], srcV[j]);
727         v = APPLY_MATRIX (matrix, 2, srcY[j], srcU[j], srcV[j]);
728
729         destY[j] = y;
730         destU[j] = u;
731         destV[j] = v;
732       }
733       destY += dest_stride;
734       destU += dest_stride;
735       destV += dest_stride;
736
737       srcY += src_stride;
738       srcU += src_stride;
739       srcV += src_stride;
740     }
741   } else {
742     for (i = 0; i < h; i++) {
743       memcpy (destY, srcY, w);
744       memcpy (destU, srcU, w);
745       memcpy (destV, srcV, w);
746
747       destY += dest_stride;
748       destU += dest_stride;
749       destV += dest_stride;
750
751       srcY += src_stride;
752       srcU += src_stride;
753       srcV += src_stride;
754     }
755   }
756 }
757
758 static void
759 copy_y42b_y42b (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
760     gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
761     gint dest_y, GstVideoFormat src_format, const guint8 * src,
762     gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
763     gint w, gint h)
764 {
765   gint i, j;
766   guint8 *destY, *destU, *destV;
767   const guint8 *srcY, *srcU, *srcV;
768   gint dest_strideY, dest_strideUV;
769   gint src_strideY, src_strideUV;
770   gint src_y_idx, src_uv_idx;
771   gint dest_y_idx, dest_uv_idx;
772   gint matrix[12];
773   gint y1, y2;
774   gint u1, u2;
775   gint v1, v2;
776
777   dest_strideY = gst_video_format_get_row_stride (dest_format, 0, dest_width);
778   dest_strideUV = gst_video_format_get_row_stride (dest_format, 1, dest_width);
779   src_strideY = gst_video_format_get_row_stride (src_format, 0, src_width);
780   src_strideUV = gst_video_format_get_row_stride (src_format, 1, src_width);
781
782   destY =
783       dest + gst_video_format_get_component_offset (dest_format, 0,
784       dest_width, dest_height);
785   destU =
786       dest + gst_video_format_get_component_offset (dest_format, 1,
787       dest_width, dest_height);
788   destV =
789       dest + gst_video_format_get_component_offset (dest_format, 2,
790       dest_width, dest_height);
791
792   srcY =
793       src + gst_video_format_get_component_offset (src_format, 0,
794       src_width, src_height);
795   srcU =
796       src + gst_video_format_get_component_offset (src_format, 1,
797       src_width, src_height);
798   srcV =
799       src + gst_video_format_get_component_offset (src_format, 2,
800       src_width, src_height);
801
802
803   destY = destY + dest_y * dest_strideY + dest_x;
804   destU = destU + dest_y * dest_strideUV + dest_x / 2;
805   destV = destV + dest_y * dest_strideUV + dest_x / 2;
806
807   srcY = srcY + src_y * src_strideY + src_x;
808   srcU = srcU + src_y * src_strideUV + src_x / 2;
809   srcV = srcV + src_y * src_strideUV + src_x / 2;
810
811   h = dest_y + h;
812   w = dest_x + w;
813
814   if (src_sdtv != dest_sdtv)
815     memcpy (matrix,
816         dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
817         cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
818   else
819     memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
820
821   /* 1. Copy all macro pixel scanlines, the destination scanline
822    *    now starts at macro pixel boundary. */
823   for (i = dest_y; i < h; i++) {
824     /* 1.1. Handle the first destination pixel if it doesn't
825      *      start at the macro pixel boundary, i.e. blend with
826      *      the background! */
827     if (dest_x % 2 == 1) {
828       y1 = srcY[0];
829       u1 = srcU[0];
830       v1 = srcV[0];
831
832       destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
833       destU[0] = CLAMP (
834           (destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
835       destV[0] = CLAMP (
836           (destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
837       j = dest_x + 1;
838       src_y_idx = dest_y_idx = dest_uv_idx = 1;
839       src_uv_idx = (src_x % 2) + 1;
840     } else {
841       j = dest_x;
842       src_y_idx = dest_y_idx = dest_uv_idx = 0;
843       src_uv_idx = (src_x % 2);
844     }
845
846     /* 1.2. Copy all macro pixels from the source to the destination.
847      *      All pixels now start at macro pixel boundary, i.e. no
848      *      blending with the background is necessary. */
849     for (; j < w - 1; j += 2) {
850       y1 = srcY[src_y_idx];
851       y2 = srcY[src_y_idx + 1];
852
853       u1 = srcU[src_uv_idx / 2];
854       v1 = srcV[src_uv_idx / 2];
855       src_uv_idx++;
856       u2 = srcU[src_uv_idx / 2];
857       v2 = srcV[src_uv_idx / 2];
858       src_uv_idx++;
859
860       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
861       destY[dest_y_idx + 1] =
862           CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
863
864       destU[dest_uv_idx] = CLAMP (
865           (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
866                   u2, v2)) / 2, 0, 255);
867       destV[dest_uv_idx] = CLAMP (
868           (APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
869                   u2, v2)) / 2, 0, 255);
870
871       dest_y_idx += 2;
872       src_y_idx += 2;
873       dest_uv_idx++;
874     }
875
876     /* 1.3. Now copy the last pixel if one exists and blend it
877      *      with the background because we only fill part of
878      *      the macro pixel. In case this is the last pixel of
879      *      the destination we will a larger part. */
880     if (j == w - 1 && j == dest_width - 1) {
881       y1 = srcY[src_y_idx];
882       u1 = srcU[src_uv_idx / 2];
883       v1 = srcV[src_uv_idx / 2];
884
885       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
886       destU[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
887       destV[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
888     } else if (j == w - 1) {
889       y1 = srcY[src_y_idx];
890       u1 = srcU[src_uv_idx / 2];
891       v1 = srcV[src_uv_idx / 2];
892
893       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
894       destU[dest_uv_idx] = CLAMP (
895           (destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
896                   v1)) / 2, 0, 255);
897       destV[dest_uv_idx] = CLAMP (
898           (destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
899                   v1)) / 2, 0, 255);
900     }
901
902     destY += dest_strideY;
903     destU += dest_strideUV;
904     destV += dest_strideUV;
905     srcY += src_strideY;
906
907     srcU += src_strideUV;
908     srcV += src_strideUV;
909   }
910 }
911
912 static void
913 copy_y41b_y41b (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
914     gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
915     gint dest_y, GstVideoFormat src_format, const guint8 * src,
916     gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
917     gint w, gint h)
918 {
919   gint i, j;
920   guint8 *destY, *destU, *destV;
921   const guint8 *srcY, *srcU, *srcV;
922   gint dest_strideY, dest_strideUV;
923   gint src_strideY, src_strideUV;
924   gint src_y_idx, src_uv_idx;
925   gint dest_y_idx, dest_uv_idx;
926   gint matrix[12];
927   gint y1, y2, y3, y4;
928   gint u1, u2, u3, u4;
929   gint v1, v2, v3, v4;
930
931   dest_strideY = gst_video_format_get_row_stride (dest_format, 0, dest_width);
932   dest_strideUV = gst_video_format_get_row_stride (dest_format, 1, dest_width);
933   src_strideY = gst_video_format_get_row_stride (src_format, 0, src_width);
934   src_strideUV = gst_video_format_get_row_stride (src_format, 1, src_width);
935
936   destY =
937       dest + gst_video_format_get_component_offset (dest_format, 0,
938       dest_width, dest_height);
939   destU =
940       dest + gst_video_format_get_component_offset (dest_format, 1,
941       dest_width, dest_height);
942   destV =
943       dest + gst_video_format_get_component_offset (dest_format, 2,
944       dest_width, dest_height);
945
946   srcY =
947       src + gst_video_format_get_component_offset (src_format, 0,
948       src_width, src_height);
949   srcU =
950       src + gst_video_format_get_component_offset (src_format, 1,
951       src_width, src_height);
952   srcV =
953       src + gst_video_format_get_component_offset (src_format, 2,
954       src_width, src_height);
955
956
957   destY = destY + dest_y * dest_strideY + dest_x;
958   destU = destU + dest_y * dest_strideUV + dest_x / 4;
959   destV = destV + dest_y * dest_strideUV + dest_x / 4;
960
961   srcY = srcY + src_y * src_strideY + src_x;
962   srcU = srcU + src_y * src_strideUV + src_x / 4;
963   srcV = srcV + src_y * src_strideUV + src_x / 4;
964
965   h = dest_y + h;
966   w = dest_x + w;
967
968   if (src_sdtv != dest_sdtv)
969     memcpy (matrix,
970         dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
971         cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
972   else
973     memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
974
975   /* 1. Copy all macro pixel scanlines, the destination scanline
976    *    now starts at macro pixel boundary. */
977   for (i = dest_y; i < h; i++) {
978     /* 1.1. Handle the first destination pixel if it doesn't
979      *      start at the macro pixel boundary, i.e. blend with
980      *      the background! */
981     if (dest_x % 4 == 1) {
982       y1 = srcY[0];
983       y2 = srcY[1];
984       y3 = srcY[2];
985       u1 = srcU[0];
986       v1 = srcV[0];
987
988       destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
989       destY[1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
990       destY[2] = CLAMP (APPLY_MATRIX (matrix, 0, y3, u1, v1), 0, 255);
991
992       destU[0] = CLAMP (
993           (destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
994                   v1) + APPLY_MATRIX (matrix, 1, y2, u1,
995                   v1) + APPLY_MATRIX (matrix, 1, y3, u1, v1)) / 4, 0, 255);
996       destV[0] =
997           CLAMP ((destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
998                   v1) + APPLY_MATRIX (matrix, 2, y2, u1,
999                   v1) + APPLY_MATRIX (matrix, 2, y3, u1, v1)) / 4, 0, 255);
1000
1001       j = dest_x + 3;
1002       src_y_idx = dest_y_idx = 3;
1003       dest_uv_idx = 1;
1004       src_uv_idx = (src_x % 4) + 3;
1005     } else if (dest_x % 4 == 2) {
1006       y1 = srcY[0];
1007       y2 = srcY[1];
1008       u1 = srcU[0];
1009       v1 = srcV[0];
1010
1011       destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1012       destY[1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
1013
1014       destU[0] = CLAMP (
1015           (2 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
1016                   v1) + APPLY_MATRIX (matrix, 1, y2, u1, v1)) / 4, 0, 255);
1017       destV[0] =
1018           CLAMP ((2 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
1019                   v1) + APPLY_MATRIX (matrix, 2, y2, u1, v1)) / 4, 0, 255);
1020
1021       j = dest_x + 2;
1022       src_y_idx = dest_y_idx = 2;
1023       dest_uv_idx = 1;
1024       src_uv_idx = (src_x % 4) + 2;
1025     } else if (dest_x % 4 == 3) {
1026       y1 = srcY[0];
1027       u1 = srcU[0];
1028       v1 = srcV[0];
1029
1030       destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1031
1032       destU[0] = CLAMP (
1033           (3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0, 255);
1034       destV[0] = CLAMP (
1035           (3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0, 255);
1036
1037       j = dest_x + 1;
1038       src_y_idx = dest_y_idx = 1;
1039       dest_uv_idx = 1;
1040       src_uv_idx = (src_x % 4) + 1;
1041     } else {
1042       j = dest_x;
1043       src_y_idx = dest_y_idx = dest_uv_idx = 0;
1044       src_uv_idx = (src_x % 4);
1045     }
1046
1047     /* 1.2. Copy all macro pixels from the source to the destination.
1048      *      All pixels now start at macro pixel boundary, i.e. no
1049      *      blending with the background is necessary. */
1050     for (; j < w - 3; j += 4) {
1051       y1 = srcY[src_y_idx];
1052       y2 = srcY[src_y_idx + 1];
1053       y3 = srcY[src_y_idx + 2];
1054       y4 = srcY[src_y_idx + 3];
1055
1056       u1 = srcU[src_uv_idx / 4];
1057       v1 = srcV[src_uv_idx / 4];
1058       src_uv_idx++;
1059       u2 = srcU[src_uv_idx / 4];
1060       v2 = srcV[src_uv_idx / 4];
1061       src_uv_idx++;
1062       u3 = srcU[src_uv_idx / 4];
1063       v3 = srcV[src_uv_idx / 4];
1064       src_uv_idx++;
1065       u4 = srcU[src_uv_idx / 4];
1066       v4 = srcV[src_uv_idx / 4];
1067       src_uv_idx++;
1068
1069       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1070       destY[dest_y_idx + 1] =
1071           CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
1072       destY[dest_y_idx + 2] =
1073           CLAMP (APPLY_MATRIX (matrix, 0, y3, u3, v3), 0, 255);
1074       destY[dest_y_idx + 3] =
1075           CLAMP (APPLY_MATRIX (matrix, 0, y4, u4, v4), 0, 255);
1076
1077       destU[dest_uv_idx] = CLAMP (
1078           (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
1079                   u2, v2) + APPLY_MATRIX (matrix, 1, y3, u3,
1080                   v3) + APPLY_MATRIX (matrix, 1, y4, u4, v4)) / 4, 0, 255);
1081       destV[dest_uv_idx] =
1082           CLAMP ((APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix,
1083                   2, y2, u2, v2) + APPLY_MATRIX (matrix, 2, y3, u3,
1084                   v3) + APPLY_MATRIX (matrix, 2, y4, u4, v4)) / 4, 0, 255);
1085
1086       dest_y_idx += 4;
1087       src_y_idx += 4;
1088       dest_uv_idx++;
1089     }
1090
1091     /* 1.3. Now copy the last pixel if one exists and blend it
1092      *      with the background because we only fill part of
1093      *      the macro pixel. In case this is the last pixel of
1094      *      the destination we will a larger part. */
1095     if (j == w - 1 && j == dest_width - 1) {
1096       y1 = srcY[src_y_idx];
1097       u1 = srcU[src_uv_idx / 4];
1098       v1 = srcV[src_uv_idx / 4];
1099
1100       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1101       destU[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
1102       destV[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
1103     } else if (j == w - 1) {
1104       y1 = srcY[src_y_idx];
1105       u1 = srcU[src_uv_idx / 4];
1106       v1 = srcV[src_uv_idx / 4];
1107
1108       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1109       destU[dest_uv_idx] = CLAMP (
1110           (destU[dest_uv_idx] + 3 * APPLY_MATRIX (matrix, 1, y1, u1,
1111                   v1)) / 4, 0, 255);
1112       destV[dest_uv_idx] = CLAMP (
1113           (destV[dest_uv_idx] + 3 * APPLY_MATRIX (matrix, 1, y1, u1,
1114                   v1)) / 4, 0, 255);
1115     } else if (j == w - 2 && j == dest_width - 2) {
1116       y1 = srcY[src_y_idx];
1117       y2 = srcY[src_y_idx + 1];
1118       u1 = srcU[src_uv_idx / 4];
1119       v1 = srcV[src_uv_idx / 4];
1120
1121       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1122       destY[dest_y_idx + 1] =
1123           CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
1124       destU[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
1125       destV[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
1126     } else if (j == w - 2) {
1127       y1 = srcY[src_y_idx];
1128       y2 = srcY[src_y_idx + 1];
1129       u1 = srcU[src_uv_idx / 4];
1130       v1 = srcV[src_uv_idx / 4];
1131
1132       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1133       destY[dest_y_idx + 1] =
1134           CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
1135       destU[dest_uv_idx] =
1136           CLAMP ((destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1137                   v1)) / 2, 0, 255);
1138       destV[dest_uv_idx] =
1139           CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1140                   v1)) / 2, 0, 255);
1141     } else if (j == w - 3 && j == dest_width - 3) {
1142       y1 = srcY[src_y_idx];
1143       y2 = srcY[src_y_idx + 1];
1144       y3 = srcY[src_y_idx + 2];
1145       u1 = srcU[src_uv_idx / 4];
1146       v1 = srcV[src_uv_idx / 4];
1147
1148       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1149       destY[dest_y_idx + 1] =
1150           CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
1151       destY[dest_y_idx + 2] =
1152           CLAMP (APPLY_MATRIX (matrix, 0, y3, u1, v1), 0, 255);
1153       destU[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
1154       destV[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
1155     } else if (j == w - 3) {
1156       y1 = srcY[src_y_idx];
1157       y2 = srcY[src_y_idx + 1];
1158       y3 = srcY[src_y_idx + 2];
1159       u1 = srcU[src_uv_idx / 4];
1160       v1 = srcV[src_uv_idx / 4];
1161
1162       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1163       destY[dest_y_idx + 1] =
1164           CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
1165       destY[dest_y_idx + 2] =
1166           CLAMP (APPLY_MATRIX (matrix, 0, y3, u1, v1), 0, 255);
1167       destU[dest_uv_idx] =
1168           CLAMP ((3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1169                   v1)) / 4, 0, 255);
1170       destV[dest_uv_idx] =
1171           CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1172                   v1)) / 4, 0, 255);
1173     }
1174
1175     destY += dest_strideY;
1176     destU += dest_strideUV;
1177     destV += dest_strideUV;
1178     srcY += src_strideY;
1179     srcU += src_strideUV;
1180     srcV += src_strideUV;
1181   }
1182 }
1183
1184 static void
1185 copy_i420_i420 (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
1186     gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
1187     gint dest_y, GstVideoFormat src_format, const guint8 * src,
1188     gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
1189     gint w, gint h)
1190 {
1191   gint i, j;
1192   guint8 *destY, *destU, *destV;
1193   const guint8 *srcY, *srcU, *srcV;
1194   guint8 *destY2;
1195   const guint8 *srcY2, *srcU2, *srcV2;
1196   gint dest_strideY, dest_strideUV;
1197   gint src_strideY, src_strideUV;
1198   gint src_y_idx, src_uv_idx;
1199   gint dest_y_idx, dest_uv_idx;
1200   gint matrix[12];
1201   gint y1, y2, y3, y4;
1202   gint u1, u2, u3, u4;
1203   gint v1, v2, v3, v4;
1204
1205   dest_strideY =
1206       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, dest_width);
1207   dest_strideUV =
1208       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, dest_width);
1209   src_strideY =
1210       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, src_width);
1211   src_strideUV =
1212       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, src_width);
1213
1214   destY =
1215       dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
1216       dest_width, dest_height);
1217   destU =
1218       dest + gst_video_format_get_component_offset (dest_format, 1,
1219       dest_width, dest_height);
1220   destV =
1221       dest + gst_video_format_get_component_offset (dest_format, 2,
1222       dest_width, dest_height);
1223
1224   srcY =
1225       src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
1226       src_width, src_height);
1227   srcU =
1228       src + gst_video_format_get_component_offset (src_format, 1,
1229       src_width, src_height);
1230   srcV =
1231       src + gst_video_format_get_component_offset (src_format, 2,
1232       src_width, src_height);
1233
1234
1235   destY = destY + dest_y * dest_strideY + dest_x;
1236   destU = destU + (dest_y / 2) * dest_strideUV + dest_x / 2;
1237   destV = destV + (dest_y / 2) * dest_strideUV + dest_x / 2;
1238
1239   srcY = srcY + src_y * src_strideY + src_x;
1240   srcU = srcU + (src_y / 2) * src_strideUV + src_x / 2;
1241   srcV = srcV + (src_y / 2) * src_strideUV + src_x / 2;
1242
1243   destY2 = destY + dest_strideY;
1244   srcY2 = srcY + src_strideY;
1245
1246   h = dest_y + h;
1247   w = dest_x + w;
1248
1249   if (src_sdtv != dest_sdtv)
1250     memcpy (matrix,
1251         dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
1252         cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
1253   else
1254     memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
1255
1256   /* 1. Handle the first destination scanline specially if it
1257    *    doesn't start at the macro pixel boundary, i.e. blend
1258    *    with the background! */
1259   if (dest_y % 2 == 1) {
1260     /* 1.1. Handle the first destination pixel if it doesn't
1261      *      start at the macro pixel boundary, i.e. blend with
1262      *      the background! */
1263     if (dest_x % 2 == 1) {
1264       y1 = srcY[0];
1265       u1 = srcU[0];
1266       v1 = srcV[0];
1267
1268       destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1269       destU[0] =
1270           CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
1271           255);
1272       destV[0] =
1273           CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
1274           255);
1275
1276       j = dest_x + 1;
1277       src_y_idx = dest_y_idx = dest_uv_idx = 1;
1278       src_uv_idx = (src_x % 2) + 1;
1279     } else {
1280       j = dest_x;
1281       src_y_idx = dest_y_idx = dest_uv_idx = 0;
1282       src_uv_idx = (src_x % 2);
1283     }
1284
1285     /* 1.2. Copy all macro pixels from the source to the destination
1286      *      but blend with the background because we're only filling
1287      *      the lower part of the macro pixels. */
1288     for (; j < w - 1; j += 2) {
1289       y1 = srcY[src_y_idx];
1290       y2 = srcY[src_y_idx + 1];
1291
1292       u1 = srcU[src_uv_idx / 2];
1293       v1 = srcV[src_uv_idx / 2];
1294       src_uv_idx++;
1295       u2 = srcU[src_uv_idx / 2];
1296       v2 = srcV[src_uv_idx / 2];
1297       src_uv_idx++;
1298
1299       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1300       destY[dest_y_idx + 1] =
1301           CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
1302       destU[dest_uv_idx] =
1303           CLAMP ((2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1304                   v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
1305       destV[dest_uv_idx] =
1306           CLAMP ((2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
1307                   v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
1308
1309       dest_y_idx += 2;
1310       src_y_idx += 2;
1311       dest_uv_idx++;
1312     }
1313
1314     /* 1.3. Now copy the last pixel if one exists and blend it
1315      *      with the background because we only fill part of
1316      *      the macro pixel. In case this is the last pixel of
1317      *      the destination we will a larger part. */
1318     if (j == w - 1 && j == dest_width - 1) {
1319       y1 = srcY[src_y_idx];
1320       u1 = srcU[src_uv_idx / 2];
1321       v1 = srcV[src_uv_idx / 2];
1322
1323       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1324       destU[dest_uv_idx] = CLAMP (
1325           (destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0,
1326           255);
1327       destV[dest_uv_idx] =
1328           CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
1329                   v1)) / 2, 0, 255);
1330     } else if (j == w - 1) {
1331       y1 = srcY[src_y_idx];
1332       u1 = srcU[src_uv_idx / 2];
1333       v1 = srcV[src_uv_idx / 2];
1334
1335       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1336       destU[dest_uv_idx] = CLAMP (
1337           (3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
1338           0, 255);
1339       destV[dest_uv_idx] =
1340           CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
1341                   v1)) / 4, 0, 255);
1342     }
1343
1344     destY += dest_strideY;
1345     destY2 += dest_strideY;
1346     destU += dest_strideUV;
1347     destV += dest_strideUV;
1348     srcY += src_strideY;
1349     srcY2 += src_strideY;
1350     src_y++;
1351     if (src_y % 2 == 0) {
1352       srcU += src_strideUV;
1353       srcV += src_strideUV;
1354     }
1355     i = dest_y + 1;
1356   } else {
1357     i = dest_y;
1358   }
1359
1360   /* 2. Copy all macro pixel scanlines, the destination scanline
1361    *    now starts at macro pixel boundary. */
1362   for (; i < h - 1; i += 2) {
1363     /* 2.1. Handle the first destination pixel if it doesn't
1364      *      start at the macro pixel boundary, i.e. blend with
1365      *      the background! */
1366
1367     srcU2 = srcU;
1368     srcV2 = srcV;
1369     if (src_y % 2 == 1) {
1370       srcU2 += src_strideUV;
1371       srcV2 += src_strideUV;
1372     }
1373
1374     if (dest_x % 2 == 1) {
1375       y1 = srcY[0];
1376       y2 = srcY2[0];
1377       u1 = srcU[0];
1378       v1 = srcV[0];
1379       u2 = srcU2[0];
1380       v2 = srcV2[0];
1381
1382       destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1383       destY2[0] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
1384       destU[0] = CLAMP (
1385           (2 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
1386                   v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
1387       destV[0] = CLAMP (
1388           (2 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
1389                   v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
1390       j = dest_x + 1;
1391       src_y_idx = dest_y_idx = dest_uv_idx = 1;
1392       src_uv_idx = (src_x % 2) + 1;
1393     } else {
1394       j = dest_x;
1395       src_y_idx = dest_y_idx = dest_uv_idx = 0;
1396       src_uv_idx = (src_x % 2);
1397     }
1398
1399     /* 2.2. Copy all macro pixels from the source to the destination.
1400      *      All pixels now start at macro pixel boundary, i.e. no
1401      *      blending with the background is necessary. */
1402     for (; j < w - 1; j += 2) {
1403       y1 = srcY[src_y_idx];
1404       y2 = srcY[src_y_idx + 1];
1405       y3 = srcY2[src_y_idx];
1406       y4 = srcY2[src_y_idx + 1];
1407
1408       u1 = srcU[src_uv_idx / 2];
1409       u3 = srcU2[src_uv_idx / 2];
1410       v1 = srcV[src_uv_idx / 2];
1411       v3 = srcV2[src_uv_idx / 2];
1412       src_uv_idx++;
1413       u2 = srcU[src_uv_idx / 2];
1414       u4 = srcU2[src_uv_idx / 2];
1415       v2 = srcV[src_uv_idx / 2];
1416       v4 = srcV2[src_uv_idx / 2];
1417       src_uv_idx++;
1418
1419       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1420       destY[dest_y_idx + 1] =
1421           CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
1422       destY2[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y3, u3, v3), 0, 255);
1423       destY2[dest_y_idx + 1] =
1424           CLAMP (APPLY_MATRIX (matrix, 0, y4, u4, v4), 0, 255);
1425
1426       destU[dest_uv_idx] = CLAMP (
1427           (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
1428                   u2, v2) + APPLY_MATRIX (matrix, 1, y3, u3,
1429                   v3) + APPLY_MATRIX (matrix, 1, y4, u4, v4)) / 4, 0, 255);
1430       destV[dest_uv_idx] = CLAMP (
1431           (APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
1432                   u2, v2) + APPLY_MATRIX (matrix, 2, y3, u3,
1433                   v3) + APPLY_MATRIX (matrix, 2, y4, u4, v4)) / 4, 0, 255);
1434
1435       dest_y_idx += 2;
1436       src_y_idx += 2;
1437       dest_uv_idx++;
1438     }
1439
1440     /* 2.3. Now copy the last pixel if one exists and blend it
1441      *      with the background because we only fill part of
1442      *      the macro pixel. In case this is the last pixel of
1443      *      the destination we will a larger part. */
1444     if (j == w - 1 && j == dest_width - 1) {
1445       y1 = srcY[src_y_idx];
1446       y2 = srcY2[src_y_idx];
1447
1448       u1 = srcU[src_uv_idx / 2];
1449       u2 = srcU2[src_uv_idx / 2];
1450
1451       v1 = srcV[src_uv_idx / 2];
1452       v2 = srcV2[src_uv_idx / 2];
1453
1454       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1455       destY2[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
1456       destU[dest_uv_idx] = CLAMP (
1457           (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
1458                   u2, v2)) / 2, 0, 255);
1459       destV[dest_uv_idx] = CLAMP (
1460           (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
1461                   u2, v2)) / 2, 0, 255);
1462     } else if (j == w - 1) {
1463       y1 = srcY[src_y_idx];
1464       y2 = srcY2[src_y_idx];
1465
1466       u1 = srcU[src_uv_idx / 2];
1467       u2 = srcU2[src_uv_idx / 2];
1468
1469       v1 = srcV[src_uv_idx / 2];
1470       v2 = srcV2[src_uv_idx / 2];
1471
1472       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1473       destY2[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
1474       destU[dest_uv_idx] = CLAMP (
1475           (2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1476                   v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
1477       destV[dest_uv_idx] = CLAMP (
1478           (2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1479                   v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
1480     }
1481
1482     destY += 2 * dest_strideY;
1483     destY2 += 2 * dest_strideY;
1484     destU += dest_strideUV;
1485     destV += dest_strideUV;
1486     srcY += 2 * src_strideY;
1487     srcY2 += 2 * src_strideY;
1488
1489     src_y += 2;
1490     srcU += src_strideUV;
1491     srcV += src_strideUV;
1492   }
1493
1494   /* 3. Handle the last scanline if one exists. This again
1495    *    doesn't start at macro pixel boundary but should
1496    *    only fill the upper part of the macro pixels. */
1497   if (i == h - 1 && i == dest_height - 1) {
1498     /* 3.1. Handle the first destination pixel if it doesn't
1499      *      start at the macro pixel boundary, i.e. blend with
1500      *      the background! */
1501     if (dest_x % 2 == 1) {
1502       y1 = srcY[0];
1503       u1 = srcU[0];
1504       v1 = srcV[0];
1505
1506       destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1507       destU[0] =
1508           CLAMP ((destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
1509       destV[0] =
1510           CLAMP ((destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
1511
1512       j = dest_x + 1;
1513       src_y_idx = dest_y_idx = dest_uv_idx = 1;
1514       src_uv_idx = (src_x % 2) + 1;
1515     } else {
1516       j = dest_x;
1517       src_y_idx = dest_y_idx = dest_uv_idx = 0;
1518       src_uv_idx = (src_x % 2);
1519     }
1520
1521     /* 3.2. Copy all macro pixels from the source to the destination
1522      *      but blend with the background because we're only filling
1523      *      the upper part of the macro pixels. */
1524     for (; j < w - 1; j += 2) {
1525       y1 = srcY[src_y_idx];
1526       y2 = srcY[src_y_idx + 1];
1527
1528       u1 = srcU[src_uv_idx / 2];
1529       v1 = srcV[src_uv_idx / 2];
1530       src_uv_idx++;
1531       u2 = srcU[src_uv_idx / 2];
1532       v2 = srcV[src_uv_idx / 2];
1533       src_uv_idx++;
1534
1535       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1536       destY[dest_y_idx + 1] =
1537           CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
1538
1539       destU[dest_uv_idx] = CLAMP (
1540           (2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1541                   v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
1542       destV[dest_uv_idx] = CLAMP (
1543           (2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
1544                   v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
1545
1546       dest_y_idx += 2;
1547       src_y_idx += 2;
1548       dest_uv_idx++;
1549     }
1550
1551     /* 3.3. Now copy the last pixel if one exists and blend it
1552      *      with the background because we only fill part of
1553      *      the macro pixel. In case this is the last pixel of
1554      *      the destination we will a larger part. */
1555     if (j == w - 1 && j == dest_width - 1) {
1556       y1 = srcY[src_y_idx];
1557       u1 = srcU[src_uv_idx / 2];
1558       v1 = srcV[src_uv_idx / 2];
1559
1560       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1561       destU[dest_uv_idx] = CLAMP (
1562           (destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0,
1563           255);
1564       destV[dest_uv_idx] =
1565           CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1566                   v1)) / 2, 0, 255);
1567     } else if (j == w - 1) {
1568       y1 = srcY[src_y_idx];
1569       u1 = srcU[src_uv_idx / 2];
1570       v1 = srcV[src_uv_idx / 2];
1571
1572       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1573       destU[dest_uv_idx] = CLAMP (
1574           (3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
1575           0, 255);
1576       destV[dest_uv_idx] =
1577           CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1578                   v1)) / 4, 0, 255);
1579     }
1580   } else if (i == h - 1) {
1581     /* 3.1. Handle the first destination pixel if it doesn't
1582      *      start at the macro pixel boundary, i.e. blend with
1583      *      the background! */
1584     if (dest_x % 2 == 1) {
1585       y1 = srcY[0];
1586       u1 = srcU[0];
1587       v1 = srcV[0];
1588
1589       destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1590       destU[0] =
1591           CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
1592           255);
1593       destV[0] =
1594           CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
1595           255);
1596
1597       j = dest_x + 1;
1598       src_y_idx = dest_y_idx = dest_uv_idx = 1;
1599       src_uv_idx = (src_x % 2) + 1;
1600     } else {
1601       j = dest_x;
1602       src_y_idx = dest_y_idx = dest_uv_idx = 0;
1603       src_uv_idx = (src_x % 2);
1604     }
1605
1606     /* 3.2. Copy all macro pixels from the source to the destination
1607      *      but blend with the background because we're only filling
1608      *      the upper part of the macro pixels. */
1609     for (; j < w - 1; j += 2) {
1610       y1 = srcY[src_y_idx];
1611       y2 = srcY[src_y_idx + 1];
1612
1613       u1 = srcU[src_uv_idx / 2];
1614       v1 = srcV[src_uv_idx / 2];
1615       src_uv_idx++;
1616       u2 = srcU[src_uv_idx / 2];
1617       v2 = srcV[src_uv_idx / 2];
1618       src_uv_idx++;
1619
1620       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1621       destY[dest_y_idx + 1] =
1622           CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
1623
1624       destU[dest_uv_idx] = CLAMP (
1625           (2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1626                   v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
1627       destV[dest_uv_idx] = CLAMP (
1628           (2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
1629                   v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
1630
1631       dest_y_idx += 2;
1632       src_y_idx += 2;
1633       dest_uv_idx++;
1634     }
1635
1636     /* 3.3. Now copy the last pixel if one exists and blend it
1637      *      with the background because we only fill part of
1638      *      the macro pixel. In case this is the last pixel of
1639      *      the destination we will a larger part. */
1640     if (j == w - 1 && j == dest_width - 1) {
1641       y1 = srcY[src_y_idx];
1642       u1 = srcU[src_uv_idx / 2];
1643       v1 = srcV[src_uv_idx / 2];
1644
1645       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1646       destU[dest_uv_idx] = CLAMP (
1647           (destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0,
1648           255);
1649       destV[dest_uv_idx] =
1650           CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1651                   v1)) / 2, 0, 255);
1652     } else if (j == w - 1) {
1653       y1 = srcY[src_y_idx];
1654       u1 = srcU[src_uv_idx / 2];
1655       v1 = srcV[src_uv_idx / 2];
1656
1657       destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
1658       destU[dest_uv_idx] = CLAMP (
1659           (3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
1660           0, 255);
1661       destV[dest_uv_idx] =
1662           CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
1663                   v1)) / 4, 0, 255);
1664     }
1665   }
1666 }
1667
1668 static void
1669 copy_i420_ayuv (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
1670     gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
1671     gint dest_y, GstVideoFormat src_format, const guint8 * src,
1672     gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
1673     gint w, gint h)
1674 {
1675   const guint8 *srcY, *srcU, *srcV;
1676   gint src_strideY, src_strideUV;
1677   gint dest_stride;
1678
1679   src_strideY =
1680       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, src_width);
1681   src_strideUV =
1682       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, src_width);
1683
1684   srcY =
1685       src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
1686       src_width, src_height);
1687   srcU =
1688       src + gst_video_format_get_component_offset (src_format, 1,
1689       src_width, src_height);
1690   srcV =
1691       src + gst_video_format_get_component_offset (src_format, 2,
1692       src_width, src_height);
1693
1694   dest_stride = dest_width * 4;
1695
1696   dest = dest + dest_y * dest_stride + dest_x * 4;
1697
1698   srcY = srcY + src_y * src_strideY + src_x;
1699   srcU = srcU + (src_y / 2) * src_strideUV + src_x / 2;
1700   srcV = srcV + (src_y / 2) * src_strideUV + src_x / 2;
1701
1702   i_alpha = CLAMP (i_alpha, 0, 255);
1703
1704   if (src_sdtv != dest_sdtv) {
1705     gint i, j, uv_idx;
1706     gint y, u, v;
1707     gint y1, u1, v1;
1708     gint matrix[12];
1709
1710     memcpy (matrix,
1711         dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
1712         cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
1713
1714     for (i = 0; i < h; i++) {
1715       for (j = 0, uv_idx = src_x % 2; j < w; j++, uv_idx++) {
1716         y = srcY[j];
1717         u = srcU[uv_idx / 2];
1718         v = srcV[uv_idx / 2];
1719
1720         y1 = APPLY_MATRIX (matrix, 0, y, u, v);
1721         u1 = APPLY_MATRIX (matrix, 1, y, u, v);
1722         v1 = APPLY_MATRIX (matrix, 2, y, u, v);
1723
1724         dest[4 * j + 0] = i_alpha;
1725         dest[4 * j + 1] = y1;
1726         dest[4 * j + 2] = u1;
1727         dest[4 * j + 3] = v1;
1728       }
1729       dest += dest_stride;
1730
1731       src_y++;
1732       srcY += src_strideY;
1733       if (src_y % 2 == 0) {
1734         srcU += src_strideUV;
1735         srcV += src_strideUV;
1736       }
1737     }
1738   } else {
1739     gint i, j, uv_idx;
1740     gint y, u, v;
1741
1742     for (i = 0; i < h; i++) {
1743       for (j = 0, uv_idx = src_x % 2; j < w; j++, uv_idx++) {
1744         y = srcY[j];
1745         u = srcU[uv_idx / 2];
1746         v = srcV[uv_idx / 2];
1747
1748         dest[4 * j + 0] = i_alpha;
1749         dest[4 * j + 1] = y;
1750         dest[4 * j + 2] = u;
1751         dest[4 * j + 3] = v;
1752       }
1753       dest += dest_stride;
1754
1755       src_y++;
1756       srcY += src_strideY;
1757       if (src_y % 2 == 0) {
1758         srcU += src_strideUV;
1759         srcV += src_strideUV;
1760       }
1761     }
1762   }
1763 }
1764
1765 static void
1766 fill_rgb32 (GstVideoBoxFill fill_type, guint b_alpha, GstVideoFormat format,
1767     guint8 * dest, gboolean sdtv, gint width, gint height)
1768 {
1769   guint32 empty_pixel;
1770   gint p[4];
1771
1772   p[0] = gst_video_format_get_component_offset (format, 3, width, height);
1773   p[1] = gst_video_format_get_component_offset (format, 0, width, height);
1774   p[2] = gst_video_format_get_component_offset (format, 1, width, height);
1775   p[3] = gst_video_format_get_component_offset (format, 2, width, height);
1776
1777   b_alpha = CLAMP (b_alpha, 0, 255);
1778
1779   empty_pixel = GUINT32_FROM_LE ((b_alpha << (p[0] * 8)) |
1780       (rgb_colors_R[fill_type] << (p[1] * 8)) |
1781       (rgb_colors_G[fill_type] << (p[2] * 8)) |
1782       (rgb_colors_B[fill_type] << (p[3] * 8)));
1783
1784   orc_splat_u32 ((guint32 *) dest, empty_pixel, width * height);
1785 }
1786
1787 static void
1788 fill_rgb24 (GstVideoBoxFill fill_type, guint b_alpha, GstVideoFormat format,
1789     guint8 * dest, gboolean sdtv, gint width, gint height)
1790 {
1791   gint dest_stride = GST_ROUND_UP_4 (width * 3);
1792   gint p[4];
1793   gint i, j;
1794
1795   p[0] = gst_video_format_get_component_offset (format, 3, width, height);
1796   p[1] = gst_video_format_get_component_offset (format, 0, width, height);
1797   p[2] = gst_video_format_get_component_offset (format, 1, width, height);
1798   p[3] = gst_video_format_get_component_offset (format, 2, width, height);
1799
1800   for (i = 0; i < height; i++) {
1801     for (j = 0; j < width; j++) {
1802       dest[3 * j + p[1]] = rgb_colors_R[fill_type];
1803       dest[3 * j + p[2]] = rgb_colors_G[fill_type];
1804       dest[3 * j + p[3]] = rgb_colors_B[fill_type];
1805     }
1806     dest += dest_stride;
1807   }
1808 }
1809
1810 static void
1811 copy_rgb32 (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
1812     gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
1813     gint dest_y, GstVideoFormat src_format, const guint8 * src,
1814     gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
1815     gint w, gint h)
1816 {
1817   gint i, j;
1818   gint src_stride, dest_stride;
1819   gboolean in_alpha, out_alpha;
1820   gint in_bpp, out_bpp;
1821   gint p_out[4];
1822   gint p_in[4];
1823   gboolean packed_out = (dest_format == GST_VIDEO_FORMAT_RGB
1824       || dest_format == GST_VIDEO_FORMAT_BGR);
1825   gboolean packed_in = (src_format == GST_VIDEO_FORMAT_RGB
1826       || src_format == GST_VIDEO_FORMAT_BGR);
1827
1828   src_stride = (packed_in) ? GST_ROUND_UP_4 (3 * src_width) : 4 * src_width;
1829   dest_stride = (packed_out) ? GST_ROUND_UP_4 (3 * dest_width) : 4 * dest_width;
1830   in_bpp = (packed_in) ? 3 : 4;
1831   out_bpp = (packed_out) ? 3 : 4;
1832
1833   out_alpha = gst_video_format_has_alpha (dest_format);
1834   p_out[0] =
1835       gst_video_format_get_component_offset (dest_format, 3, dest_width,
1836       dest_height);
1837   p_out[1] =
1838       gst_video_format_get_component_offset (dest_format, 0, dest_width,
1839       dest_height);
1840   p_out[2] =
1841       gst_video_format_get_component_offset (dest_format, 1, dest_width,
1842       dest_height);
1843   p_out[3] =
1844       gst_video_format_get_component_offset (dest_format, 2, dest_width,
1845       dest_height);
1846
1847   in_alpha = gst_video_format_has_alpha (src_format);
1848   p_in[0] =
1849       gst_video_format_get_component_offset (src_format, 3, src_width,
1850       src_height);
1851   p_in[1] =
1852       gst_video_format_get_component_offset (src_format, 0, src_width,
1853       src_height);
1854   p_in[2] =
1855       gst_video_format_get_component_offset (src_format, 1, src_width,
1856       src_height);
1857   p_in[3] =
1858       gst_video_format_get_component_offset (src_format, 2, src_width,
1859       src_height);
1860
1861   dest = dest + dest_y * dest_stride + dest_x * out_bpp;
1862   src = src + src_y * src_stride + src_x * in_bpp;
1863
1864   if (in_alpha && out_alpha) {
1865     w *= 4;
1866     for (i = 0; i < h; i++) {
1867       for (j = 0; j < w; j += 4) {
1868         dest[j + p_out[0]] = (src[j + p_in[0]] * i_alpha) >> 8;
1869         dest[j + p_out[1]] = src[j + p_in[1]];
1870         dest[j + p_out[2]] = src[j + p_in[2]];
1871         dest[j + p_out[3]] = src[j + p_in[3]];
1872       }
1873       dest += dest_stride;
1874       src += src_stride;
1875     }
1876   } else if (out_alpha && !packed_in) {
1877     w *= 4;
1878     i_alpha = CLAMP (i_alpha, 0, 255);
1879
1880     for (i = 0; i < h; i++) {
1881       for (j = 0; j < w; j += 4) {
1882         dest[j + p_out[0]] = i_alpha;
1883         dest[j + p_out[1]] = src[j + p_in[1]];
1884         dest[j + p_out[2]] = src[j + p_in[2]];
1885         dest[j + p_out[3]] = src[j + p_in[3]];
1886       }
1887       dest += dest_stride;
1888       src += src_stride;
1889     }
1890   } else if (out_alpha && packed_in) {
1891     i_alpha = CLAMP (i_alpha, 0, 255);
1892
1893     for (i = 0; i < h; i++) {
1894       for (j = 0; j < w; j++) {
1895         dest[4 * j + p_out[0]] = i_alpha;
1896         dest[4 * j + p_out[1]] = src[in_bpp * j + p_in[1]];
1897         dest[4 * j + p_out[2]] = src[in_bpp * j + p_in[2]];
1898         dest[4 * j + p_out[3]] = src[in_bpp * j + p_in[3]];
1899       }
1900       dest += dest_stride;
1901       src += src_stride;
1902     }
1903   } else if (!packed_out && !packed_in) {
1904     w *= 4;
1905     for (i = 0; i < h; i++) {
1906       for (j = 0; j < w; j += 4) {
1907         dest[j + p_out[1]] = src[j + p_in[1]];
1908         dest[j + p_out[2]] = src[j + p_in[2]];
1909         dest[j + p_out[3]] = src[j + p_in[3]];
1910       }
1911       dest += dest_stride;
1912       src += src_stride;
1913     }
1914   } else {
1915     for (i = 0; i < h; i++) {
1916       for (j = 0; j < w; j++) {
1917         dest[out_bpp * j + p_out[1]] = src[in_bpp * j + p_in[1]];
1918         dest[out_bpp * j + p_out[2]] = src[in_bpp * j + p_in[2]];
1919         dest[out_bpp * j + p_out[3]] = src[in_bpp * j + p_in[3]];
1920       }
1921       dest += dest_stride;
1922       src += src_stride;
1923     }
1924   }
1925 }
1926
1927 static void
1928 copy_rgb32_ayuv (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
1929     gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
1930     gint dest_y, GstVideoFormat src_format, const guint8 * src,
1931     gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
1932     gint w, gint h)
1933 {
1934   gint i, j;
1935   gint src_stride, dest_stride;
1936   gboolean in_alpha;
1937   gint in_bpp;
1938   gint p_in[4];
1939   gboolean packed_in = (src_format == GST_VIDEO_FORMAT_RGB
1940       || src_format == GST_VIDEO_FORMAT_BGR);
1941   gint matrix[12];
1942   gint a;
1943   gint y, u, v;
1944   gint r, g, b;
1945
1946   src_stride = (packed_in) ? GST_ROUND_UP_4 (3 * src_width) : 4 * src_width;
1947   dest_stride = 4 * dest_width;
1948   in_bpp = (packed_in) ? 3 : 4;
1949
1950   in_alpha = gst_video_format_has_alpha (src_format);
1951   p_in[0] =
1952       gst_video_format_get_component_offset (src_format, 3, src_width,
1953       src_height);
1954   p_in[1] =
1955       gst_video_format_get_component_offset (src_format, 0, src_width,
1956       src_height);
1957   p_in[2] =
1958       gst_video_format_get_component_offset (src_format, 1, src_width,
1959       src_height);
1960   p_in[3] =
1961       gst_video_format_get_component_offset (src_format, 2, src_width,
1962       src_height);
1963
1964   memcpy (matrix,
1965       (dest_sdtv) ? cog_rgb_to_ycbcr_matrix_8bit_sdtv :
1966       cog_rgb_to_ycbcr_matrix_8bit_hdtv, 12 * sizeof (gint));
1967
1968   dest = dest + dest_y * dest_stride + dest_x * 4;
1969   src = src + src_y * src_stride + src_x * in_bpp;
1970
1971   if (in_alpha) {
1972     w *= 4;
1973     for (i = 0; i < h; i++) {
1974       for (j = 0; j < w; j += 4) {
1975         a = (src[j + p_in[0]] * i_alpha) >> 8;
1976         r = src[j + p_in[1]];
1977         g = src[j + p_in[2]];
1978         b = src[j + p_in[3]];
1979
1980         y = APPLY_MATRIX (matrix, 0, r, g, b);
1981         u = APPLY_MATRIX (matrix, 1, r, g, b);
1982         v = APPLY_MATRIX (matrix, 2, r, g, b);
1983
1984         dest[j + 0] = a;
1985         dest[j + 1] = CLAMP (y, 0, 255);
1986         dest[j + 2] = CLAMP (u, 0, 255);
1987         dest[j + 3] = CLAMP (v, 0, 255);
1988       }
1989       dest += dest_stride;
1990       src += src_stride;
1991     }
1992   } else if (!packed_in) {
1993     w *= 4;
1994     i_alpha = CLAMP (i_alpha, 0, 255);
1995
1996     for (i = 0; i < h; i++) {
1997       for (j = 0; j < w; j += 4) {
1998         a = i_alpha;
1999         r = src[j + p_in[1]];
2000         g = src[j + p_in[2]];
2001         b = src[j + p_in[3]];
2002
2003         y = APPLY_MATRIX (matrix, 0, r, g, b);
2004         u = APPLY_MATRIX (matrix, 1, r, g, b);
2005         v = APPLY_MATRIX (matrix, 2, r, g, b);
2006
2007         dest[j + 0] = a;
2008         dest[j + 1] = CLAMP (y, 0, 255);
2009         dest[j + 2] = CLAMP (u, 0, 255);
2010         dest[j + 3] = CLAMP (v, 0, 255);
2011       }
2012       dest += dest_stride;
2013       src += src_stride;
2014     }
2015   } else {
2016     i_alpha = CLAMP (i_alpha, 0, 255);
2017
2018     for (i = 0; i < h; i++) {
2019       for (j = 0; j < w; j++) {
2020         a = i_alpha;
2021         r = src[in_bpp * j + p_in[1]];
2022         g = src[in_bpp * j + p_in[2]];
2023         b = src[in_bpp * j + p_in[3]];
2024
2025         y = APPLY_MATRIX (matrix, 0, r, g, b);
2026         u = APPLY_MATRIX (matrix, 1, r, g, b);
2027         v = APPLY_MATRIX (matrix, 2, r, g, b);
2028
2029         dest[4 * j + 0] = a;
2030         dest[4 * j + 1] = CLAMP (y, 0, 255);
2031         dest[4 * j + 2] = CLAMP (u, 0, 255);
2032         dest[4 * j + 3] = CLAMP (v, 0, 255);
2033       }
2034       dest += dest_stride;
2035       src += src_stride;
2036     }
2037   }
2038 }
2039
2040 static void
2041 copy_ayuv_rgb32 (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
2042     gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
2043     gint dest_y, GstVideoFormat src_format, const guint8 * src,
2044     gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
2045     gint w, gint h)
2046 {
2047   gint i, j;
2048   gint src_stride, dest_stride;
2049   gboolean out_alpha;
2050   gint out_bpp;
2051   gint p_out[4];
2052   gboolean packed_out = (dest_format == GST_VIDEO_FORMAT_RGB
2053       || dest_format == GST_VIDEO_FORMAT_BGR);
2054   gint matrix[12];
2055   gint a;
2056   gint y, u, v;
2057   gint r, g, b;
2058
2059   dest_stride = (packed_out) ? GST_ROUND_UP_4 (3 * dest_width) : 4 * dest_width;
2060   src_stride = 4 * src_width;
2061   out_bpp = (packed_out) ? 3 : 4;
2062
2063   out_alpha = gst_video_format_has_alpha (dest_format);
2064   p_out[0] =
2065       gst_video_format_get_component_offset (dest_format, 3, dest_width,
2066       dest_height);
2067   p_out[1] =
2068       gst_video_format_get_component_offset (dest_format, 0, dest_width,
2069       dest_height);
2070   p_out[2] =
2071       gst_video_format_get_component_offset (dest_format, 1, dest_width,
2072       dest_height);
2073   p_out[3] =
2074       gst_video_format_get_component_offset (dest_format, 2, dest_width,
2075       dest_height);
2076
2077   memcpy (matrix,
2078       (src_sdtv) ? cog_ycbcr_to_rgb_matrix_8bit_sdtv :
2079       cog_ycbcr_to_rgb_matrix_8bit_hdtv, 12 * sizeof (gint));
2080
2081   dest = dest + dest_y * dest_stride + dest_x * out_bpp;
2082   src = src + src_y * src_stride + src_x * 4;
2083
2084   if (out_alpha) {
2085     w *= 4;
2086     for (i = 0; i < h; i++) {
2087       for (j = 0; j < w; j += 4) {
2088         a = (src[j + 0] * i_alpha) >> 8;
2089         y = src[j + 1];
2090         u = src[j + 2];
2091         v = src[j + 3];
2092
2093         r = APPLY_MATRIX (matrix, 0, y, u, v);
2094         g = APPLY_MATRIX (matrix, 1, y, u, v);
2095         b = APPLY_MATRIX (matrix, 2, y, u, v);
2096
2097         dest[j + p_out[0]] = a;
2098         dest[j + p_out[1]] = CLAMP (r, 0, 255);
2099         dest[j + p_out[2]] = CLAMP (g, 0, 255);
2100         dest[j + p_out[3]] = CLAMP (b, 0, 255);
2101       }
2102       dest += dest_stride;
2103       src += src_stride;
2104     }
2105   } else if (!packed_out) {
2106     w *= 4;
2107     for (i = 0; i < h; i++) {
2108       for (j = 0; j < w; j += 4) {
2109         y = src[j + 1];
2110         u = src[j + 2];
2111         v = src[j + 3];
2112
2113         r = APPLY_MATRIX (matrix, 0, y, u, v);
2114         g = APPLY_MATRIX (matrix, 1, y, u, v);
2115         b = APPLY_MATRIX (matrix, 2, y, u, v);
2116
2117         dest[j + p_out[1]] = CLAMP (r, 0, 255);
2118         dest[j + p_out[2]] = CLAMP (g, 0, 255);
2119         dest[j + p_out[3]] = CLAMP (b, 0, 255);
2120       }
2121       dest += dest_stride;
2122       src += src_stride;
2123     }
2124   } else {
2125     for (i = 0; i < h; i++) {
2126       for (j = 0; j < w; j++) {
2127         y = src[4 * j + 1];
2128         u = src[4 * j + 2];
2129         v = src[4 * j + 3];
2130
2131         r = APPLY_MATRIX (matrix, 0, y, u, v);
2132         g = APPLY_MATRIX (matrix, 1, y, u, v);
2133         b = APPLY_MATRIX (matrix, 2, y, u, v);
2134
2135         dest[out_bpp * j + p_out[1]] = CLAMP (r, 0, 255);
2136         dest[out_bpp * j + p_out[2]] = CLAMP (g, 0, 255);
2137         dest[out_bpp * j + p_out[3]] = CLAMP (b, 0, 255);
2138       }
2139       dest += dest_stride;
2140       src += src_stride;
2141     }
2142   }
2143 }
2144
2145 static void
2146 fill_gray (GstVideoBoxFill fill_type, guint b_alpha, GstVideoFormat format,
2147     guint8 * dest, gboolean sdtv, gint width, gint height)
2148 {
2149   gint i, j;
2150   gint dest_stride;
2151
2152   if (format == GST_VIDEO_FORMAT_GRAY8) {
2153     guint8 val = yuv_sdtv_colors_Y[fill_type];
2154
2155     dest_stride = GST_ROUND_UP_4 (width);
2156     for (i = 0; i < height; i++) {
2157       memset (dest, val, width);
2158       dest += dest_stride;
2159     }
2160   } else {
2161     guint16 val = yuv_sdtv_colors_Y[fill_type] << 8;
2162
2163     dest_stride = GST_ROUND_UP_4 (width * 2);
2164     if (format == GST_VIDEO_FORMAT_GRAY16_BE) {
2165       for (i = 0; i < height; i++) {
2166         for (j = 0; j < width; j++) {
2167           GST_WRITE_UINT16_BE (dest + 2 * j, val);
2168         }
2169         dest += dest_stride;
2170       }
2171     } else {
2172       for (i = 0; i < height; i++) {
2173         for (j = 0; j < width; j++) {
2174           GST_WRITE_UINT16_LE (dest + 2 * j, val);
2175         }
2176         dest += dest_stride;
2177       }
2178     }
2179   }
2180 }
2181
2182 static void
2183 copy_packed_simple (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
2184     gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
2185     gint dest_y, GstVideoFormat src_format, const guint8 * src,
2186     gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
2187     gint w, gint h)
2188 {
2189   gint i;
2190   gint src_stride, dest_stride;
2191   gint pixel_stride, row_size;
2192
2193   src_stride = gst_video_format_get_row_stride (src_format, 0, src_width);
2194   dest_stride = gst_video_format_get_row_stride (dest_format, 0, dest_width);
2195   pixel_stride = gst_video_format_get_pixel_stride (dest_format, 0);
2196   row_size = w * pixel_stride;
2197
2198   dest = dest + dest_y * dest_stride + dest_x * pixel_stride;
2199   src = src + src_y * src_stride + src_x * pixel_stride;
2200
2201   for (i = 0; i < h; i++) {
2202     memcpy (dest, src, row_size);
2203     dest += dest_stride;
2204     src += src_stride;
2205   }
2206 }
2207
2208 static void
2209 fill_yuy2 (GstVideoBoxFill fill_type, guint b_alpha, GstVideoFormat format,
2210     guint8 * dest, gboolean sdtv, gint width, gint height)
2211 {
2212   guint8 y, u, v;
2213   gint i, j;
2214   gint stride = gst_video_format_get_row_stride (format, 0, width);
2215
2216   y = (sdtv) ? yuv_sdtv_colors_Y[fill_type] : yuv_hdtv_colors_Y[fill_type];
2217   u = (sdtv) ? yuv_sdtv_colors_U[fill_type] : yuv_hdtv_colors_U[fill_type];
2218   v = (sdtv) ? yuv_sdtv_colors_V[fill_type] : yuv_hdtv_colors_V[fill_type];
2219
2220   width = width + (width % 2);
2221
2222   if (format == GST_VIDEO_FORMAT_YUY2) {
2223     for (i = 0; i < height; i++) {
2224       for (j = 0; j < width; j += 2) {
2225         dest[j * 2 + 0] = y;
2226         dest[j * 2 + 1] = u;
2227         dest[j * 2 + 2] = y;
2228         dest[j * 2 + 3] = v;
2229       }
2230
2231       dest += stride;
2232     }
2233   } else if (format == GST_VIDEO_FORMAT_YVYU) {
2234     for (i = 0; i < height; i++) {
2235       for (j = 0; j < width; j += 2) {
2236         dest[j * 2 + 0] = y;
2237         dest[j * 2 + 1] = v;
2238         dest[j * 2 + 2] = y;
2239         dest[j * 2 + 3] = u;
2240       }
2241
2242       dest += stride;
2243     }
2244   } else {
2245     for (i = 0; i < height; i++) {
2246       for (j = 0; j < width; j += 2) {
2247         dest[j * 2 + 0] = u;
2248         dest[j * 2 + 1] = y;
2249         dest[j * 2 + 2] = v;
2250         dest[j * 2 + 3] = y;
2251       }
2252
2253       dest += stride;
2254     }
2255   }
2256 }
2257
2258 static void
2259 copy_yuy2_yuy2 (guint i_alpha, GstVideoFormat dest_format, guint8 * dest,
2260     gboolean dest_sdtv, gint dest_width, gint dest_height, gint dest_x,
2261     gint dest_y, GstVideoFormat src_format, const guint8 * src,
2262     gboolean src_sdtv, gint src_width, gint src_height, gint src_x, gint src_y,
2263     gint w, gint h)
2264 {
2265   gint i, j;
2266   gint src_stride, dest_stride;
2267
2268   src_stride = gst_video_format_get_row_stride (src_format, 0, src_width);
2269   dest_stride = gst_video_format_get_row_stride (dest_format, 0, dest_width);
2270
2271   dest_x = (dest_x & ~1);
2272   src_x = (src_x & ~1);
2273
2274   w = w + (w % 2);
2275
2276   dest = dest + dest_y * dest_stride + dest_x * 2;
2277   src = src + src_y * src_stride + src_x * 2;
2278
2279   if (src_sdtv != dest_sdtv) {
2280     gint y1, u1, v1;
2281     gint y2, u2, v2;
2282     gint matrix[12];
2283
2284     memcpy (matrix,
2285         dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
2286         cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
2287
2288     if (src_format == GST_VIDEO_FORMAT_YUY2) {
2289       for (i = 0; i < h; i++) {
2290         for (j = 0; j < w; j += 2) {
2291           y1 = src[j * 2 + 0];
2292           y2 = src[j * 2 + 2];
2293           u1 = u2 = src[j * 2 + 1];
2294           v1 = v2 = src[j * 2 + 3];
2295
2296           dest[j * 2 + 0] = APPLY_MATRIX (matrix, 0, y1, u1, v1);
2297           dest[j * 2 + 1] = APPLY_MATRIX (matrix, 1, y1, u1, v1);
2298           dest[j * 2 + 2] = APPLY_MATRIX (matrix, 0, y1, u2, v2);
2299           dest[j * 2 + 3] = APPLY_MATRIX (matrix, 2, y2, u2, v2);
2300         }
2301         dest += dest_stride;
2302         src += src_stride;
2303       }
2304     } else if (src_format == GST_VIDEO_FORMAT_YVYU) {
2305       for (i = 0; i < h; i++) {
2306         for (j = 0; j < w; j += 2) {
2307           y1 = src[j * 2 + 0];
2308           y2 = src[j * 2 + 2];
2309           v1 = v2 = src[j * 2 + 1];
2310           u1 = u2 = src[j * 2 + 3];
2311
2312           dest[j * 2 + 0] = APPLY_MATRIX (matrix, 0, y1, u1, v1);
2313           dest[j * 2 + 1] = APPLY_MATRIX (matrix, 2, y1, u1, v1);
2314           dest[j * 2 + 2] = APPLY_MATRIX (matrix, 0, y1, u2, v2);
2315           dest[j * 2 + 3] = APPLY_MATRIX (matrix, 1, y2, u2, v2);
2316         }
2317         dest += dest_stride;
2318         src += src_stride;
2319       }
2320     } else {
2321       for (i = 0; i < h; i++) {
2322         for (j = 0; j < w; j += 2) {
2323           u1 = u2 = src[j * 2 + 0];
2324           v1 = v2 = src[j * 2 + 2];
2325           y1 = src[j * 2 + 1];
2326           y2 = src[j * 2 + 3];
2327
2328           dest[j * 2 + 1] = APPLY_MATRIX (matrix, 0, y1, u1, v1);
2329           dest[j * 2 + 0] = APPLY_MATRIX (matrix, 1, y1, u1, v1);
2330           dest[j * 2 + 3] = APPLY_MATRIX (matrix, 0, y1, u2, v2);
2331           dest[j * 2 + 2] = APPLY_MATRIX (matrix, 2, y2, u2, v2);
2332         }
2333         dest += dest_stride;
2334         src += src_stride;
2335       }
2336     }
2337   } else {
2338     for (i = 0; i < h; i++) {
2339       memcpy (dest, src, w * 2);
2340       dest += dest_stride;
2341       src += src_stride;
2342     }
2343   }
2344 }
2345
2346 #define DEFAULT_LEFT      0
2347 #define DEFAULT_RIGHT     0
2348 #define DEFAULT_TOP       0
2349 #define DEFAULT_BOTTOM    0
2350 #define DEFAULT_FILL_TYPE VIDEO_BOX_FILL_BLACK
2351 #define DEFAULT_ALPHA     1.0
2352 #define DEFAULT_BORDER_ALPHA 1.0
2353
2354 enum
2355 {
2356   PROP_0,
2357   PROP_LEFT,
2358   PROP_RIGHT,
2359   PROP_TOP,
2360   PROP_BOTTOM,
2361   PROP_FILL_TYPE,
2362   PROP_ALPHA,
2363   PROP_BORDER_ALPHA,
2364   PROP_AUTOCROP
2365       /* FILL ME */
2366 };
2367
2368 static GstStaticPadTemplate gst_video_box_src_template =
2369     GST_STATIC_PAD_TEMPLATE ("src",
2370     GST_PAD_SRC,
2371     GST_PAD_ALWAYS,
2372     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
2373         GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";"
2374         GST_VIDEO_CAPS_ABGR ";" GST_VIDEO_CAPS_RGBA ";"
2375         GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_BGRx ";"
2376         GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_RGBx ";"
2377         GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
2378         GST_VIDEO_CAPS_YUV ("Y444") ";"
2379         GST_VIDEO_CAPS_YUV ("Y42B") ";"
2380         GST_VIDEO_CAPS_YUV ("YUY2") ";"
2381         GST_VIDEO_CAPS_YUV ("YVYU") ";"
2382         GST_VIDEO_CAPS_YUV ("UYVY") ";"
2383         GST_VIDEO_CAPS_YUV ("I420") ";"
2384         GST_VIDEO_CAPS_YUV ("YV12") ";"
2385         GST_VIDEO_CAPS_YUV ("Y41B") ";"
2386         GST_VIDEO_CAPS_GRAY8 ";"
2387         GST_VIDEO_CAPS_GRAY16 ("BIG_ENDIAN") ";"
2388         GST_VIDEO_CAPS_GRAY16 ("LITTLE_ENDIAN"))
2389     );
2390
2391 static GstStaticPadTemplate gst_video_box_sink_template =
2392     GST_STATIC_PAD_TEMPLATE ("sink",
2393     GST_PAD_SINK,
2394     GST_PAD_ALWAYS,
2395     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
2396         GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";"
2397         GST_VIDEO_CAPS_ABGR ";" GST_VIDEO_CAPS_RGBA ";"
2398         GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_BGRx ";"
2399         GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_RGBx ";"
2400         GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
2401         GST_VIDEO_CAPS_YUV ("Y444") ";"
2402         GST_VIDEO_CAPS_YUV ("Y42B") ";"
2403         GST_VIDEO_CAPS_YUV ("YUY2") ";"
2404         GST_VIDEO_CAPS_YUV ("YVYU") ";"
2405         GST_VIDEO_CAPS_YUV ("UYVY") ";"
2406         GST_VIDEO_CAPS_YUV ("I420") ";"
2407         GST_VIDEO_CAPS_YUV ("YV12") ";"
2408         GST_VIDEO_CAPS_YUV ("Y41B") ";"
2409         GST_VIDEO_CAPS_GRAY8 ";"
2410         GST_VIDEO_CAPS_GRAY16 ("BIG_ENDIAN") ";"
2411         GST_VIDEO_CAPS_GRAY16 ("LITTLE_ENDIAN"))
2412     );
2413
2414 GST_BOILERPLATE (GstVideoBox, gst_video_box, GstBaseTransform,
2415     GST_TYPE_BASE_TRANSFORM);
2416
2417 static void gst_video_box_set_property (GObject * object, guint prop_id,
2418     const GValue * value, GParamSpec * pspec);
2419 static void gst_video_box_get_property (GObject * object, guint prop_id,
2420     GValue * value, GParamSpec * pspec);
2421
2422 static gboolean gst_video_box_recalc_transform (GstVideoBox * video_box);
2423 static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans,
2424     GstPadDirection direction, GstCaps * from);
2425 static gboolean gst_video_box_set_caps (GstBaseTransform * trans,
2426     GstCaps * in, GstCaps * out);
2427 static gboolean gst_video_box_get_unit_size (GstBaseTransform * trans,
2428     GstCaps * caps, guint * size);
2429 static GstFlowReturn gst_video_box_transform (GstBaseTransform * trans,
2430     GstBuffer * in, GstBuffer * out);
2431 static void gst_video_box_before_transform (GstBaseTransform * trans,
2432     GstBuffer * in);
2433 static void gst_video_box_fixate_caps (GstBaseTransform * trans,
2434     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
2435 static gboolean gst_video_box_src_event (GstBaseTransform * trans,
2436     GstEvent * event);
2437
2438 #define GST_TYPE_VIDEO_BOX_FILL (gst_video_box_fill_get_type())
2439 static GType
2440 gst_video_box_fill_get_type (void)
2441 {
2442   static GType video_box_fill_type = 0;
2443   static const GEnumValue video_box_fill[] = {
2444     {VIDEO_BOX_FILL_BLACK, "Black", "black"},
2445     {VIDEO_BOX_FILL_GREEN, "Green", "green"},
2446     {VIDEO_BOX_FILL_BLUE, "Blue", "blue"},
2447     {VIDEO_BOX_FILL_RED, "Red", "red"},
2448     {VIDEO_BOX_FILL_YELLOW, "Yellow", "yellow"},
2449     {VIDEO_BOX_FILL_WHITE, "White", "white"},
2450     {0, NULL, NULL},
2451   };
2452
2453   if (!video_box_fill_type) {
2454     video_box_fill_type =
2455         g_enum_register_static ("GstVideoBoxFill", video_box_fill);
2456   }
2457   return video_box_fill_type;
2458 }
2459
2460
2461 static void
2462 gst_video_box_base_init (gpointer g_class)
2463 {
2464   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2465
2466   gst_element_class_set_details_simple (element_class, "Video box filter",
2467       "Filter/Effect/Video",
2468       "Resizes a video by adding borders or cropping",
2469       "Wim Taymans <wim@fluendo.com>");
2470
2471   gst_element_class_add_static_pad_template (element_class,
2472       &gst_video_box_sink_template);
2473   gst_element_class_add_static_pad_template (element_class,
2474       &gst_video_box_src_template);
2475 }
2476
2477 static void
2478 gst_video_box_finalize (GObject * object)
2479 {
2480   GstVideoBox *video_box = GST_VIDEO_BOX (object);
2481
2482   if (video_box->mutex) {
2483     g_mutex_free (video_box->mutex);
2484     video_box->mutex = NULL;
2485   }
2486
2487   G_OBJECT_CLASS (parent_class)->finalize (object);
2488 }
2489
2490 static void
2491 gst_video_box_class_init (GstVideoBoxClass * klass)
2492 {
2493   GObjectClass *gobject_class = (GObjectClass *) klass;
2494   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
2495
2496   gobject_class->set_property = gst_video_box_set_property;
2497   gobject_class->get_property = gst_video_box_get_property;
2498   gobject_class->finalize = gst_video_box_finalize;
2499
2500   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILL_TYPE,
2501       g_param_spec_enum ("fill", "Fill", "How to fill the borders",
2502           GST_TYPE_VIDEO_BOX_FILL, DEFAULT_FILL_TYPE,
2503           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
2504   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LEFT,
2505       g_param_spec_int ("left", "Left",
2506           "Pixels to box at left (<0  = add a border)", G_MININT, G_MAXINT,
2507           DEFAULT_LEFT,
2508           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
2509   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RIGHT,
2510       g_param_spec_int ("right", "Right",
2511           "Pixels to box at right (<0 = add a border)", G_MININT, G_MAXINT,
2512           DEFAULT_RIGHT,
2513           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
2514   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TOP,
2515       g_param_spec_int ("top", "Top",
2516           "Pixels to box at top (<0 = add a border)", G_MININT, G_MAXINT,
2517           DEFAULT_TOP,
2518           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
2519   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BOTTOM,
2520       g_param_spec_int ("bottom", "Bottom",
2521           "Pixels to box at bottom (<0 = add a border)", G_MININT, G_MAXINT,
2522           DEFAULT_BOTTOM,
2523           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
2524   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ALPHA,
2525       g_param_spec_double ("alpha", "Alpha", "Alpha value picture", 0.0, 1.0,
2526           DEFAULT_ALPHA,
2527           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
2528   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER_ALPHA,
2529       g_param_spec_double ("border-alpha", "Border Alpha",
2530           "Alpha value of the border", 0.0, 1.0, DEFAULT_BORDER_ALPHA,
2531           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
2532   /**
2533    * GstVideoBox:autocrop
2534    *
2535    * If set to %TRUE videobox will automatically crop/pad the input
2536    * video to be centered in the output.
2537    *
2538    * Since: 0.10.16
2539    **/
2540   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_AUTOCROP,
2541       g_param_spec_boolean ("autocrop", "Auto crop",
2542           "Auto crop", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2543
2544   trans_class->transform = GST_DEBUG_FUNCPTR (gst_video_box_transform);
2545   trans_class->before_transform =
2546       GST_DEBUG_FUNCPTR (gst_video_box_before_transform);
2547   trans_class->transform_caps =
2548       GST_DEBUG_FUNCPTR (gst_video_box_transform_caps);
2549   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_box_set_caps);
2550   trans_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_video_box_get_unit_size);
2551   trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_video_box_fixate_caps);
2552   trans_class->src_event = GST_DEBUG_FUNCPTR (gst_video_box_src_event);
2553 }
2554
2555 static void
2556 gst_video_box_init (GstVideoBox * video_box, GstVideoBoxClass * g_class)
2557 {
2558   video_box->box_right = DEFAULT_RIGHT;
2559   video_box->box_left = DEFAULT_LEFT;
2560   video_box->box_top = DEFAULT_TOP;
2561   video_box->box_bottom = DEFAULT_BOTTOM;
2562   video_box->crop_right = 0;
2563   video_box->crop_left = 0;
2564   video_box->crop_top = 0;
2565   video_box->crop_bottom = 0;
2566   video_box->fill_type = DEFAULT_FILL_TYPE;
2567   video_box->alpha = DEFAULT_ALPHA;
2568   video_box->border_alpha = DEFAULT_BORDER_ALPHA;
2569   video_box->autocrop = FALSE;
2570
2571   video_box->mutex = g_mutex_new ();
2572 }
2573
2574 static void
2575 gst_video_box_set_property (GObject * object, guint prop_id,
2576     const GValue * value, GParamSpec * pspec)
2577 {
2578   GstVideoBox *video_box = GST_VIDEO_BOX (object);
2579
2580   g_mutex_lock (video_box->mutex);
2581   switch (prop_id) {
2582     case PROP_LEFT:
2583       video_box->box_left = g_value_get_int (value);
2584       if (video_box->box_left < 0) {
2585         video_box->border_left = -video_box->box_left;
2586         video_box->crop_left = 0;
2587       } else {
2588         video_box->border_left = 0;
2589         video_box->crop_left = video_box->box_left;
2590       }
2591       break;
2592     case PROP_RIGHT:
2593       video_box->box_right = g_value_get_int (value);
2594       if (video_box->box_right < 0) {
2595         video_box->border_right = -video_box->box_right;
2596         video_box->crop_right = 0;
2597       } else {
2598         video_box->border_right = 0;
2599         video_box->crop_right = video_box->box_right;
2600       }
2601       break;
2602     case PROP_TOP:
2603       video_box->box_top = g_value_get_int (value);
2604       if (video_box->box_top < 0) {
2605         video_box->border_top = -video_box->box_top;
2606         video_box->crop_top = 0;
2607       } else {
2608         video_box->border_top = 0;
2609         video_box->crop_top = video_box->box_top;
2610       }
2611       break;
2612     case PROP_BOTTOM:
2613       video_box->box_bottom = g_value_get_int (value);
2614       if (video_box->box_bottom < 0) {
2615         video_box->border_bottom = -video_box->box_bottom;
2616         video_box->crop_bottom = 0;
2617       } else {
2618         video_box->border_bottom = 0;
2619         video_box->crop_bottom = video_box->box_bottom;
2620       }
2621       break;
2622     case PROP_FILL_TYPE:
2623       video_box->fill_type = g_value_get_enum (value);
2624       break;
2625     case PROP_ALPHA:
2626       video_box->alpha = g_value_get_double (value);
2627       break;
2628     case PROP_BORDER_ALPHA:
2629       video_box->border_alpha = g_value_get_double (value);
2630       break;
2631     case PROP_AUTOCROP:
2632       video_box->autocrop = g_value_get_boolean (value);
2633       break;
2634     default:
2635       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2636       break;
2637   }
2638   gst_video_box_recalc_transform (video_box);
2639
2640   GST_DEBUG_OBJECT (video_box, "Calling reconfigure");
2641   gst_base_transform_reconfigure (GST_BASE_TRANSFORM_CAST (video_box));
2642
2643   g_mutex_unlock (video_box->mutex);
2644 }
2645
2646 static void
2647 gst_video_box_autocrop (GstVideoBox * video_box)
2648 {
2649   gint crop_w = video_box->in_width - video_box->out_width;
2650   gint crop_h = video_box->in_height - video_box->out_height;
2651
2652   video_box->box_left = crop_w / 2;
2653   if (video_box->box_left < 0) {
2654     video_box->border_left = -video_box->box_left;
2655     video_box->crop_left = 0;
2656   } else {
2657     video_box->border_left = 0;
2658     video_box->crop_left = video_box->box_left;
2659   }
2660
2661   /* Round down/up for odd width differences */
2662   if (crop_w < 0)
2663     crop_w -= 1;
2664   else
2665     crop_w += 1;
2666
2667   video_box->box_right = crop_w / 2;
2668   if (video_box->box_right < 0) {
2669     video_box->border_right = -video_box->box_right;
2670     video_box->crop_right = 0;
2671   } else {
2672     video_box->border_right = 0;
2673     video_box->crop_right = video_box->box_right;
2674   }
2675
2676   video_box->box_top = crop_h / 2;
2677   if (video_box->box_top < 0) {
2678     video_box->border_top = -video_box->box_top;
2679     video_box->crop_top = 0;
2680   } else {
2681     video_box->border_top = 0;
2682     video_box->crop_top = video_box->box_top;
2683   }
2684
2685   /* Round down/up for odd height differences */
2686   if (crop_h < 0)
2687     crop_h -= 1;
2688   else
2689     crop_h += 1;
2690   video_box->box_bottom = crop_h / 2;
2691
2692   if (video_box->box_bottom < 0) {
2693     video_box->border_bottom = -video_box->box_bottom;
2694     video_box->crop_bottom = 0;
2695   } else {
2696     video_box->border_bottom = 0;
2697     video_box->crop_bottom = video_box->box_bottom;
2698   }
2699 }
2700
2701 static void
2702 gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
2703     GParamSpec * pspec)
2704 {
2705   GstVideoBox *video_box = GST_VIDEO_BOX (object);
2706
2707   switch (prop_id) {
2708     case PROP_LEFT:
2709       g_value_set_int (value, video_box->box_left);
2710       break;
2711     case PROP_RIGHT:
2712       g_value_set_int (value, video_box->box_right);
2713       break;
2714     case PROP_TOP:
2715       g_value_set_int (value, video_box->box_top);
2716       break;
2717     case PROP_BOTTOM:
2718       g_value_set_int (value, video_box->box_bottom);
2719       break;
2720     case PROP_FILL_TYPE:
2721       g_value_set_enum (value, video_box->fill_type);
2722       break;
2723     case PROP_ALPHA:
2724       g_value_set_double (value, video_box->alpha);
2725       break;
2726     case PROP_BORDER_ALPHA:
2727       g_value_set_double (value, video_box->border_alpha);
2728       break;
2729     case PROP_AUTOCROP:
2730       g_value_set_boolean (value, video_box->autocrop);
2731       break;
2732     default:
2733       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2734       break;
2735   }
2736 }
2737
2738 static inline gint
2739 gst_video_box_transform_dimension (gint val, gint delta)
2740 {
2741   gint64 new_val = (gint64) val + (gint64) delta;
2742
2743   new_val = CLAMP (new_val, 1, G_MAXINT);
2744
2745   return (gint) new_val;
2746 }
2747
2748 static gboolean
2749 gst_video_box_transform_dimension_value (const GValue * src_val,
2750     gint delta, GValue * dest_val)
2751 {
2752   gboolean ret = TRUE;
2753
2754   g_value_init (dest_val, G_VALUE_TYPE (src_val));
2755
2756   if (G_VALUE_HOLDS_INT (src_val)) {
2757     gint ival = g_value_get_int (src_val);
2758
2759     ival = gst_video_box_transform_dimension (ival, delta);
2760     g_value_set_int (dest_val, ival);
2761   } else if (GST_VALUE_HOLDS_INT_RANGE (src_val)) {
2762     gint min = gst_value_get_int_range_min (src_val);
2763     gint max = gst_value_get_int_range_max (src_val);
2764
2765     min = gst_video_box_transform_dimension (min, delta);
2766     max = gst_video_box_transform_dimension (max, delta);
2767     if (min > max) {
2768       ret = FALSE;
2769       g_value_unset (dest_val);
2770     } else {
2771       gst_value_set_int_range (dest_val, min, max);
2772     }
2773   } else if (GST_VALUE_HOLDS_LIST (src_val)) {
2774     gint i;
2775
2776     for (i = 0; i < gst_value_list_get_size (src_val); ++i) {
2777       const GValue *list_val;
2778       GValue newval = { 0, };
2779
2780       list_val = gst_value_list_get_value (src_val, i);
2781       if (gst_video_box_transform_dimension_value (list_val, delta, &newval))
2782         gst_value_list_append_value (dest_val, &newval);
2783       g_value_unset (&newval);
2784     }
2785
2786     if (gst_value_list_get_size (dest_val) == 0) {
2787       g_value_unset (dest_val);
2788       ret = FALSE;
2789     }
2790   } else {
2791     g_value_unset (dest_val);
2792     ret = FALSE;
2793   }
2794
2795   return ret;
2796 }
2797
2798 static GstCaps *
2799 gst_video_box_transform_caps (GstBaseTransform * trans,
2800     GstPadDirection direction, GstCaps * from)
2801 {
2802   GstVideoBox *video_box = GST_VIDEO_BOX (trans);
2803   GstCaps *to, *ret;
2804   const GstCaps *templ;
2805   const gchar *name;
2806   GstStructure *structure;
2807   GstPad *other;
2808
2809   to = gst_caps_copy (from);
2810   /* Just to be sure... */
2811   gst_caps_truncate (to);
2812   structure = gst_caps_get_structure (to, 0);
2813
2814   /* Transform width/height */
2815   if (video_box->autocrop) {
2816     gst_structure_remove_field (structure, "width");
2817     gst_structure_remove_field (structure, "height");
2818   } else {
2819     gint dw = 0, dh = 0;
2820     const GValue *v;
2821     GValue w_val = { 0, };
2822     GValue h_val = { 0, };
2823
2824     /* calculate width and height */
2825     if (direction == GST_PAD_SINK) {
2826       dw -= video_box->box_left;
2827       dw -= video_box->box_right;
2828     } else {
2829       dw += video_box->box_left;
2830       dw += video_box->box_right;
2831     }
2832
2833     if (direction == GST_PAD_SINK) {
2834       dh -= video_box->box_top;
2835       dh -= video_box->box_bottom;
2836     } else {
2837       dh += video_box->box_top;
2838       dh += video_box->box_bottom;
2839     }
2840
2841     v = gst_structure_get_value (structure, "width");
2842     if (!gst_video_box_transform_dimension_value (v, dw, &w_val)) {
2843       GST_WARNING_OBJECT (video_box, "could not tranform width value with dw=%d"
2844           ", caps structure=%" GST_PTR_FORMAT, dw, structure);
2845       gst_caps_unref (to);
2846       to = gst_caps_new_empty ();
2847       return to;
2848     }
2849     gst_structure_set_value (structure, "width", &w_val);
2850
2851     v = gst_structure_get_value (structure, "height");
2852     if (!gst_video_box_transform_dimension_value (v, dh, &h_val)) {
2853       g_value_unset (&w_val);
2854       GST_WARNING_OBJECT (video_box,
2855           "could not tranform height value with dh=%d" ", caps structure=%"
2856           GST_PTR_FORMAT, dh, structure);
2857       gst_caps_unref (to);
2858       to = gst_caps_new_empty ();
2859       return to;
2860     }
2861     gst_structure_set_value (structure, "height", &h_val);
2862     g_value_unset (&w_val);
2863     g_value_unset (&h_val);
2864   }
2865
2866   /* Supported conversions:
2867    * I420->AYUV
2868    * I420->YV12
2869    * YV12->AYUV
2870    * YV12->I420
2871    * AYUV->I420
2872    * AYUV->YV12
2873    * AYUV->xRGB (24bpp, 32bpp, incl. alpha)
2874    * xRGB->xRGB (24bpp, 32bpp, from/to all variants, incl. alpha)
2875    * xRGB->AYUV (24bpp, 32bpp, incl. alpha)
2876    *
2877    * Passthrough only for everything else.
2878    */
2879   name = gst_structure_get_name (structure);
2880   if (g_str_equal (name, "video/x-raw-yuv")) {
2881     guint32 fourcc;
2882
2883     if (gst_structure_get_fourcc (structure, "format", &fourcc) &&
2884         (fourcc == GST_STR_FOURCC ("AYUV") ||
2885             fourcc == GST_STR_FOURCC ("I420") ||
2886             fourcc == GST_STR_FOURCC ("YV12"))) {
2887       GValue list = { 0, };
2888       GValue val = { 0, };
2889       GstStructure *s2;
2890
2891       /* get rid of format */
2892       gst_structure_remove_field (structure, "format");
2893       gst_structure_remove_field (structure, "color-matrix");
2894       gst_structure_remove_field (structure, "chroma-site");
2895
2896       s2 = gst_structure_copy (structure);
2897
2898       g_value_init (&list, GST_TYPE_LIST);
2899       g_value_init (&val, GST_TYPE_FOURCC);
2900       gst_value_set_fourcc (&val, GST_STR_FOURCC ("AYUV"));
2901       gst_value_list_append_value (&list, &val);
2902       g_value_reset (&val);
2903       gst_value_set_fourcc (&val, GST_STR_FOURCC ("I420"));
2904       gst_value_list_append_value (&list, &val);
2905       g_value_reset (&val);
2906       gst_value_set_fourcc (&val, GST_STR_FOURCC ("YV12"));
2907       gst_value_list_append_value (&list, &val);
2908       g_value_unset (&val);
2909       gst_structure_set_value (structure, "format", &list);
2910       g_value_unset (&list);
2911
2912       /* We can only convert to RGB if input is AYUV */
2913       if (fourcc == GST_STR_FOURCC ("AYUV")) {
2914         gst_structure_set_name (s2, "video/x-raw-rgb");
2915         g_value_init (&list, GST_TYPE_LIST);
2916         g_value_init (&val, G_TYPE_INT);
2917         g_value_set_int (&val, 32);
2918         gst_value_list_append_value (&list, &val);
2919         g_value_reset (&val);
2920         g_value_set_int (&val, 24);
2921         gst_value_list_append_value (&list, &val);
2922         g_value_unset (&val);
2923         gst_structure_set_value (s2, "depth", &list);
2924         gst_structure_set_value (s2, "bpp", &list);
2925         g_value_unset (&list);
2926         gst_caps_append_structure (to, s2);
2927       } else {
2928         gst_structure_free (s2);
2929       }
2930     }
2931   } else if (g_str_equal (name, "video/x-raw-rgb")) {
2932     gint bpp;
2933
2934     if (gst_structure_get_int (structure, "bpp", &bpp) &&
2935         (bpp == 32 || bpp == 24)) {
2936       GValue list = { 0, };
2937       GValue val = { 0, };
2938       GstStructure *s2;
2939
2940       /* get rid of format */
2941       gst_structure_remove_field (structure, "depth");
2942       gst_structure_remove_field (structure, "bpp");
2943       gst_structure_remove_field (structure, "red_mask");
2944       gst_structure_remove_field (structure, "green_mask");
2945       gst_structure_remove_field (structure, "blue_mask");
2946       gst_structure_remove_field (structure, "alpha_mask");
2947
2948       s2 = gst_structure_copy (structure);
2949
2950       g_value_init (&list, GST_TYPE_LIST);
2951       g_value_init (&val, G_TYPE_INT);
2952       g_value_set_int (&val, 32);
2953       gst_value_list_append_value (&list, &val);
2954       g_value_reset (&val);
2955       g_value_set_int (&val, 24);
2956       gst_value_list_append_value (&list, &val);
2957       g_value_unset (&val);
2958       gst_structure_set_value (structure, "depth", &list);
2959       gst_structure_set_value (structure, "bpp", &list);
2960       g_value_unset (&list);
2961
2962       gst_structure_set_name (s2, "video/x-raw-yuv");
2963       gst_structure_set (s2, "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
2964           NULL);
2965       gst_caps_append_structure (to, s2);
2966     }
2967   }
2968
2969   /* filter against set allowed caps on the pad */
2970   other = (direction == GST_PAD_SINK) ? trans->srcpad : trans->sinkpad;
2971
2972   templ = gst_pad_get_pad_template_caps (other);
2973   ret = gst_caps_intersect (to, templ);
2974   gst_caps_unref (to);
2975
2976   GST_DEBUG_OBJECT (video_box, "direction %d, transformed %" GST_PTR_FORMAT
2977       " to %" GST_PTR_FORMAT, direction, from, ret);
2978
2979   return ret;
2980 }
2981
2982 static gboolean
2983 gst_video_box_recalc_transform (GstVideoBox * video_box)
2984 {
2985   gboolean res = TRUE;
2986
2987   /* if we have the same format in and out and we don't need to perform any
2988    * cropping at all, we can just operate in passthrough mode */
2989   if (video_box->in_format == video_box->out_format &&
2990       video_box->box_left == 0 && video_box->box_right == 0 &&
2991       video_box->box_top == 0 && video_box->box_bottom == 0 &&
2992       video_box->in_sdtv == video_box->out_sdtv) {
2993
2994     GST_LOG_OBJECT (video_box, "we are using passthrough");
2995     gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
2996         TRUE);
2997   } else {
2998     GST_LOG_OBJECT (video_box, "we are not using passthrough");
2999     gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
3000         FALSE);
3001   }
3002   return res;
3003 }
3004
3005 static gboolean
3006 gst_video_box_select_processing_functions (GstVideoBox * video_box)
3007 {
3008   switch (video_box->out_format) {
3009     case GST_VIDEO_FORMAT_AYUV:
3010       video_box->fill = fill_ayuv;
3011       switch (video_box->in_format) {
3012         case GST_VIDEO_FORMAT_AYUV:
3013           video_box->copy = copy_ayuv_ayuv;
3014           break;
3015         case GST_VIDEO_FORMAT_I420:
3016         case GST_VIDEO_FORMAT_YV12:
3017           video_box->copy = copy_i420_ayuv;
3018           break;
3019         case GST_VIDEO_FORMAT_ARGB:
3020         case GST_VIDEO_FORMAT_ABGR:
3021         case GST_VIDEO_FORMAT_RGBA:
3022         case GST_VIDEO_FORMAT_BGRA:
3023         case GST_VIDEO_FORMAT_xRGB:
3024         case GST_VIDEO_FORMAT_xBGR:
3025         case GST_VIDEO_FORMAT_RGBx:
3026         case GST_VIDEO_FORMAT_BGRx:
3027         case GST_VIDEO_FORMAT_RGB:
3028         case GST_VIDEO_FORMAT_BGR:
3029           video_box->copy = copy_rgb32_ayuv;
3030           break;
3031         default:
3032           break;
3033       }
3034       break;
3035     case GST_VIDEO_FORMAT_I420:
3036     case GST_VIDEO_FORMAT_YV12:
3037       video_box->fill = fill_planar_yuv;
3038       switch (video_box->in_format) {
3039         case GST_VIDEO_FORMAT_AYUV:
3040           video_box->copy = copy_ayuv_i420;
3041           break;
3042         case GST_VIDEO_FORMAT_I420:
3043         case GST_VIDEO_FORMAT_YV12:
3044           video_box->copy = copy_i420_i420;
3045           break;
3046         default:
3047           break;
3048       }
3049       break;
3050     case GST_VIDEO_FORMAT_ARGB:
3051     case GST_VIDEO_FORMAT_ABGR:
3052     case GST_VIDEO_FORMAT_RGBA:
3053     case GST_VIDEO_FORMAT_BGRA:
3054     case GST_VIDEO_FORMAT_xRGB:
3055     case GST_VIDEO_FORMAT_xBGR:
3056     case GST_VIDEO_FORMAT_RGBx:
3057     case GST_VIDEO_FORMAT_BGRx:
3058     case GST_VIDEO_FORMAT_RGB:
3059     case GST_VIDEO_FORMAT_BGR:
3060       video_box->fill = (video_box->out_format == GST_VIDEO_FORMAT_BGR
3061           || video_box->out_format ==
3062           GST_VIDEO_FORMAT_RGB) ? fill_rgb24 : fill_rgb32;
3063       switch (video_box->in_format) {
3064         case GST_VIDEO_FORMAT_ARGB:
3065         case GST_VIDEO_FORMAT_ABGR:
3066         case GST_VIDEO_FORMAT_RGBA:
3067         case GST_VIDEO_FORMAT_BGRA:
3068         case GST_VIDEO_FORMAT_xRGB:
3069         case GST_VIDEO_FORMAT_xBGR:
3070         case GST_VIDEO_FORMAT_RGBx:
3071         case GST_VIDEO_FORMAT_BGRx:
3072         case GST_VIDEO_FORMAT_RGB:
3073         case GST_VIDEO_FORMAT_BGR:
3074           video_box->copy = copy_rgb32;
3075           break;
3076         case GST_VIDEO_FORMAT_AYUV:
3077           video_box->copy = copy_ayuv_rgb32;
3078         default:
3079           break;
3080       }
3081       break;
3082     case GST_VIDEO_FORMAT_GRAY8:
3083     case GST_VIDEO_FORMAT_GRAY16_BE:
3084     case GST_VIDEO_FORMAT_GRAY16_LE:
3085       video_box->fill = fill_gray;
3086       switch (video_box->in_format) {
3087         case GST_VIDEO_FORMAT_GRAY8:
3088         case GST_VIDEO_FORMAT_GRAY16_BE:
3089         case GST_VIDEO_FORMAT_GRAY16_LE:
3090           video_box->copy = copy_packed_simple;
3091           break;
3092         default:
3093           break;
3094       }
3095       break;
3096     case GST_VIDEO_FORMAT_YUY2:
3097     case GST_VIDEO_FORMAT_YVYU:
3098     case GST_VIDEO_FORMAT_UYVY:
3099       video_box->fill = fill_yuy2;
3100       switch (video_box->in_format) {
3101         case GST_VIDEO_FORMAT_YUY2:
3102         case GST_VIDEO_FORMAT_YVYU:
3103         case GST_VIDEO_FORMAT_UYVY:
3104           video_box->copy = copy_yuy2_yuy2;
3105           break;
3106         default:
3107           break;
3108       }
3109       break;
3110     case GST_VIDEO_FORMAT_Y444:
3111     case GST_VIDEO_FORMAT_Y42B:
3112     case GST_VIDEO_FORMAT_Y41B:
3113       video_box->fill = fill_planar_yuv;
3114       switch (video_box->in_format) {
3115         case GST_VIDEO_FORMAT_Y444:
3116           video_box->copy = copy_y444_y444;
3117           break;
3118         case GST_VIDEO_FORMAT_Y42B:
3119           video_box->copy = copy_y42b_y42b;
3120           break;
3121         case GST_VIDEO_FORMAT_Y41B:
3122           video_box->copy = copy_y41b_y41b;
3123           break;
3124         default:
3125           break;
3126       }
3127       break;
3128     default:
3129       break;
3130   }
3131
3132   return video_box->fill != NULL && video_box->copy != NULL;
3133 }
3134
3135 static gboolean
3136 gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
3137 {
3138   GstVideoBox *video_box = GST_VIDEO_BOX (trans);
3139   gboolean ret;
3140   const gchar *matrix;
3141
3142   g_mutex_lock (video_box->mutex);
3143
3144   ret =
3145       gst_video_format_parse_caps (in, &video_box->in_format,
3146       &video_box->in_width, &video_box->in_height);
3147   ret &=
3148       gst_video_format_parse_caps (out, &video_box->out_format,
3149       &video_box->out_width, &video_box->out_height);
3150
3151   matrix = gst_video_parse_caps_color_matrix (in);
3152   video_box->in_sdtv = matrix ? g_str_equal (matrix, "sdtv") : TRUE;
3153   matrix = gst_video_parse_caps_color_matrix (out);
3154   video_box->out_sdtv = matrix ? g_str_equal (matrix, "sdtv") : TRUE;
3155
3156   /* something wrong getting the caps */
3157   if (!ret)
3158     goto no_caps;
3159
3160   GST_DEBUG_OBJECT (trans, "Input w: %d h: %d", video_box->in_width,
3161       video_box->in_height);
3162   GST_DEBUG_OBJECT (trans, "Output w: %d h: %d", video_box->out_width,
3163       video_box->out_height);
3164
3165   if (video_box->autocrop)
3166     gst_video_box_autocrop (video_box);
3167
3168   /* recalc the transformation strategy */
3169   ret = gst_video_box_recalc_transform (video_box);
3170
3171   if (ret)
3172     ret = gst_video_box_select_processing_functions (video_box);
3173   g_mutex_unlock (video_box->mutex);
3174
3175   return ret;
3176
3177   /* ERRORS */
3178 no_caps:
3179   {
3180     GST_DEBUG_OBJECT (video_box,
3181         "Invalid caps: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT, in, out);
3182     g_mutex_unlock (video_box->mutex);
3183     return FALSE;
3184   }
3185 }
3186
3187 static gboolean
3188 gst_video_box_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
3189     guint * size)
3190 {
3191   GstVideoFormat format;
3192   gint width, height;
3193   gboolean ret;
3194
3195   g_assert (size);
3196
3197   ret = gst_video_format_parse_caps (caps, &format, &width, &height);
3198   if (!ret) {
3199     GST_ERROR_OBJECT (trans, "Invalid caps: %" GST_PTR_FORMAT, caps);
3200     return FALSE;
3201   }
3202
3203   *size = gst_video_format_get_size (format, width, height);
3204
3205   GST_LOG_OBJECT (trans, "Returning from _unit_size %d", *size);
3206
3207   return TRUE;
3208 }
3209
3210 static void
3211 gst_video_box_fixate_caps (GstBaseTransform * trans,
3212     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
3213 {
3214   gint width, height;
3215   GstStructure *s;
3216   gboolean ret;
3217
3218   ret = gst_video_format_parse_caps (caps, NULL, &width, &height);
3219   if (!ret)
3220     return;
3221
3222   s = gst_caps_get_structure (othercaps, 0);
3223   gst_structure_fixate_field_nearest_int (s, "width", width);
3224   gst_structure_fixate_field_nearest_int (s, "height", height);
3225 }
3226
3227 static gboolean
3228 gst_video_box_src_event (GstBaseTransform * trans, GstEvent * event)
3229 {
3230   GstVideoBox *video_box = GST_VIDEO_BOX (trans);
3231   GstStructure *new_structure;
3232   const GstStructure *structure;
3233   const gchar *event_name;
3234   gdouble pointer_x;
3235   gdouble pointer_y;
3236
3237   GST_OBJECT_LOCK (video_box);
3238   if (GST_EVENT_TYPE (event) == GST_EVENT_NAVIGATION &&
3239       (video_box->box_left != 0 || video_box->box_top != 0)) {
3240     structure = gst_event_get_structure (event);
3241     event_name = gst_structure_get_string (structure, "event");
3242
3243     if (event_name &&
3244         (strcmp (event_name, "mouse-move") == 0 ||
3245             strcmp (event_name, "mouse-button-press") == 0 ||
3246             strcmp (event_name, "mouse-button-release") == 0)) {
3247       if (gst_structure_get_double (structure, "pointer_x", &pointer_x) &&
3248           gst_structure_get_double (structure, "pointer_y", &pointer_y)) {
3249         gdouble new_pointer_x, new_pointer_y;
3250         GstEvent *new_event;
3251
3252         new_pointer_x = pointer_x + video_box->box_left;
3253         new_pointer_y = pointer_y + video_box->box_top;
3254
3255         new_structure = gst_structure_copy (structure);
3256         gst_structure_set (new_structure,
3257             "pointer_x", G_TYPE_DOUBLE, (gdouble) (new_pointer_x),
3258             "pointer_y", G_TYPE_DOUBLE, (gdouble) (new_pointer_y), NULL);
3259
3260         new_event = gst_event_new_navigation (new_structure);
3261         gst_event_unref (event);
3262         event = new_event;
3263       } else {
3264         GST_WARNING_OBJECT (video_box, "Failed to read navigation event");
3265       }
3266     }
3267   }
3268   GST_OBJECT_UNLOCK (video_box);
3269
3270   return GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
3271 }
3272
3273 static void
3274 gst_video_box_process (GstVideoBox * video_box, const guint8 * src,
3275     guint8 * dest)
3276 {
3277   guint b_alpha = CLAMP (video_box->border_alpha * 256, 0, 255);
3278   guint i_alpha = CLAMP (video_box->alpha * 256, 0, 255);
3279   GstVideoBoxFill fill_type = video_box->fill_type;
3280   gint br, bl, bt, bb, crop_w, crop_h;
3281
3282   crop_h = 0;
3283   crop_w = 0;
3284
3285   br = video_box->box_right;
3286   bl = video_box->box_left;
3287   bt = video_box->box_top;
3288   bb = video_box->box_bottom;
3289
3290   if (br >= 0 && bl >= 0) {
3291     crop_w = video_box->in_width - (br + bl);
3292   } else if (br >= 0 && bl < 0) {
3293     crop_w = video_box->in_width - (br);
3294   } else if (br < 0 && bl >= 0) {
3295     crop_w = video_box->in_width - (bl);
3296   } else if (br < 0 && bl < 0) {
3297     crop_w = video_box->in_width;
3298   }
3299
3300   if (bb >= 0 && bt >= 0) {
3301     crop_h = video_box->in_height - (bb + bt);
3302   } else if (bb >= 0 && bt < 0) {
3303     crop_h = video_box->in_height - (bb);
3304   } else if (bb < 0 && bt >= 0) {
3305     crop_h = video_box->in_height - (bt);
3306   } else if (bb < 0 && bt < 0) {
3307     crop_h = video_box->in_height;
3308   }
3309
3310   GST_DEBUG_OBJECT (video_box, "Borders are: L:%d, R:%d, T:%d, B:%d", bl, br,
3311       bt, bb);
3312   GST_DEBUG_OBJECT (video_box, "Alpha value is: %u (frame) %u (border)",
3313       i_alpha, b_alpha);
3314
3315   if (crop_h < 0 || crop_w < 0) {
3316     video_box->fill (fill_type, b_alpha, video_box->out_format, dest,
3317         video_box->out_sdtv, video_box->out_width, video_box->out_height);
3318   } else if (bb == 0 && bt == 0 && br == 0 && bl == 0) {
3319     video_box->copy (i_alpha, video_box->out_format, dest, video_box->out_sdtv,
3320         video_box->out_width, video_box->out_height, 0, 0, video_box->in_format,
3321         src, video_box->in_sdtv, video_box->in_width, video_box->in_height, 0,
3322         0, crop_w, crop_h);
3323   } else {
3324     gint src_x = 0, src_y = 0;
3325     gint dest_x = 0, dest_y = 0;
3326
3327     /* Fill everything if a border should be added somewhere */
3328     if (bt < 0 || bb < 0 || br < 0 || bl < 0)
3329       video_box->fill (fill_type, b_alpha, video_box->out_format, dest,
3330           video_box->out_sdtv, video_box->out_width, video_box->out_height);
3331
3332     /* Top border */
3333     if (bt < 0) {
3334       dest_y += -bt;
3335     } else {
3336       src_y += bt;
3337     }
3338
3339     /* Left border */
3340     if (bl < 0) {
3341       dest_x += -bl;
3342     } else {
3343       src_x += bl;
3344     }
3345
3346     /* Frame */
3347     video_box->copy (i_alpha, video_box->out_format, dest, video_box->out_sdtv,
3348         video_box->out_width, video_box->out_height, dest_x, dest_y,
3349         video_box->in_format, src, video_box->in_sdtv, video_box->in_width,
3350         video_box->in_height, src_x, src_y, crop_w, crop_h);
3351   }
3352
3353   GST_LOG_OBJECT (video_box, "image created");
3354 }
3355
3356 static void
3357 gst_video_box_before_transform (GstBaseTransform * trans, GstBuffer * in)
3358 {
3359   GstVideoBox *video_box = GST_VIDEO_BOX (trans);
3360   GstClockTime timestamp, stream_time;
3361
3362   timestamp = GST_BUFFER_TIMESTAMP (in);
3363   stream_time =
3364       gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
3365
3366   GST_DEBUG_OBJECT (video_box, "sync to %" GST_TIME_FORMAT,
3367       GST_TIME_ARGS (timestamp));
3368
3369   if (GST_CLOCK_TIME_IS_VALID (stream_time))
3370     gst_object_sync_values (G_OBJECT (video_box), stream_time);
3371 }
3372
3373 static GstFlowReturn
3374 gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
3375     GstBuffer * out)
3376 {
3377   GstVideoBox *video_box = GST_VIDEO_BOX (trans);
3378   const guint8 *indata;
3379   guint8 *outdata;
3380
3381   indata = GST_BUFFER_DATA (in);
3382   outdata = GST_BUFFER_DATA (out);
3383
3384   g_mutex_lock (video_box->mutex);
3385   gst_video_box_process (video_box, indata, outdata);
3386   g_mutex_unlock (video_box->mutex);
3387   return GST_FLOW_OK;
3388 }
3389
3390 /* FIXME: 0.11 merge with videocrop plugin */
3391 static gboolean
3392 plugin_init (GstPlugin * plugin)
3393 {
3394   gst_controller_init (NULL, NULL);
3395
3396   GST_DEBUG_CATEGORY_INIT (videobox_debug, "videobox", 0,
3397       "Resizes a video by adding borders or cropping");
3398
3399   return gst_element_register (plugin, "videobox", GST_RANK_NONE,
3400       GST_TYPE_VIDEO_BOX);
3401 }
3402
3403 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
3404     GST_VERSION_MINOR,
3405     "videobox",
3406     "resizes a video by adding borders or cropping",
3407     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)