]> git.sesse.net Git - vlc/blob - modules/video_filter/blend.c
* modules/video_filter/blend.c: fix for chroma planes in ->I420 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 BlendPalette( filter_t *, picture_t *, picture_t *, picture_t *,
53                           int, int );
54
55 /*****************************************************************************
56  * Module descriptor
57  *****************************************************************************/
58 vlc_module_begin();
59     set_description( _("Video pictures blending") );
60     set_capability( "video blending", 100 );
61     set_callbacks( OpenFilter, CloseFilter );
62 vlc_module_end();
63
64 /*****************************************************************************
65  * OpenFilter: probe the filter and return score
66  *****************************************************************************/
67 static int OpenFilter( vlc_object_t *p_this )
68 {
69     filter_t *p_filter = (filter_t*)p_this;
70     filter_sys_t *p_sys;
71
72     /* Check if we can handle that format.
73      * We could try to use a chroma filter if we can't. */
74     if( ( p_filter->fmt_in.video.i_chroma != VLC_FOURCC('Y','U','V','A') &&
75           p_filter->fmt_in.video.i_chroma != VLC_FOURCC('Y','U','V','P') ) ||
76         ( p_filter->fmt_out.video.i_chroma != VLC_FOURCC('I','4','2','0') &&
77           p_filter->fmt_out.video.i_chroma != VLC_FOURCC('Y','V','1','2') &&
78           p_filter->fmt_out.video.i_chroma != VLC_FOURCC('R','V','1','6') &&
79           p_filter->fmt_out.video.i_chroma != VLC_FOURCC('R','V','2','4') &&
80           p_filter->fmt_out.video.i_chroma != VLC_FOURCC('R','V','3','2') ) )
81     {
82         return VLC_EGENERIC;
83     }
84
85     /* Allocate the memory needed to store the decoder's structure */
86     if( ( p_filter->p_sys = p_sys =
87           (filter_sys_t *)malloc(sizeof(filter_sys_t)) ) == NULL )
88     {
89         msg_Err( p_filter, "out of memory" );
90         return VLC_EGENERIC;
91     }
92
93     /* Misc init */
94     p_filter->pf_video_blend = Blend;
95
96     msg_Dbg( p_filter, "chroma: %4.4s -> %4.4s",
97              (char *)&p_filter->fmt_in.video.i_chroma,
98              (char *)&p_filter->fmt_out.video.i_chroma );
99
100
101     return VLC_SUCCESS;
102 }
103
104 /****************************************************************************
105  * Blend: the whole thing
106  ****************************************************************************
107  * This function is called just after the thread is launched.
108  ****************************************************************************/
109 static void Blend( filter_t *p_filter, picture_t *p_dst,
110                    picture_t *p_dst_orig, picture_t *p_src,
111                    int i_x_offset, int i_y_offset )
112 {
113     if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','A') &&
114         ( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('I','4','2','0') ||
115           p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','1','2') ) )
116     {
117         BlendI420( p_filter, p_dst, p_dst_orig, p_src,
118                    i_x_offset, i_y_offset );
119         return;
120     }
121     if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','A') &&
122         p_filter->fmt_out.video.i_chroma == VLC_FOURCC('R','V','1','6') )
123     {
124         BlendR16( p_filter, p_dst, p_dst_orig, p_src,
125                   i_x_offset, i_y_offset );
126         return;
127     }
128     if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','A') &&
129         ( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('R','V','2','4') ||
130           p_filter->fmt_out.video.i_chroma == VLC_FOURCC('R','V','3','2') ) )
131     {
132         BlendR24( p_filter, p_dst, p_dst_orig, p_src,
133                   i_x_offset, i_y_offset );
134         return;
135     }
136     if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','P') &&
137         ( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('I','4','2','0') ||
138           p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','1','2') ) )
139     {
140         BlendPalette( p_filter, p_dst, p_dst_orig, p_src,
141                       i_x_offset, i_y_offset );
142         return;
143     }
144
145     msg_Dbg( p_filter, "no matching alpha blending routine" );
146 }
147
148 static void BlendI420( filter_t *p_filter, picture_t *p_dst,
149                        picture_t *p_dst_orig, picture_t *p_src,
150                        int i_x_offset, int i_y_offset )
151 {
152     filter_sys_t *p_sys = p_filter->p_sys;
153     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
154     uint8_t *p_src1_y, *p_src2_y, *p_dst_y;
155     uint8_t *p_src1_u, *p_src2_u, *p_dst_u;
156     uint8_t *p_src1_v, *p_src2_v, *p_dst_v;
157     uint8_t *p_trans;
158     int i_width, i_height, i_x, i_y;
159     vlc_bool_t b_even_scanline = i_y_offset % 2;
160
161     i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
162     p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
163               p_filter->fmt_out.video.i_x_offset +
164               p_dst->p[Y_PLANE].i_pitch *
165               ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
166     p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
167               p_filter->fmt_out.video.i_x_offset/2 +
168               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
169               p_dst->p[U_PLANE].i_pitch;
170     p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
171               p_filter->fmt_out.video.i_x_offset/2 +
172               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
173               p_dst->p[V_PLANE].i_pitch;
174
175     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
176     p_src1_y = p_dst_orig->p[Y_PLANE].p_pixels + i_x_offset +
177                p_filter->fmt_out.video.i_x_offset +
178                p_dst_orig->p[Y_PLANE].i_pitch *
179                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
180     p_src1_u = p_dst_orig->p[U_PLANE].p_pixels + i_x_offset/2 +
181                p_filter->fmt_out.video.i_x_offset/2 +
182                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
183                p_dst_orig->p[U_PLANE].i_pitch;
184     p_src1_v = p_dst_orig->p[V_PLANE].p_pixels + i_x_offset/2 +
185                p_filter->fmt_out.video.i_x_offset/2 +
186                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
187                p_dst_orig->p[V_PLANE].i_pitch;
188
189     i_src2_pitch = p_src->p[Y_PLANE].i_pitch;
190     p_src2_y = p_src->p[Y_PLANE].p_pixels +
191                p_filter->fmt_in.video.i_x_offset +
192                p_src->p[Y_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
193     p_src2_u = p_src->p[U_PLANE].p_pixels +
194                p_filter->fmt_in.video.i_x_offset/2 +
195                p_src->p[U_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
196     p_src2_v = p_src->p[V_PLANE].p_pixels +
197                p_filter->fmt_in.video.i_x_offset/2 +
198                p_src->p[V_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
199
200     p_trans = p_src->p[A_PLANE].p_pixels +
201               p_filter->fmt_in.video.i_x_offset +
202               p_src->p[A_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
203
204     i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
205                      p_filter->fmt_in.video.i_visible_width );
206
207     i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
208                       p_filter->fmt_in.video.i_visible_height );
209
210 #define MAX_TRANS 255
211 #define TRANS_BITS  8
212
213     /* Draw until we reach the bottom of the subtitle */
214     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
215          p_dst_y += i_dst_pitch, p_src1_y += i_src1_pitch,
216          p_src2_y += i_src2_pitch,
217          p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
218          p_src1_u += b_even_scanline ? i_src1_pitch/2 : 0,
219          p_src2_u += i_src2_pitch,
220          p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
221          p_src1_v += b_even_scanline ? i_src1_pitch/2 : 0,
222          p_src2_v += i_src2_pitch )
223     {
224         b_even_scanline = !b_even_scanline;
225
226         /* Draw until we reach the end of the line */
227         for( i_x = 0; i_x < i_width; i_x++ )
228         {
229             if( !p_trans[i_x] )
230             {
231                 /* Completely transparent. Don't change pixel */
232                 continue;
233             }
234             else if( p_trans[i_x] == MAX_TRANS )
235             {
236                 /* Completely opaque. Completely overwrite underlying pixel */
237                 p_dst_y[i_x] = p_src2_y[i_x];
238
239                 if( b_even_scanline && i_x % 2 == 0 )
240                 {
241                     p_dst_u[i_x/2] = p_src2_u[i_x];
242                     p_dst_v[i_x/2] = p_src2_v[i_x];
243                 }
244                 continue;
245             }
246
247             /* Blending */
248             p_dst_y[i_x] = ( (uint16_t)p_src2_y[i_x] * p_trans[i_x] +
249                 (uint16_t)p_src1_y[i_x] * (MAX_TRANS - p_trans[i_x]) )
250                 >> TRANS_BITS;
251
252             if( b_even_scanline && i_x % 2 == 0 )
253             {
254                 p_dst_u[i_x/2] = ( (uint16_t)p_src2_u[i_x] * p_trans[i_x] +
255                 (uint16_t)p_src1_u[i_x/2] * (MAX_TRANS - p_trans[i_x]) )
256                 >> TRANS_BITS;
257                 p_dst_v[i_x/2] = ( (uint16_t)p_src2_v[i_x] * p_trans[i_x] +
258                 (uint16_t)p_src1_v[i_x/2] * (MAX_TRANS - p_trans[i_x]) )
259                 >> TRANS_BITS;
260             }
261         }
262     }
263
264 #undef MAX_TRANS
265 #undef TRANS_BITS
266
267     return;
268 }
269
270 static inline void yuv_to_rgb( int *r, int *g, int *b,
271                                uint8_t y1, uint8_t u1, uint8_t v1 )
272 {
273     /* macros used for YUV pixel conversions */
274 #   define SCALEBITS 10
275 #   define ONE_HALF  (1 << (SCALEBITS - 1))
276 #   define FIX(x)    ((int) ((x) * (1<<SCALEBITS) + 0.5))
277 #   define CLAMP( x ) (((x) > 255) ? 255 : ((x) < 0) ? 0 : (x));
278
279     int y, cb, cr, r_add, g_add, b_add;
280
281     cb = u1 - 128;
282     cr = v1 - 128;
283     r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;
284     g_add = - FIX(0.34414*255.0/224.0) * cb
285             - FIX(0.71414*255.0/224.0) * cr + ONE_HALF;
286     b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;
287     y = (y1 - 16) * FIX(255.0/219.0);
288     *r = CLAMP((y + r_add) >> SCALEBITS);
289     *g = CLAMP((y + g_add) >> SCALEBITS);
290     *b = CLAMP((y + b_add) >> SCALEBITS);
291 }
292
293 static void BlendR16( filter_t *p_filter, picture_t *p_dst_pic,
294                       picture_t *p_dst_orig, picture_t *p_src,
295                       int i_x_offset, int i_y_offset )
296 {
297     filter_sys_t *p_sys = p_filter->p_sys;
298     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
299     uint8_t *p_dst, *p_src1, *p_src2_y;
300     uint8_t *p_src2_u, *p_src2_v;
301     uint8_t *p_trans;
302     int i_width, i_height, i_x, i_y, i_pix_pitch;
303     int r, g, b;
304
305     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
306     i_dst_pitch = p_dst_pic->p->i_pitch;
307     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
308             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
309             p_dst_pic->p->i_pitch *
310             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
311
312     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
313     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
314                p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
315                p_dst_orig->p->i_pitch *
316                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
317
318     i_src2_pitch = p_src->p[Y_PLANE].i_pitch;
319     p_src2_y = p_src->p[Y_PLANE].p_pixels +
320                p_filter->fmt_in.video.i_x_offset +
321                p_src->p[Y_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
322     p_src2_u = p_src->p[U_PLANE].p_pixels +
323                p_filter->fmt_in.video.i_x_offset/2 +
324                p_src->p[U_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
325     p_src2_v = p_src->p[V_PLANE].p_pixels +
326                p_filter->fmt_in.video.i_x_offset/2 +
327                p_src->p[V_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
328
329     p_trans = p_src->p[A_PLANE].p_pixels +
330               p_filter->fmt_in.video.i_x_offset +
331               p_src->p[A_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
332
333     i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
334                      p_filter->fmt_in.video.i_visible_width );
335
336     i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
337                       p_filter->fmt_in.video.i_visible_height );
338
339 #define MAX_TRANS 255
340 #define TRANS_BITS  8
341
342     /* Draw until we reach the bottom of the subtitle */
343     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
344          p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
345          p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
346          p_src2_v += i_src2_pitch )
347     {
348         /* Draw until we reach the end of the line */
349         for( i_x = 0; i_x < i_width; i_x++ )
350         {
351             if( !p_trans[i_x] )
352             {
353                 /* Completely transparent. Don't change pixel */
354                 continue;
355             }
356             else if( p_trans[i_x] == MAX_TRANS )
357             {
358                 /* Completely opaque. Completely overwrite underlying pixel */
359                 yuv_to_rgb( &r, &g, &b,
360                             p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
361
362     ((uint16_t *)(&p_dst[i_x * i_pix_pitch]))[0] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
363                 continue;
364             }
365
366             /* Blending */
367             yuv_to_rgb( &r, &g, &b,
368                         p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
369
370     ((uint16_t *)(&p_dst[i_x * i_pix_pitch]))[0] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
371         }
372     }
373
374 #undef MAX_TRANS
375 #undef TRANS_BITS
376
377     return;
378 }
379
380 static void BlendR24( filter_t *p_filter, picture_t *p_dst_pic,
381                       picture_t *p_dst_orig, picture_t *p_src,
382                       int i_x_offset, int i_y_offset )
383 {
384     filter_sys_t *p_sys = p_filter->p_sys;
385     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
386     uint8_t *p_dst, *p_src1, *p_src2_y;
387     uint8_t *p_src2_u, *p_src2_v;
388     uint8_t *p_trans;
389     int i_width, i_height, i_x, i_y, i_pix_pitch;
390     int r, g, b;
391
392     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
393     i_dst_pitch = p_dst_pic->p->i_pitch;
394     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
395             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
396             p_dst_pic->p->i_pitch *
397             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
398
399     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
400     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
401                p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
402                p_dst_orig->p->i_pitch *
403                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
404
405     i_src2_pitch = p_src->p[Y_PLANE].i_pitch;
406     p_src2_y = p_src->p[Y_PLANE].p_pixels +
407                p_filter->fmt_in.video.i_x_offset +
408                p_src->p[Y_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
409     p_src2_u = p_src->p[U_PLANE].p_pixels +
410                p_filter->fmt_in.video.i_x_offset/2 +
411                p_src->p[U_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
412     p_src2_v = p_src->p[V_PLANE].p_pixels +
413                p_filter->fmt_in.video.i_x_offset/2 +
414                p_src->p[V_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
415
416     p_trans = p_src->p[A_PLANE].p_pixels +
417               p_filter->fmt_in.video.i_x_offset +
418               p_src->p[A_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
419
420     i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
421                      p_filter->fmt_in.video.i_visible_width );
422
423     i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
424                       p_filter->fmt_in.video.i_visible_height );
425
426 #define MAX_TRANS 255
427 #define TRANS_BITS  8
428
429     /* Draw until we reach the bottom of the subtitle */
430     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
431          p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
432          p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
433          p_src2_v += i_src2_pitch )
434     {
435         /* Draw until we reach the end of the line */
436         for( i_x = 0; i_x < i_width; i_x++ )
437         {
438             if( !p_trans[i_x] )
439             {
440                 /* Completely transparent. Don't change pixel */
441                 continue;
442             }
443             else if( p_trans[i_x] == MAX_TRANS )
444             {
445                 /* Completely opaque. Completely overwrite underlying pixel */
446                 yuv_to_rgb( &r, &g, &b,
447                             p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
448
449                 p_dst[i_x * i_pix_pitch]     = r;
450                 p_dst[i_x * i_pix_pitch + 1] = g;
451                 p_dst[i_x * i_pix_pitch + 2] = b;
452                 continue;
453             }
454
455             /* Blending */
456             yuv_to_rgb( &r, &g, &b,
457                         p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
458
459             p_dst[i_x * i_pix_pitch]     = ( r * p_trans[i_x] +
460                 (uint16_t)p_src1[i_x * i_pix_pitch] *
461                 (MAX_TRANS - p_trans[i_x]) ) >> TRANS_BITS;
462             p_dst[i_x * i_pix_pitch + 1] = ( g * p_trans[i_x] +
463                 (uint16_t)p_src1[i_x * i_pix_pitch + 1] *
464                 (MAX_TRANS - p_trans[i_x]) ) >> TRANS_BITS;
465             p_dst[i_x * i_pix_pitch + 2] = ( b * p_trans[i_x] +
466                 (uint16_t)p_src1[i_x * i_pix_pitch + 2] *
467                 (MAX_TRANS - p_trans[i_x]) ) >> TRANS_BITS;
468         }
469     }
470
471 #undef MAX_TRANS
472 #undef TRANS_BITS
473
474     return;
475 }
476
477 static void BlendPalette( filter_t *p_filter, picture_t *p_dst,
478                           picture_t *p_dst_orig, picture_t *p_src,
479                           int i_x_offset, int i_y_offset )
480 {
481     filter_sys_t *p_sys = p_filter->p_sys;
482     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
483     uint8_t *p_src1_y, *p_src2, *p_dst_y;
484     uint8_t *p_src1_u, *p_dst_u;
485     uint8_t *p_src1_v, *p_dst_v;
486     int i_width, i_height, i_x, i_y;
487     vlc_bool_t b_even_scanline = i_y_offset % 2;
488
489     i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
490     p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
491               p_filter->fmt_out.video.i_x_offset +
492               p_dst->p[Y_PLANE].i_pitch *
493               ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
494     p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
495               p_filter->fmt_out.video.i_x_offset/2 +
496               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
497               p_dst->p[U_PLANE].i_pitch;
498     p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
499               p_filter->fmt_out.video.i_x_offset/2 +
500               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
501               p_dst->p[V_PLANE].i_pitch;
502
503     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
504     p_src1_y = p_dst_orig->p[Y_PLANE].p_pixels + i_x_offset +
505                p_filter->fmt_out.video.i_x_offset +
506                p_dst_orig->p[Y_PLANE].i_pitch *
507                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
508     p_src1_u = p_dst_orig->p[U_PLANE].p_pixels + i_x_offset/2 +
509                p_filter->fmt_out.video.i_x_offset/2 +
510                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
511                p_dst_orig->p[U_PLANE].i_pitch;
512     p_src1_v = p_dst_orig->p[V_PLANE].p_pixels + i_x_offset/2 +
513                p_filter->fmt_out.video.i_x_offset/2 +
514                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
515                p_dst_orig->p[V_PLANE].i_pitch;
516
517     i_src2_pitch = p_src->p->i_pitch;
518     p_src2 = p_src->p->p_pixels + p_filter->fmt_in.video.i_x_offset +
519              i_src2_pitch * p_filter->fmt_in.video.i_y_offset;
520
521     i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
522                      p_filter->fmt_in.video.i_visible_width );
523
524     i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
525                       p_filter->fmt_in.video.i_visible_height );
526
527 #define MAX_TRANS 255
528 #define TRANS_BITS  8
529 #define p_trans p_src2
530 #define p_pal p_filter->fmt_in.video.p_palette->palette
531
532     /* Draw until we reach the bottom of the subtitle */
533     for( i_y = 0; i_y < i_height; i_y++,
534          p_dst_y += i_dst_pitch, p_src1_y += i_src1_pitch,
535          p_src2 += i_src2_pitch,
536          p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
537          p_src1_u += b_even_scanline ? i_src1_pitch/2 : 0,
538          p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
539          p_src1_v += b_even_scanline ? i_src1_pitch/2 : 0 )
540     {
541         b_even_scanline = !b_even_scanline;
542
543         /* Draw until we reach the end of the line */
544         for( i_x = 0; i_x < i_width; i_x++ )
545         {
546             if( !p_pal[p_trans[i_x]][3] )
547             {
548                 /* Completely transparent. Don't change pixel */
549                 continue;
550             }
551             else if( p_pal[p_trans[i_x]][3] == MAX_TRANS )
552             {
553                 /* Completely opaque. Completely overwrite underlying pixel */
554                 p_dst_y[i_x] = p_pal[p_src2[i_x]][0];
555
556                 if( b_even_scanline && i_x % 2 == 0 )
557                 {
558                     p_dst_u[i_x/2] = p_pal[p_src2[i_x]][1];
559                     p_dst_v[i_x/2] = p_pal[p_src2[i_x]][2];
560                 }
561                 continue;
562             }
563
564             /* Blending */
565             p_dst_y[i_x] = ( (uint16_t)p_pal[p_src2[i_x]][0] *
566                 p_pal[p_trans[i_x]][3] + (uint16_t)p_src1_y[i_x] *
567                 (MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
568
569             if( b_even_scanline && i_x % 2 == 0 )
570             {
571                 p_dst_u[i_x/2] = ( (uint16_t)p_pal[p_src2[i_x]][1] *
572                     p_pal[p_trans[i_x]][3] + (uint16_t)p_src1_u[i_x/2] *
573                     (MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
574                 p_dst_v[i_x/2] = ( (uint16_t)p_pal[p_src2[i_x]][2] *
575                     p_pal[p_trans[i_x]][3] + (uint16_t)p_src1_v[i_x/2] *
576                     (MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
577             }
578         }
579     }
580
581 #undef MAX_TRANS
582 #undef TRANS_BITS
583 #undef p_trans
584 #undef p_pal
585
586     return;
587 }
588
589 /*****************************************************************************
590  * CloseFilter: clean up the filter
591  *****************************************************************************/
592 static void CloseFilter( vlc_object_t *p_this )
593 {
594     filter_t *p_filter = (filter_t*)p_this;
595     filter_sys_t *p_sys = p_filter->p_sys;
596
597     free( p_sys );
598 }