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