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