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