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