]> git.sesse.net Git - vlc/blob - modules/video_filter/blend.c
* modules/video_filter/blend.c: YUVP -> YUY2 alpha-blending.
[vlc] / modules / video_filter / blend.c
1 /*****************************************************************************
2  * blend.c: alpha blend 2 pictures together
3  *****************************************************************************
4  * Copyright (C) 2003 VideoLAN
5  * $Id$
6  *
7  * Author: Gildas Bazin <gbazin@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <vlc/vlc.h>
28 #include <vlc/decoder.h>
29 #include "vlc_filter.h"
30
31 /*****************************************************************************
32  * filter_sys_t : filter descriptor
33  *****************************************************************************/
34 struct filter_sys_t
35 {
36 };
37
38 /****************************************************************************
39  * Local prototypes
40  ****************************************************************************/
41 static int  OpenFilter ( vlc_object_t * );
42 static void CloseFilter( vlc_object_t * );
43
44 static void Blend( filter_t *, picture_t *, picture_t *, picture_t *,
45                    int, int );
46 static void BlendI420( filter_t *, picture_t *, picture_t *, picture_t *,
47                        int, int );
48 static void BlendR16( filter_t *, picture_t *, picture_t *, picture_t *,
49                       int, int );
50 static void BlendR24( filter_t *, picture_t *, picture_t *, picture_t *,
51                       int, int );
52 static void BlendYUY2( filter_t *, picture_t *, picture_t *, picture_t *,
53                        int, int );
54 static void BlendPalI420( filter_t *, picture_t *, picture_t *, picture_t *,
55                           int, int );
56 static void BlendPalYUY2( filter_t *, picture_t *, picture_t *, picture_t *,
57                           int, int );
58
59 /*****************************************************************************
60  * Module descriptor
61  *****************************************************************************/
62 vlc_module_begin();
63     set_description( _("Video pictures blending") );
64     set_capability( "video blending", 100 );
65     set_callbacks( OpenFilter, CloseFilter );
66 vlc_module_end();
67
68 /*****************************************************************************
69  * OpenFilter: probe the filter and return score
70  *****************************************************************************/
71 static int OpenFilter( vlc_object_t *p_this )
72 {
73     filter_t *p_filter = (filter_t*)p_this;
74     filter_sys_t *p_sys;
75
76     /* Check if we can handle that format.
77      * We could try to use a chroma filter if we can't. */
78     if( ( p_filter->fmt_in.video.i_chroma != VLC_FOURCC('Y','U','V','A') &&
79           p_filter->fmt_in.video.i_chroma != VLC_FOURCC('Y','U','V','P') ) ||
80         ( p_filter->fmt_out.video.i_chroma != VLC_FOURCC('I','4','2','0') &&
81           p_filter->fmt_out.video.i_chroma != VLC_FOURCC('Y','U','Y','2') &&
82           p_filter->fmt_out.video.i_chroma != VLC_FOURCC('Y','V','1','2') &&
83           p_filter->fmt_out.video.i_chroma != VLC_FOURCC('R','V','1','6') &&
84           p_filter->fmt_out.video.i_chroma != VLC_FOURCC('R','V','2','4') &&
85           p_filter->fmt_out.video.i_chroma != VLC_FOURCC('R','V','3','2') ) )
86     {
87         return VLC_EGENERIC;
88     }
89
90     /* Allocate the memory needed to store the decoder's structure */
91     if( ( p_filter->p_sys = p_sys =
92           (filter_sys_t *)malloc(sizeof(filter_sys_t)) ) == NULL )
93     {
94         msg_Err( p_filter, "out of memory" );
95         return VLC_EGENERIC;
96     }
97
98     /* Misc init */
99     p_filter->pf_video_blend = Blend;
100
101     msg_Dbg( p_filter, "chroma: %4.4s -> %4.4s",
102              (char *)&p_filter->fmt_in.video.i_chroma,
103              (char *)&p_filter->fmt_out.video.i_chroma );
104
105
106     return VLC_SUCCESS;
107 }
108
109 /****************************************************************************
110  * Blend: the whole thing
111  ****************************************************************************
112  * This function is called just after the thread is launched.
113  ****************************************************************************/
114 static void Blend( filter_t *p_filter, picture_t *p_dst,
115                    picture_t *p_dst_orig, picture_t *p_src,
116                    int i_x_offset, int i_y_offset )
117 {
118     if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','A') &&
119         ( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('I','4','2','0') ||
120           p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','1','2') ) )
121     {
122         BlendI420( p_filter, p_dst, p_dst_orig, p_src,
123                    i_x_offset, i_y_offset );
124         return;
125     }
126     if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','A') &&
127         p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','U','Y','2') )
128     {
129         BlendYUY2( p_filter, p_dst, p_dst_orig, p_src,
130                    i_x_offset, i_y_offset );
131         return;
132     }
133     if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','A') &&
134         p_filter->fmt_out.video.i_chroma == VLC_FOURCC('R','V','1','6') )
135     {
136         BlendR16( p_filter, p_dst, p_dst_orig, p_src,
137                   i_x_offset, i_y_offset );
138         return;
139     }
140     if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','A') &&
141         ( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('R','V','2','4') ||
142           p_filter->fmt_out.video.i_chroma == VLC_FOURCC('R','V','3','2') ) )
143     {
144         BlendR24( p_filter, p_dst, p_dst_orig, p_src,
145                   i_x_offset, i_y_offset );
146         return;
147     }
148     if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','P') &&
149         ( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('I','4','2','0') ||
150           p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','1','2') ) )
151     {
152         BlendPalI420( p_filter, p_dst, p_dst_orig, p_src,
153                       i_x_offset, i_y_offset );
154         return;
155     }
156     if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','P') &&
157         p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','U','Y','2') )
158     {
159         BlendPalYUY2( p_filter, p_dst, p_dst_orig, p_src,
160                       i_x_offset, i_y_offset );
161         return;
162     }
163
164     msg_Dbg( p_filter, "no matching alpha blending routine" );
165 }
166
167 static void BlendI420( filter_t *p_filter, picture_t *p_dst,
168                        picture_t *p_dst_orig, picture_t *p_src,
169                        int i_x_offset, int i_y_offset )
170 {
171     filter_sys_t *p_sys = p_filter->p_sys;
172     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
173     uint8_t *p_src1_y, *p_src2_y, *p_dst_y;
174     uint8_t *p_src1_u, *p_src2_u, *p_dst_u;
175     uint8_t *p_src1_v, *p_src2_v, *p_dst_v;
176     uint8_t *p_trans;
177     int i_width, i_height, i_x, i_y;
178     vlc_bool_t b_even_scanline = i_y_offset % 2;
179
180     i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
181     p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
182               p_filter->fmt_out.video.i_x_offset +
183               p_dst->p[Y_PLANE].i_pitch *
184               ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
185     p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
186               p_filter->fmt_out.video.i_x_offset/2 +
187               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
188               p_dst->p[U_PLANE].i_pitch;
189     p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
190               p_filter->fmt_out.video.i_x_offset/2 +
191               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
192               p_dst->p[V_PLANE].i_pitch;
193
194     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
195     p_src1_y = p_dst_orig->p[Y_PLANE].p_pixels + i_x_offset +
196                p_filter->fmt_out.video.i_x_offset +
197                p_dst_orig->p[Y_PLANE].i_pitch *
198                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
199     p_src1_u = p_dst_orig->p[U_PLANE].p_pixels + i_x_offset/2 +
200                p_filter->fmt_out.video.i_x_offset/2 +
201                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
202                p_dst_orig->p[U_PLANE].i_pitch;
203     p_src1_v = p_dst_orig->p[V_PLANE].p_pixels + i_x_offset/2 +
204                p_filter->fmt_out.video.i_x_offset/2 +
205                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
206                p_dst_orig->p[V_PLANE].i_pitch;
207
208     i_src2_pitch = p_src->p[Y_PLANE].i_pitch;
209     p_src2_y = p_src->p[Y_PLANE].p_pixels +
210                p_filter->fmt_in.video.i_x_offset +
211                p_src->p[Y_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
212     p_src2_u = p_src->p[U_PLANE].p_pixels +
213                p_filter->fmt_in.video.i_x_offset/2 +
214                p_src->p[U_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
215     p_src2_v = p_src->p[V_PLANE].p_pixels +
216                p_filter->fmt_in.video.i_x_offset/2 +
217                p_src->p[V_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
218
219     p_trans = p_src->p[A_PLANE].p_pixels +
220               p_filter->fmt_in.video.i_x_offset +
221               p_src->p[A_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
222
223     i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
224                      p_filter->fmt_in.video.i_visible_width );
225
226     i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
227                       p_filter->fmt_in.video.i_visible_height );
228
229 #define MAX_TRANS 255
230 #define TRANS_BITS  8
231
232     /* Draw until we reach the bottom of the subtitle */
233     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
234          p_dst_y += i_dst_pitch, p_src1_y += i_src1_pitch,
235          p_src2_y += i_src2_pitch,
236          p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
237          p_src1_u += b_even_scanline ? i_src1_pitch/2 : 0,
238          p_src2_u += i_src2_pitch,
239          p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
240          p_src1_v += b_even_scanline ? i_src1_pitch/2 : 0,
241          p_src2_v += i_src2_pitch )
242     {
243         b_even_scanline = !b_even_scanline;
244
245         /* Draw until we reach the end of the line */
246         for( i_x = 0; i_x < i_width; i_x++ )
247         {
248             if( !p_trans[i_x] )
249             {
250                 /* Completely transparent. Don't change pixel */
251                 continue;
252             }
253             else if( p_trans[i_x] == MAX_TRANS )
254             {
255                 /* Completely opaque. Completely overwrite underlying pixel */
256                 p_dst_y[i_x] = p_src2_y[i_x];
257
258                 if( b_even_scanline && i_x % 2 == 0 )
259                 {
260                     p_dst_u[i_x/2] = p_src2_u[i_x];
261                     p_dst_v[i_x/2] = p_src2_v[i_x];
262                 }
263                 continue;
264             }
265
266             /* Blending */
267             p_dst_y[i_x] = ( (uint16_t)p_src2_y[i_x] * p_trans[i_x] +
268                 (uint16_t)p_src1_y[i_x] * (MAX_TRANS - p_trans[i_x]) )
269                 >> TRANS_BITS;
270
271             if( b_even_scanline && i_x % 2 == 0 )
272             {
273                 p_dst_u[i_x/2] = ( (uint16_t)p_src2_u[i_x] * p_trans[i_x] +
274                 (uint16_t)p_src1_u[i_x/2] * (MAX_TRANS - p_trans[i_x]) )
275                 >> TRANS_BITS;
276                 p_dst_v[i_x/2] = ( (uint16_t)p_src2_v[i_x] * p_trans[i_x] +
277                 (uint16_t)p_src1_v[i_x/2] * (MAX_TRANS - p_trans[i_x]) )
278                 >> TRANS_BITS;
279             }
280         }
281     }
282
283 #undef MAX_TRANS
284 #undef TRANS_BITS
285
286     return;
287 }
288
289 static inline void yuv_to_rgb( int *r, int *g, int *b,
290                                uint8_t y1, uint8_t u1, uint8_t v1 )
291 {
292     /* macros used for YUV pixel conversions */
293 #   define SCALEBITS 10
294 #   define ONE_HALF  (1 << (SCALEBITS - 1))
295 #   define FIX(x)    ((int) ((x) * (1<<SCALEBITS) + 0.5))
296 #   define CLAMP( x ) (((x) > 255) ? 255 : ((x) < 0) ? 0 : (x));
297
298     int y, cb, cr, r_add, g_add, b_add;
299
300     cb = u1 - 128;
301     cr = v1 - 128;
302     r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;
303     g_add = - FIX(0.34414*255.0/224.0) * cb
304             - FIX(0.71414*255.0/224.0) * cr + ONE_HALF;
305     b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;
306     y = (y1 - 16) * FIX(255.0/219.0);
307     *r = CLAMP((y + r_add) >> SCALEBITS);
308     *g = CLAMP((y + g_add) >> SCALEBITS);
309     *b = CLAMP((y + b_add) >> SCALEBITS);
310 }
311
312 static void BlendR16( filter_t *p_filter, picture_t *p_dst_pic,
313                       picture_t *p_dst_orig, picture_t *p_src,
314                       int i_x_offset, int i_y_offset )
315 {
316     filter_sys_t *p_sys = p_filter->p_sys;
317     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
318     uint8_t *p_dst, *p_src1, *p_src2_y;
319     uint8_t *p_src2_u, *p_src2_v;
320     uint8_t *p_trans;
321     int i_width, i_height, i_x, i_y, i_pix_pitch;
322     int r, g, b;
323
324     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
325     i_dst_pitch = p_dst_pic->p->i_pitch;
326     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
327             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
328             p_dst_pic->p->i_pitch *
329             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
330
331     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
332     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
333                p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
334                p_dst_orig->p->i_pitch *
335                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
336
337     i_src2_pitch = p_src->p[Y_PLANE].i_pitch;
338     p_src2_y = p_src->p[Y_PLANE].p_pixels +
339                p_filter->fmt_in.video.i_x_offset +
340                p_src->p[Y_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
341     p_src2_u = p_src->p[U_PLANE].p_pixels +
342                p_filter->fmt_in.video.i_x_offset/2 +
343                p_src->p[U_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
344     p_src2_v = p_src->p[V_PLANE].p_pixels +
345                p_filter->fmt_in.video.i_x_offset/2 +
346                p_src->p[V_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
347
348     p_trans = p_src->p[A_PLANE].p_pixels +
349               p_filter->fmt_in.video.i_x_offset +
350               p_src->p[A_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
351
352     i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
353                      p_filter->fmt_in.video.i_visible_width );
354
355     i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
356                       p_filter->fmt_in.video.i_visible_height );
357
358 #define MAX_TRANS 255
359 #define TRANS_BITS  8
360
361     /* Draw until we reach the bottom of the subtitle */
362     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
363          p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
364          p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
365          p_src2_v += i_src2_pitch )
366     {
367         /* Draw until we reach the end of the line */
368         for( i_x = 0; i_x < i_width; i_x++ )
369         {
370             if( !p_trans[i_x] )
371             {
372                 /* Completely transparent. Don't change pixel */
373                 continue;
374             }
375             else if( p_trans[i_x] == MAX_TRANS )
376             {
377                 /* Completely opaque. Completely overwrite underlying pixel */
378                 yuv_to_rgb( &r, &g, &b,
379                             p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
380
381     ((uint16_t *)(&p_dst[i_x * i_pix_pitch]))[0] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
382                 continue;
383             }
384
385             /* Blending */
386             yuv_to_rgb( &r, &g, &b,
387                         p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
388
389     ((uint16_t *)(&p_dst[i_x * i_pix_pitch]))[0] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
390         }
391     }
392
393 #undef MAX_TRANS
394 #undef TRANS_BITS
395
396     return;
397 }
398
399 static void BlendR24( filter_t *p_filter, picture_t *p_dst_pic,
400                       picture_t *p_dst_orig, picture_t *p_src,
401                       int i_x_offset, int i_y_offset )
402 {
403     filter_sys_t *p_sys = p_filter->p_sys;
404     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
405     uint8_t *p_dst, *p_src1, *p_src2_y;
406     uint8_t *p_src2_u, *p_src2_v;
407     uint8_t *p_trans;
408     int i_width, i_height, i_x, i_y, i_pix_pitch;
409     int r, g, b;
410
411     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
412     i_dst_pitch = p_dst_pic->p->i_pitch;
413     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
414             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
415             p_dst_pic->p->i_pitch *
416             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
417
418     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
419     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
420                p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
421                p_dst_orig->p->i_pitch *
422                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
423
424     i_src2_pitch = p_src->p[Y_PLANE].i_pitch;
425     p_src2_y = p_src->p[Y_PLANE].p_pixels +
426                p_filter->fmt_in.video.i_x_offset +
427                p_src->p[Y_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
428     p_src2_u = p_src->p[U_PLANE].p_pixels +
429                p_filter->fmt_in.video.i_x_offset/2 +
430                p_src->p[U_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
431     p_src2_v = p_src->p[V_PLANE].p_pixels +
432                p_filter->fmt_in.video.i_x_offset/2 +
433                p_src->p[V_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
434
435     p_trans = p_src->p[A_PLANE].p_pixels +
436               p_filter->fmt_in.video.i_x_offset +
437               p_src->p[A_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
438
439     i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
440                      p_filter->fmt_in.video.i_visible_width );
441
442     i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
443                       p_filter->fmt_in.video.i_visible_height );
444
445 #define MAX_TRANS 255
446 #define TRANS_BITS  8
447
448     /* Draw until we reach the bottom of the subtitle */
449     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
450          p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
451          p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
452          p_src2_v += i_src2_pitch )
453     {
454         /* Draw until we reach the end of the line */
455         for( i_x = 0; i_x < i_width; i_x++ )
456         {
457             if( !p_trans[i_x] )
458             {
459                 /* Completely transparent. Don't change pixel */
460                 continue;
461             }
462             else if( p_trans[i_x] == MAX_TRANS )
463             {
464                 /* Completely opaque. Completely overwrite underlying pixel */
465                 yuv_to_rgb( &r, &g, &b,
466                             p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
467
468                 p_dst[i_x * i_pix_pitch]     = r;
469                 p_dst[i_x * i_pix_pitch + 1] = g;
470                 p_dst[i_x * i_pix_pitch + 2] = b;
471                 continue;
472             }
473
474             /* Blending */
475             yuv_to_rgb( &r, &g, &b,
476                         p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
477
478             p_dst[i_x * i_pix_pitch]     = ( r * p_trans[i_x] +
479                 (uint16_t)p_src1[i_x * i_pix_pitch] *
480                 (MAX_TRANS - p_trans[i_x]) ) >> TRANS_BITS;
481             p_dst[i_x * i_pix_pitch + 1] = ( g * p_trans[i_x] +
482                 (uint16_t)p_src1[i_x * i_pix_pitch + 1] *
483                 (MAX_TRANS - p_trans[i_x]) ) >> TRANS_BITS;
484             p_dst[i_x * i_pix_pitch + 2] = ( b * p_trans[i_x] +
485                 (uint16_t)p_src1[i_x * i_pix_pitch + 2] *
486                 (MAX_TRANS - p_trans[i_x]) ) >> TRANS_BITS;
487         }
488     }
489
490 #undef MAX_TRANS
491 #undef TRANS_BITS
492
493     return;
494 }
495
496 static void BlendYUY2( filter_t *p_filter, picture_t *p_dst_pic,
497                        picture_t *p_dst_orig, picture_t *p_src,
498                        int i_x_offset, int i_y_offset )
499 {
500     filter_sys_t *p_sys = p_filter->p_sys;
501     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
502     uint8_t *p_dst, *p_src1, *p_src2_y;
503     uint8_t *p_src2_u, *p_src2_v;
504     uint8_t *p_trans;
505     int i_width, i_height, i_x, i_y, i_pix_pitch;
506
507     i_pix_pitch = 2;
508     i_dst_pitch = p_dst_pic->p->i_pitch;
509     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
510             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
511             p_dst_pic->p->i_pitch *
512             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
513
514     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
515     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
516                p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
517                p_dst_orig->p->i_pitch *
518                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
519
520     i_src2_pitch = p_src->p[Y_PLANE].i_pitch;
521     p_src2_y = p_src->p[Y_PLANE].p_pixels +
522                p_filter->fmt_in.video.i_x_offset +
523                p_src->p[Y_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
524     p_src2_u = p_src->p[U_PLANE].p_pixels +
525                p_filter->fmt_in.video.i_x_offset/2 +
526                p_src->p[U_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
527     p_src2_v = p_src->p[V_PLANE].p_pixels +
528                p_filter->fmt_in.video.i_x_offset/2 +
529                p_src->p[V_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
530
531     p_trans = p_src->p[A_PLANE].p_pixels +
532               p_filter->fmt_in.video.i_x_offset +
533               p_src->p[A_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
534
535     i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
536                      p_filter->fmt_in.video.i_visible_width );
537
538     i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
539                       p_filter->fmt_in.video.i_visible_height );
540
541 #define MAX_TRANS 255
542 #define TRANS_BITS  8
543
544     /* Draw until we reach the bottom of the subtitle */
545     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
546          p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
547          p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
548          p_src2_v += i_src2_pitch )
549     {
550         /* Draw until we reach the end of the line */
551         for( i_x = 0; i_x < i_width; i_x += 2 )
552         {
553             if( !p_trans[i_x] )
554             {
555                 /* Completely transparent. Don't change pixel */
556             }
557             else if( p_trans[i_x] == MAX_TRANS )
558             {
559                 /* Completely opaque. Completely overwrite underlying pixel */
560                 p_dst[i_x * 2]     = p_src2_y[i_x];
561                 p_dst[i_x * 2 + 1] = p_src2_u[i_x];
562                 p_dst[i_x * 2 + 3] = p_src2_v[i_x];
563             }
564             else
565             {
566                 /* Blending */
567                 p_dst[i_x * 2]     = ( (uint16_t)p_src2_y[i_x] * p_trans[i_x] +
568                     (uint16_t)p_src1[i_x * 2] *
569                     (MAX_TRANS - p_trans[i_x]) ) >> TRANS_BITS;
570                 p_dst[i_x * 2 + 1] = ( (uint16_t)p_src2_u[i_x] * p_trans[i_x] +
571                     (uint16_t)p_src1[i_x * 2 + 1] *
572                     (MAX_TRANS - p_trans[i_x]) ) >> TRANS_BITS;
573                 p_dst[i_x * 2 + 3] = ( (uint16_t)p_src2_v[i_x] * p_trans[i_x] +
574                     (uint16_t)p_src1[i_x * 2 + 3] *
575                     (MAX_TRANS - p_trans[i_x]) ) >> TRANS_BITS;
576             }
577
578             if( !p_trans[i_x+1] )
579             {
580                 /* Completely transparent. Don't change pixel */
581             }
582             else if( p_trans[i_x+1] == MAX_TRANS )
583             {
584                 /* Completely opaque. Completely overwrite underlying pixel */
585                 p_dst[i_x * 2 + 2] = p_src2_y[i_x + 1];
586             }
587             else
588             {
589                 /* Blending */
590                 p_dst[i_x * 2 + 2] = ( (uint16_t)p_src2_y[i_x+1] *
591                     p_trans[i_x+1] + (uint16_t)p_src1[i_x * 2 + 2] *
592                     (MAX_TRANS - p_trans[i_x+1]) ) >> TRANS_BITS;
593             }
594         }
595     }
596
597 #undef MAX_TRANS
598 #undef TRANS_BITS
599
600     return;
601 }
602
603 static void BlendPalI420( filter_t *p_filter, picture_t *p_dst,
604                           picture_t *p_dst_orig, picture_t *p_src,
605                           int i_x_offset, int i_y_offset )
606 {
607     filter_sys_t *p_sys = p_filter->p_sys;
608     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
609     uint8_t *p_src1_y, *p_src2, *p_dst_y;
610     uint8_t *p_src1_u, *p_dst_u;
611     uint8_t *p_src1_v, *p_dst_v;
612     int i_width, i_height, i_x, i_y;
613     vlc_bool_t b_even_scanline = i_y_offset % 2;
614
615     i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
616     p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
617               p_filter->fmt_out.video.i_x_offset +
618               p_dst->p[Y_PLANE].i_pitch *
619               ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
620     p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
621               p_filter->fmt_out.video.i_x_offset/2 +
622               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
623               p_dst->p[U_PLANE].i_pitch;
624     p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
625               p_filter->fmt_out.video.i_x_offset/2 +
626               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
627               p_dst->p[V_PLANE].i_pitch;
628
629     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
630     p_src1_y = p_dst_orig->p[Y_PLANE].p_pixels + i_x_offset +
631                p_filter->fmt_out.video.i_x_offset +
632                p_dst_orig->p[Y_PLANE].i_pitch *
633                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
634     p_src1_u = p_dst_orig->p[U_PLANE].p_pixels + i_x_offset/2 +
635                p_filter->fmt_out.video.i_x_offset/2 +
636                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
637                p_dst_orig->p[U_PLANE].i_pitch;
638     p_src1_v = p_dst_orig->p[V_PLANE].p_pixels + i_x_offset/2 +
639                p_filter->fmt_out.video.i_x_offset/2 +
640                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
641                p_dst_orig->p[V_PLANE].i_pitch;
642
643     i_src2_pitch = p_src->p->i_pitch;
644     p_src2 = p_src->p->p_pixels + p_filter->fmt_in.video.i_x_offset +
645              i_src2_pitch * p_filter->fmt_in.video.i_y_offset;
646
647     i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
648                      p_filter->fmt_in.video.i_visible_width );
649
650     i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
651                       p_filter->fmt_in.video.i_visible_height );
652
653 #define MAX_TRANS 255
654 #define TRANS_BITS  8
655 #define p_trans p_src2
656 #define p_pal p_filter->fmt_in.video.p_palette->palette
657
658     /* Draw until we reach the bottom of the subtitle */
659     for( i_y = 0; i_y < i_height; i_y++,
660          p_dst_y += i_dst_pitch, p_src1_y += i_src1_pitch,
661          p_src2 += i_src2_pitch,
662          p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
663          p_src1_u += b_even_scanline ? i_src1_pitch/2 : 0,
664          p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
665          p_src1_v += b_even_scanline ? i_src1_pitch/2 : 0 )
666     {
667         b_even_scanline = !b_even_scanline;
668
669         /* Draw until we reach the end of the line */
670         for( i_x = 0; i_x < i_width; i_x++ )
671         {
672             if( !p_pal[p_trans[i_x]][3] )
673             {
674                 /* Completely transparent. Don't change pixel */
675                 continue;
676             }
677             else if( p_pal[p_trans[i_x]][3] == MAX_TRANS )
678             {
679                 /* Completely opaque. Completely overwrite underlying pixel */
680                 p_dst_y[i_x] = p_pal[p_src2[i_x]][0];
681
682                 if( b_even_scanline && i_x % 2 == 0 )
683                 {
684                     p_dst_u[i_x/2] = p_pal[p_src2[i_x]][1];
685                     p_dst_v[i_x/2] = p_pal[p_src2[i_x]][2];
686                 }
687                 continue;
688             }
689
690             /* Blending */
691             p_dst_y[i_x] = ( (uint16_t)p_pal[p_src2[i_x]][0] *
692                 p_pal[p_trans[i_x]][3] + (uint16_t)p_src1_y[i_x] *
693                 (MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
694
695             if( b_even_scanline && i_x % 2 == 0 )
696             {
697                 p_dst_u[i_x/2] = ( (uint16_t)p_pal[p_src2[i_x]][1] *
698                     p_pal[p_trans[i_x]][3] + (uint16_t)p_src1_u[i_x/2] *
699                     (MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
700                 p_dst_v[i_x/2] = ( (uint16_t)p_pal[p_src2[i_x]][2] *
701                     p_pal[p_trans[i_x]][3] + (uint16_t)p_src1_v[i_x/2] *
702                     (MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
703             }
704         }
705     }
706
707 #undef MAX_TRANS
708 #undef TRANS_BITS
709 #undef p_trans
710 #undef p_pal
711
712     return;
713 }
714
715 static void BlendPalYUY2( filter_t *p_filter, picture_t *p_dst_pic,
716                           picture_t *p_dst_orig, picture_t *p_src,
717                           int i_x_offset, int i_y_offset )
718 {
719     filter_sys_t *p_sys = p_filter->p_sys;
720     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
721     uint8_t *p_src1, *p_src2, *p_dst;
722     int i_width, i_height, i_x, i_y, i_pix_pitch;
723
724     i_pix_pitch = 2;
725     i_dst_pitch = p_dst_pic->p->i_pitch;
726     p_dst = p_dst_pic->p->p_pixels + i_pix_pitch * (i_x_offset +
727             p_filter->fmt_out.video.i_x_offset) + p_dst_pic->p->i_pitch *
728             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
729
730     i_src1_pitch = p_dst_orig->p->i_pitch;
731     p_src1 = p_dst_orig->p->p_pixels + i_pix_pitch * (i_x_offset +
732              p_filter->fmt_out.video.i_x_offset) + p_dst_orig->p->i_pitch *
733              ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
734
735     i_src2_pitch = p_src->p->i_pitch;
736     p_src2 = p_src->p->p_pixels + p_filter->fmt_in.video.i_x_offset +
737              i_src2_pitch * p_filter->fmt_in.video.i_y_offset;
738
739     i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
740                      p_filter->fmt_in.video.i_visible_width );
741
742     i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
743                       p_filter->fmt_in.video.i_visible_height );
744
745 #define MAX_TRANS 255
746 #define TRANS_BITS  8
747 #define p_trans p_src2
748 #define p_pal p_filter->fmt_in.video.p_palette->palette
749
750     /* Draw until we reach the bottom of the subtitle */
751     for( i_y = 0; i_y < i_height; i_y++,
752          p_dst += i_dst_pitch, p_src1 += i_src1_pitch, p_src2 += i_src2_pitch )
753     {
754         /* Draw until we reach the end of the line */
755         for( i_x = 0; i_x < i_width; i_x += 2 )
756         {
757             if( !p_pal[p_trans[i_x]][3] )
758             {
759                 /* Completely transparent. Don't change pixel */
760             }
761             else if( p_pal[p_trans[i_x]][3] == MAX_TRANS )
762             {
763                 /* Completely opaque. Completely overwrite underlying pixel */
764                 p_dst[i_x * 2]     = p_pal[p_src2[i_x]][0];
765                 p_dst[i_x * 2 + 1] = p_pal[p_src2[i_x]][1];
766                 p_dst[i_x * 2 + 3] = p_pal[p_src2[i_x]][2];
767             }
768             else
769             {
770                 /* Blending */
771                 p_dst[i_x * 2]     = ( (uint16_t)p_pal[p_src2[i_x]][0] *
772                     p_pal[p_trans[i_x]][3] + (uint16_t)p_src1[i_x * 2] *
773                     (MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
774                 p_dst[i_x * 2 + 1] = ( (uint16_t)p_pal[p_src2[i_x]][1] *
775                     p_pal[p_trans[i_x]][3] + (uint16_t)p_src1[i_x * 2 + 1] *
776                     (MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
777                 p_dst[i_x * 2 + 3] = ( (uint16_t)p_pal[p_src2[i_x]][2] *
778                     p_pal[p_trans[i_x]][3] + (uint16_t)p_src1[i_x * 2 + 3] *
779                     (MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
780             }
781
782             if( !p_pal[p_trans[i_x+1]][3] )
783             {
784                 /* Completely transparent. Don't change pixel */
785             }
786             else if( p_pal[p_trans[i_x+1]][3] == MAX_TRANS )
787             {
788                 /* Completely opaque. Completely overwrite underlying pixel */
789                 p_dst[i_x * 2 + 2] = p_pal[p_src2[i_x + 1]][0];
790             }
791             else
792             {
793                 /* Blending */
794                 p_dst[i_x * 2 + 2] = ( (uint16_t)p_pal[p_src2[i_x+1]][0] *
795                     p_pal[p_trans[i_x+1]][3] + (uint16_t)p_src1[i_x * 2 + 2] *
796                     (MAX_TRANS - p_pal[p_trans[i_x+1]][3]) ) >> TRANS_BITS;
797             }
798         }
799     }
800
801 #undef MAX_TRANS
802 #undef TRANS_BITS
803 #undef p_trans
804 #undef p_pal
805
806     return;
807 }
808
809 /*****************************************************************************
810  * CloseFilter: clean up the filter
811  *****************************************************************************/
812 static void CloseFilter( vlc_object_t *p_this )
813 {
814     filter_t *p_filter = (filter_t*)p_this;
815     filter_sys_t *p_sys = p_filter->p_sys;
816
817     free( p_sys );
818 }