]> git.sesse.net Git - vlc/blob - modules/video_filter/blend.c
More blend clean up.
[vlc] / modules / video_filter / blend.c
1 /*****************************************************************************
2  * blend.c: alpha blend 2 pictures together
3  *****************************************************************************
4  * Copyright (C) 2003-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *          Antoine Cellerier <dionoea @t videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_vout.h>
35 #include "vlc_filter.h"
36
37 /*****************************************************************************
38  * filter_sys_t : filter descriptor
39  *****************************************************************************/
40 struct filter_sys_t
41 {
42     int i_dummy;
43 };
44
45 /****************************************************************************
46  * Local prototypes
47  ****************************************************************************/
48 static int  OpenFilter ( vlc_object_t * );
49 static void CloseFilter( vlc_object_t * );
50
51 static void Blend( filter_t *, picture_t *, picture_t *, picture_t *,
52                    int, int, int );
53
54 /* TODO i_alpha support for BlendR16 */
55 /* YUVA */
56 static void BlendI420( filter_t *, picture_t *, picture_t *, picture_t *,
57                        int, int, int, int, int );
58 static void BlendR16( filter_t *, picture_t *, picture_t *, picture_t *,
59                       int, int, int, int, int );
60 static void BlendR24( filter_t *, picture_t *, picture_t *, picture_t *,
61                       int, int, int, int, int );
62 static void BlendYUVPacked( filter_t *, picture_t *, picture_t *, picture_t *,
63                             int, int, int, int, int );
64
65 /* I420, YV12 */
66 static void BlendI420I420( filter_t *, picture_t *, picture_t *, picture_t *,
67                            int, int, int, int, int );
68 static void BlendI420I420_no_alpha(
69                            filter_t *, picture_t *, picture_t *, picture_t *,
70                            int, int, int, int );
71 static void BlendI420R16( filter_t *, picture_t *, picture_t *, picture_t *,
72                            int, int, int, int, int );
73 static void BlendI420R24( filter_t *, picture_t *, picture_t *, picture_t *,
74                           int, int, int, int, int );
75 static void BlendI420YUVPacked( filter_t *, picture_t *, picture_t *,
76                                 picture_t *, int, int, int, int, int );
77
78 /* YUVP */
79 static void BlendPalI420( filter_t *, picture_t *, picture_t *, picture_t *,
80                           int, int, int, int, int );
81 static void BlendPalYUVPacked( filter_t *, picture_t *, picture_t *, picture_t *,
82                                int, int, int, int, int );
83 static void BlendPalRV( filter_t *, picture_t *, picture_t *, picture_t *,
84                         int, int, int, int, int );
85
86 /* RGBA */
87 static void BlendRGBAI420( filter_t *, picture_t *, picture_t *, picture_t *,
88                            int, int, int, int, int );
89 static void BlendRGBAYUVPacked( filter_t *, picture_t *, picture_t *,
90                                 picture_t *, int, int, int, int, int );
91 static void BlendRGBAR16( filter_t *, picture_t *, picture_t *, picture_t *,
92                           int, int, int, int, int );
93 static void BlendRGBAR24( filter_t *, picture_t *, picture_t *, picture_t *,
94                           int, int, int, int, int );
95
96 /*****************************************************************************
97  * Module descriptor
98  *****************************************************************************/
99 vlc_module_begin();
100     set_description( N_("Video pictures blending") );
101     set_capability( "video blending", 100 );
102     set_callbacks( OpenFilter, CloseFilter );
103 vlc_module_end();
104
105 /*****************************************************************************
106  * OpenFilter: probe the filter and return score
107  *****************************************************************************/
108 static int OpenFilter( vlc_object_t *p_this )
109 {
110     filter_t *p_filter = (filter_t*)p_this;
111     filter_sys_t *p_sys;
112
113     /* Check if we can handle that format.
114      * We could try to use a chroma filter if we can't. */
115     int in_chroma = p_filter->fmt_in.video.i_chroma;
116     int out_chroma = p_filter->fmt_out.video.i_chroma;
117     if( ( in_chroma  != VLC_FOURCC('Y','U','V','A') &&
118           in_chroma  != VLC_FOURCC('I','4','2','0') &&
119           in_chroma  != VLC_FOURCC('Y','V','1','2') &&
120           in_chroma  != VLC_FOURCC('Y','U','V','P') &&
121           in_chroma  != VLC_FOURCC('R','G','B','A') ) ||
122         ( out_chroma != VLC_FOURCC('I','4','2','0') &&
123           out_chroma != VLC_FOURCC('Y','U','Y','2') &&
124           out_chroma != VLC_FOURCC('Y','V','1','2') &&
125           out_chroma != VLC_FOURCC('U','Y','V','Y') &&
126           out_chroma != VLC_FOURCC('Y','V','Y','U') &&
127           out_chroma != VLC_FOURCC('R','V','1','6') &&
128           out_chroma != VLC_FOURCC('R','V','2','4') &&
129           out_chroma != VLC_FOURCC('R','V','3','2') ) )
130     {
131         return VLC_EGENERIC;
132     }
133
134     /* Allocate the memory needed to store the decoder's structure */
135     if( ( p_filter->p_sys = p_sys =
136           (filter_sys_t *)malloc(sizeof(filter_sys_t)) ) == NULL )
137         return VLC_ENOMEM;
138
139     /* Misc init */
140     p_filter->pf_video_blend = Blend;
141
142     msg_Dbg( p_filter, "chroma: %4.4s -> %4.4s",
143              (char *)&p_filter->fmt_in.video.i_chroma,
144              (char *)&p_filter->fmt_out.video.i_chroma );
145
146     return VLC_SUCCESS;
147 }
148
149 /*****************************************************************************
150  * CloseFilter: clean up the filter
151  *****************************************************************************/
152 static void CloseFilter( vlc_object_t *p_this )
153 {
154     filter_t *p_filter = (filter_t*)p_this;
155     filter_sys_t *p_sys = p_filter->p_sys;
156
157     free( p_sys );
158 }
159
160 /****************************************************************************
161  * Blend: the whole thing
162  ****************************************************************************
163  * This function is called just after the thread is launched.
164  ****************************************************************************/
165 static void Blend( filter_t *p_filter, picture_t *p_dst,
166                    picture_t *p_dst_orig, picture_t *p_src,
167                    int i_x_offset, int i_y_offset, int i_alpha )
168 {
169     int i_width, i_height;
170
171     if( i_alpha == 0 ) return;
172
173     i_width = __MIN((int)p_filter->fmt_out.video.i_visible_width - i_x_offset,
174                     (int)p_filter->fmt_in.video.i_visible_width);
175
176     i_height = __MIN((int)p_filter->fmt_out.video.i_visible_height -i_y_offset,
177                      (int)p_filter->fmt_in.video.i_visible_height);
178
179     if( i_width <= 0 || i_height <= 0 ) return;
180
181 #if 0
182     msg_Dbg( p_filter, "chroma: %4.4s -> %4.4s\n",
183              (char *)&p_filter->fmt_in.video.i_chroma,
184              (char *)&p_filter->fmt_out.video.i_chroma );
185 #endif
186
187     switch( p_filter->fmt_in.video.i_chroma )
188     {
189         case VLC_FOURCC('Y','U','V','A'):
190             switch( p_filter->fmt_out.video.i_chroma )
191             {
192                 case VLC_FOURCC('I','4','2','0'):
193                 case VLC_FOURCC('Y','V','1','2'):
194                     BlendI420( p_filter, p_dst, p_dst_orig, p_src,
195                                i_x_offset, i_y_offset,
196                                i_width, i_height, i_alpha );
197                     return;
198                 case VLC_FOURCC('Y','U','Y','2'):
199                 case VLC_FOURCC('U','Y','V','Y'):
200                 case VLC_FOURCC('Y','V','Y','U'):
201                     BlendYUVPacked( p_filter, p_dst, p_dst_orig, p_src,
202                                     i_x_offset, i_y_offset,
203                                     i_width, i_height, i_alpha );
204                     return;
205                 case VLC_FOURCC('R','V','1','6'):
206                     BlendR16( p_filter, p_dst, p_dst_orig, p_src,
207                               i_x_offset, i_y_offset,
208                               i_width, i_height, i_alpha );
209                     return;
210                 case VLC_FOURCC('R','V','2','4'):
211                 case VLC_FOURCC('R','V','3','2'):
212                     BlendR24( p_filter, p_dst, p_dst_orig, p_src,
213                               i_x_offset, i_y_offset,
214                               i_width, i_height, i_alpha );
215                     return;
216             }
217         case VLC_FOURCC('Y','U','V','P'):
218             switch( p_filter->fmt_out.video.i_chroma )
219             {
220                 case VLC_FOURCC('I','4','2','0'):
221                 case VLC_FOURCC('Y','V','1','2'):
222                     BlendPalI420( p_filter, p_dst, p_dst_orig, p_src,
223                                   i_x_offset, i_y_offset,
224                                   i_width, i_height, i_alpha );
225                     return;
226                 case VLC_FOURCC('Y','U','Y','2'):
227                 case VLC_FOURCC('U','Y','V','Y'):
228                 case VLC_FOURCC('Y','V','Y','U'):
229                     BlendPalYUVPacked( p_filter, p_dst, p_dst_orig, p_src,
230                                        i_x_offset, i_y_offset,
231                                        i_width, i_height, i_alpha );
232                     return;
233                 case VLC_FOURCC('R','V','1','6'):
234                 case VLC_FOURCC('R','V','2','4'):
235                 case VLC_FOURCC('R','V','3','2'):
236                     BlendPalRV( p_filter, p_dst, p_dst_orig, p_src,
237                                 i_x_offset, i_y_offset,
238                                 i_width, i_height, i_alpha );
239                     return;
240             }
241         case VLC_FOURCC('Y','V','1','2'):
242         case VLC_FOURCC('I','4','2','0'):
243             switch( p_filter->fmt_out.video.i_chroma )
244             {
245                 case VLC_FOURCC('I','4','2','0'):
246                 case VLC_FOURCC('Y','V','1','2'):
247                     if( i_alpha == 0xff )
248                         BlendI420I420_no_alpha(
249                                    p_filter, p_dst, p_dst_orig, p_src,
250                                    i_x_offset, i_y_offset,
251                                    i_width, i_height );
252                     else
253                         BlendI420I420( p_filter, p_dst, p_dst_orig, p_src,
254                                        i_x_offset, i_y_offset,
255                                        i_width, i_height, i_alpha );
256                     return;
257                 case VLC_FOURCC('Y','U','Y','2'):
258                 case VLC_FOURCC('U','Y','V','Y'):
259                 case VLC_FOURCC('Y','V','Y','U'):
260                     BlendI420YUVPacked( p_filter, p_dst, p_dst_orig, p_src,
261                                         i_x_offset, i_y_offset,
262                                         i_width, i_height, i_alpha );
263                     return;
264                 case VLC_FOURCC('R','V','1','6'):
265                     BlendI420R16( p_filter, p_dst, p_dst_orig, p_src,
266                                   i_x_offset, i_y_offset,
267                                   i_width, i_height, i_alpha );
268                     return;
269                 case VLC_FOURCC('R','V','2','4'):
270                 case VLC_FOURCC('R','V','3','2'):
271                     BlendI420R24( p_filter, p_dst, p_dst_orig, p_src,
272                                   i_x_offset, i_y_offset,
273                                   i_width, i_height, i_alpha );
274                     return;
275             }
276         case VLC_FOURCC('R','G','B','A'):
277             switch( p_filter->fmt_out.video.i_chroma )
278             {
279                 case VLC_FOURCC('I','4','2','0'):
280                 case VLC_FOURCC('Y','V','1','2'):
281                     BlendRGBAI420( p_filter, p_dst, p_dst_orig, p_src,
282                                    i_x_offset, i_y_offset,
283                                    i_width, i_height, i_alpha );
284                     return;
285                 case VLC_FOURCC('Y','U','Y','2'):
286                 case VLC_FOURCC('U','Y','V','Y'):
287                 case VLC_FOURCC('Y','V','Y','U'):
288                     BlendRGBAYUVPacked( p_filter, p_dst, p_dst_orig, p_src,
289                                         i_x_offset, i_y_offset,
290                                         i_width, i_height, i_alpha );
291                     return;
292                 case VLC_FOURCC('R','V','2','4'):
293                 case VLC_FOURCC('R','V','3','2'):
294                     BlendRGBAR24( p_filter, p_dst, p_dst_orig, p_src,
295                                   i_x_offset, i_y_offset,
296                                   i_width, i_height, i_alpha );
297                     return;
298                 case VLC_FOURCC('R','V','1','6'):
299                     BlendRGBAR16( p_filter, p_dst, p_dst_orig, p_src,
300                                   i_x_offset, i_y_offset,
301                                   i_width, i_height, i_alpha );
302                     return;
303             }
304     }
305
306     msg_Dbg( p_filter, "no matching alpha blending routine "
307              "(chroma: %4.4s -> %4.4s)",
308              (char *)&p_filter->fmt_in.video.i_chroma,
309              (char *)&p_filter->fmt_out.video.i_chroma );
310 }
311
312 /***********************************************************************
313  * Utils
314  ***********************************************************************/
315 static inline uint8_t vlc_uint8( int v )
316 {
317     if( v > 255 )
318         return 255;
319     else if( v < 0 )
320         return 0;
321     return v;
322 }
323
324 #define MAX_TRANS 255
325 #define TRANS_BITS  8
326
327 static inline int vlc_blend( int v1, int v2, int a )
328 {
329     /* TODO bench if the tests really increase speed */
330     if( a == 0 )
331         return v2;
332     else if( a == MAX_TRANS )
333         return v1;
334     return ( v1 * a + v2 * (MAX_TRANS - a ) ) >> TRANS_BITS;
335 }
336
337 static inline int vlc_alpha( int t, int a )
338 {
339     return (t * a) / 255;
340 }
341
342 static inline void yuv_to_rgb( int *r, int *g, int *b,
343                                uint8_t y1, uint8_t u1, uint8_t v1 )
344 {
345     /* macros used for YUV pixel conversions */
346 #   define SCALEBITS 10
347 #   define ONE_HALF  (1 << (SCALEBITS - 1))
348 #   define FIX(x)    ((int) ((x) * (1<<SCALEBITS) + 0.5))
349
350     int y, cb, cr, r_add, g_add, b_add;
351
352     cb = u1 - 128;
353     cr = v1 - 128;
354     r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;
355     g_add = - FIX(0.34414*255.0/224.0) * cb
356             - FIX(0.71414*255.0/224.0) * cr + ONE_HALF;
357     b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;
358     y = (y1 - 16) * FIX(255.0/219.0);
359     *r = vlc_uint8( (y + r_add) >> SCALEBITS );
360     *g = vlc_uint8( (y + g_add) >> SCALEBITS );
361     *b = vlc_uint8( (y + b_add) >> SCALEBITS );
362 #undef FIX
363 #undef ONE_HALF
364 #undef SCALEBITS
365 }
366
367 static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
368                                int r, int g, int b )
369 {
370     *y = ( ( (  66 * r + 129 * g +  25 * b + 128 ) >> 8 ) + 16 );
371     *u =   ( ( -38 * r -  74 * g + 112 * b + 128 ) >> 8 ) + 128 ;
372     *v =   ( ( 112 * r -  94 * g -  18 * b + 128 ) >> 8 ) + 128 ;
373 }
374
375 static uint8_t *vlc_plane_start( int *pi_pitch,
376                                  picture_t *p_picture,
377                                  int i_plane,
378                                  int i_x_offset, int i_y_offset,
379                                  const video_format_t *p_fmt,
380                                  int r )
381 {
382     const int i_pitch = p_picture->p[i_plane].i_pitch;
383     uint8_t *p_pixels = p_picture->p[i_plane].p_pixels;
384
385     const int i_dx = ( i_x_offset + p_fmt->i_x_offset ) / r;
386     const int i_dy = ( i_y_offset + p_fmt->i_y_offset ) / r;
387
388     if( pi_pitch )
389         *pi_pitch = i_pitch;
390     return &p_pixels[ i_dy * i_pitch + i_dx ];
391 }
392
393 /***********************************************************************
394  * YUVA
395  ***********************************************************************/
396 static void BlendI420( filter_t *p_filter, picture_t *p_dst,
397                        picture_t *p_dst_orig, picture_t *p_src,
398                        int i_x_offset, int i_y_offset,
399                        int i_width, int i_height, int i_alpha )
400 {
401     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
402     uint8_t *p_src1_y, *p_src2_y, *p_dst_y;
403     uint8_t *p_src1_u, *p_src2_u, *p_dst_u;
404     uint8_t *p_src1_v, *p_src2_v, *p_dst_v;
405     uint8_t *p_trans;
406     int i_x, i_y, i_trans = 0;
407     bool b_even_scanline = i_y_offset % 2;
408
409     p_dst_y = vlc_plane_start( &i_dst_pitch, p_dst, Y_PLANE,
410                                i_x_offset, i_y_offset, &p_filter->fmt_out.video, 1 );
411     p_dst_u = vlc_plane_start( NULL, p_dst, U_PLANE,
412                                i_x_offset, i_y_offset, &p_filter->fmt_out.video, 2 );
413     p_dst_v = vlc_plane_start( NULL, p_dst, V_PLANE,
414                                i_x_offset, i_y_offset, &p_filter->fmt_out.video, 2 );
415
416     p_src1_y = vlc_plane_start( &i_src1_pitch, p_dst_orig, Y_PLANE,
417                                 i_x_offset, i_y_offset, &p_filter->fmt_out.video, 1 );
418     p_src1_u = vlc_plane_start( NULL, p_dst_orig, U_PLANE,
419                                 i_x_offset, i_y_offset, &p_filter->fmt_out.video, 2 );
420     p_src1_v = vlc_plane_start( NULL, p_dst_orig, V_PLANE,
421                                 i_x_offset, i_y_offset, &p_filter->fmt_out.video, 2 );
422
423     p_src2_y = vlc_plane_start( &i_src2_pitch, p_src, Y_PLANE,
424                                 0, 0, &p_filter->fmt_in.video, 1 );
425     p_src2_u = vlc_plane_start( NULL, p_src, U_PLANE,
426                                 0, 0, &p_filter->fmt_in.video, 2 );
427     p_src2_v = vlc_plane_start( NULL, p_src, V_PLANE,
428                                 0, 0, &p_filter->fmt_in.video, 2 );
429     p_trans = vlc_plane_start( NULL, p_src, A_PLANE,
430                                0, 0, &p_filter->fmt_in.video, 1 );
431
432     /* Draw until we reach the bottom of the subtitle */
433     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
434          p_dst_y += i_dst_pitch, p_src1_y += i_src1_pitch,
435          p_src2_y += i_src2_pitch,
436          p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
437          p_src1_u += b_even_scanline ? i_src1_pitch/2 : 0,
438          p_src2_u += i_src2_pitch,
439          p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
440          p_src1_v += b_even_scanline ? i_src1_pitch/2 : 0,
441          p_src2_v += i_src2_pitch )
442     {
443         b_even_scanline = !b_even_scanline;
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 )
449                 i_trans = vlc_alpha( p_trans[i_x], i_alpha );
450
451             if( !i_trans )
452                 continue;
453
454             /* Blending */
455             p_dst_y[i_x] = vlc_blend( p_src2_y[i_x], p_src1_y[i_x], i_trans );
456             if( b_even_scanline && i_x % 2 == 0 )
457             {
458                 p_dst_u[i_x/2] = vlc_blend( p_src2_u[i_x], p_src1_u[i_x/2], i_trans );
459                 p_dst_v[i_x/2] = vlc_blend( p_src2_v[i_x], p_src1_v[i_x/2], i_trans );
460             }
461         }
462     }
463 }
464
465 static void BlendR16( filter_t *p_filter, picture_t *p_dst_pic,
466                       picture_t *p_dst_orig, picture_t *p_src,
467                       int i_x_offset, int i_y_offset,
468                       int i_width, int i_height, int i_alpha )
469 {
470     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
471     uint8_t *p_dst, *p_src1, *p_src2_y;
472     uint8_t *p_src2_u, *p_src2_v;
473     uint8_t *p_trans;
474     int i_x, i_y, i_pix_pitch, i_trans = 0;
475     int r, g, b;
476
477     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
478     i_dst_pitch = p_dst_pic->p->i_pitch;
479     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
480             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
481             p_dst_pic->p->i_pitch *
482             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
483
484     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
485     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
486                p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
487                p_dst_orig->p->i_pitch *
488                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
489
490     p_src2_y = vlc_plane_start( &i_src2_pitch, p_src, Y_PLANE,
491                                 0, 0, &p_filter->fmt_in.video, 1 );
492     p_src2_u = vlc_plane_start( NULL, p_src, U_PLANE,
493                                 0, 0, &p_filter->fmt_in.video, 2 );
494     p_src2_v = vlc_plane_start( NULL, p_src, V_PLANE,
495                                 0, 0, &p_filter->fmt_in.video, 2 );
496     p_trans = vlc_plane_start( NULL, p_src, A_PLANE,
497                                0, 0, &p_filter->fmt_in.video, 1 );
498
499     /* Draw until we reach the bottom of the subtitle */
500     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
501          p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
502          p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
503          p_src2_v += i_src2_pitch )
504     {
505         /* Draw until we reach the end of the line */
506         for( i_x = 0; i_x < i_width; i_x++ )
507         {
508             if( p_trans )
509                 i_trans = vlc_alpha( p_trans[i_x], i_alpha );
510             if( !i_trans )
511                 continue;
512
513             /* Blending */
514             /* FIXME: do the blending
515              * FIXME use rgb shift (when present) */
516             yuv_to_rgb( &r, &g, &b,
517                         p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
518
519     ((uint16_t *)(&p_dst[i_x * i_pix_pitch]))[0] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
520         }
521     }
522 }
523
524 static void BlendR24( filter_t *p_filter, picture_t *p_dst_pic,
525                       picture_t *p_dst_orig, picture_t *p_src,
526                       int i_x_offset, int i_y_offset,
527                       int i_width, int i_height, int i_alpha )
528 {
529     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
530     uint8_t *p_dst, *p_src1, *p_src2_y;
531     uint8_t *p_src2_u, *p_src2_v;
532     uint8_t *p_trans;
533     int i_x, i_y, i_pix_pitch, i_trans = 0;
534     int r, g, b;
535
536     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
537     i_dst_pitch = p_dst_pic->p->i_pitch;
538     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
539             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
540             p_dst_pic->p->i_pitch *
541             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
542
543     i_src1_pitch = p_dst_orig->p->i_pitch;
544     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
545                p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
546                p_dst_orig->p->i_pitch *
547                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
548
549     p_src2_y = vlc_plane_start( &i_src2_pitch, p_src, Y_PLANE,
550                                 0, 0, &p_filter->fmt_in.video, 1 );
551     p_src2_u = vlc_plane_start( NULL, p_src, U_PLANE,
552                                 0, 0, &p_filter->fmt_in.video, 2 );
553     p_src2_v = vlc_plane_start( NULL, p_src, V_PLANE,
554                                 0, 0, &p_filter->fmt_in.video, 2 );
555     p_trans = vlc_plane_start( NULL, p_src, A_PLANE,
556                                0, 0, &p_filter->fmt_in.video, 1 );
557
558     if( (i_pix_pitch == 4)
559      && (((((intptr_t)p_dst)|((intptr_t)p_src1)|i_dst_pitch|i_src1_pitch)
560           & 3) == 0) )
561     {
562         /*
563         ** if picture pixels are 32 bits long and lines addresses are 32 bit
564         ** aligned, optimize rendering
565         */
566         uint32_t *p32_dst = (uint32_t *)p_dst;
567         uint32_t i32_dst_pitch = (uint32_t)(i_dst_pitch>>2);
568         uint32_t *p32_src1 = (uint32_t *)p_src1;
569         uint32_t i32_src1_pitch = (uint32_t)(i_src1_pitch>>2);
570
571         int i_rshift, i_gshift, i_bshift;
572         uint32_t i_rmask, i_gmask, i_bmask;
573
574         if( p_dst_pic->p_heap )
575         {
576             i_rmask = p_dst_pic->p_heap->i_rmask;
577             i_gmask = p_dst_pic->p_heap->i_gmask;
578             i_bmask = p_dst_pic->p_heap->i_bmask;
579             i_rshift = p_dst_pic->p_heap->i_lrshift;
580             i_gshift = p_dst_pic->p_heap->i_lgshift;
581             i_bshift = p_dst_pic->p_heap->i_lbshift;
582         }
583         else
584         {
585             i_rmask = p_dst_pic->format.i_rmask;
586             i_gmask = p_dst_pic->format.i_gmask;
587             i_bmask = p_dst_pic->format.i_bmask;
588
589             if( (i_rmask == 0x00FF0000)
590              && (i_gmask == 0x0000FF00)
591              && (i_bmask == 0x000000FF) )
592             {
593                 /* X8R8G8B8 pixel layout */
594                 i_rshift = 16;
595                 i_bshift = 8;
596                 i_gshift = 0;
597             }
598             else if( (i_rmask == 0xFF000000)
599                   && (i_gmask == 0x00FF0000)
600                   && (i_bmask == 0x0000FF00) )
601             {
602                 /* R8G8B8X8 pixel layout */
603                 i_rshift = 24;
604                 i_bshift = 16;
605                 i_gshift = 8;
606             }
607             else
608             {
609                 goto slower;
610             }
611         }
612         /* Draw until we reach the bottom of the subtitle */
613         for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
614              p32_dst += i32_dst_pitch, p32_src1 += i32_src1_pitch,
615              p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
616              p_src2_v += i_src2_pitch )
617         {
618             /* Draw until we reach the end of the line */
619             for( i_x = 0; i_x < i_width; i_x++ )
620             {
621                 if( p_trans )
622                     i_trans = vlc_alpha( p_trans[i_x], i_alpha );
623                 if( !i_trans )
624                     continue;
625
626                 if( i_trans == MAX_TRANS )
627                 {
628                     /* Completely opaque. Completely overwrite underlying pixel */
629                     yuv_to_rgb( &r, &g, &b,
630                                 p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
631
632                     p32_dst[i_x] = (r<<i_rshift) |
633                                    (g<<i_gshift) |
634                                    (b<<i_bshift);
635                 }
636                 else
637                 {
638                     /* Blending */
639                     uint32_t i_pix_src1 = p32_src1[i_x];
640                     yuv_to_rgb( &r, &g, &b,
641                                 p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
642
643                     p32_dst[i_x] = ( vlc_blend( r, (i_pix_src1 & i_rmask)>>i_rshift, i_trans ) << i_rshift ) |
644                                    ( vlc_blend( g, (i_pix_src1 & i_gmask)>>i_gshift, i_trans ) << i_gshift ) |
645                                    ( vlc_blend( b, (i_pix_src1 & i_bmask)>>i_bshift, i_trans ) << i_bshift );
646                 }
647             }
648         }
649     }
650     else
651     {
652         int i_rindex, i_bindex, i_gindex;
653         uint32_t i_rmask, i_gmask, i_bmask;
654
655         slower:
656
657         i_rmask = p_dst_pic->format.i_rmask;
658         i_gmask = p_dst_pic->format.i_gmask;
659         i_bmask = p_dst_pic->format.i_bmask;
660
661         /*
662         ** quick and dirty way to get byte index from mask
663         ** will only work correctly if mask are 8 bit aligned
664         ** and are 8 bit long
665         */
666 #ifdef WORDS_BIGENDIAN
667         i_rindex = ((i_rmask>>16) & 1)
668                  | ((i_rmask>>8) & 2)
669                  | ((i_rmask) & 3);
670         i_gindex = ((i_gmask>>16) & 1)
671                  | ((i_gmask>>8) & 2)
672                  | ((i_gmask) & 3);
673         i_bindex = ((i_bmask>>16) & 1)
674                  | ((i_bmask>>8) & 2)
675                  | ((i_bmask) & 3);
676 #else
677         i_rindex = ((i_rmask>>24) & 3)
678                  | ((i_rmask>>16) & 2)
679                  | ((i_rmask>>8) & 1);
680         i_gindex = ((i_gmask>>24) & 3)
681                  | ((i_gmask>>16) & 2)
682                  | ((i_gmask>>8) & 1);
683         i_bindex = ((i_bmask>>24) & 3)
684                  | ((i_bmask>>16) & 2)
685                  | ((i_bmask>>8) & 1);
686 #endif
687
688         /* Draw until we reach the bottom of the subtitle */
689         for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
690              p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
691              p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
692              p_src2_v += i_src2_pitch )
693         {
694             /* Draw until we reach the end of the line */
695             for( i_x = 0; i_x < i_width; i_x++ )
696             {
697                 if( p_trans )
698                     i_trans = vlc_alpha( p_trans[i_x], i_alpha );
699                 if( !i_trans )
700                     continue;
701
702                 const int i_pos = i_x * i_pix_pitch;
703                 if( i_trans == MAX_TRANS )
704                 {
705
706                     /* Completely opaque. Completely overwrite underlying pixel */
707                     yuv_to_rgb( &r, &g, &b,
708                                 p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
709
710                     p_dst[i_pos + i_rindex ] = r;
711                     p_dst[i_pos + i_gindex ] = g;
712                     p_dst[i_pos + i_bindex ] = b;
713                 }
714                 else
715                 {
716                     int i_rpos = i_pos + i_rindex;
717                     int i_gpos = i_pos + i_gindex;
718                     int i_bpos = i_pos + i_bindex;
719
720                     /* Blending */
721                     yuv_to_rgb( &r, &g, &b,
722                                 p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
723
724                     p_dst[i_rpos] = vlc_blend( r, p_src1[i_rpos], i_trans );
725                     p_dst[i_gpos] = vlc_blend( g, p_src1[i_gpos], i_trans );
726                     p_dst[i_bpos] = vlc_blend( b, p_src1[i_gpos], i_trans );
727                 }
728             }
729         }
730     }
731 }
732
733 static void BlendYUVPacked( filter_t *p_filter, picture_t *p_dst_pic,
734                             picture_t *p_dst_orig, picture_t *p_src,
735                             int i_x_offset, int i_y_offset,
736                             int i_width, int i_height, int i_alpha )
737 {
738     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
739     uint8_t *p_dst, *p_src1, *p_src2_y;
740     uint8_t *p_src2_u, *p_src2_v;
741     uint8_t *p_trans;
742     int i_x, i_y, i_pix_pitch, i_trans = 0;
743     bool b_even = !((i_x_offset + p_filter->fmt_out.video.i_x_offset)%2);
744     int i_l_offset = 0, i_u_offset = 0, i_v_offset = 0;
745
746     if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','U','Y','2') )
747     {
748         i_l_offset = 0;
749         i_u_offset = 1;
750         i_v_offset = 3;
751     }
752     else if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('U','Y','V','Y') )
753     {
754         i_l_offset = 1;
755         i_u_offset = 0;
756         i_v_offset = 2;
757     }
758     else if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','Y','U') )
759     {
760         i_l_offset = 0;
761         i_u_offset = 3;
762         i_v_offset = 1;
763     }
764
765     i_pix_pitch = 2;
766     i_dst_pitch = p_dst_pic->p->i_pitch;
767     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
768             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
769             p_dst_pic->p->i_pitch *
770             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
771
772     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
773     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
774                p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
775                p_dst_orig->p->i_pitch *
776                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
777
778     p_src2_y = vlc_plane_start( &i_src2_pitch, p_src, Y_PLANE,
779                                 0, 0, &p_filter->fmt_in.video, 1 );
780     p_src2_u = vlc_plane_start( NULL, p_src, U_PLANE,
781                                 0, 0, &p_filter->fmt_in.video, 2 );
782     p_src2_v = vlc_plane_start( NULL, p_src, V_PLANE,
783                                 0, 0, &p_filter->fmt_in.video, 2 );
784     p_trans = vlc_plane_start( NULL, p_src, A_PLANE,
785                                0, 0, &p_filter->fmt_in.video, 1 );
786
787     i_width &= ~1; /* Needs to be a multiple of 2 */
788
789     /* Draw until we reach the bottom of the subtitle */
790     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
791          p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
792          p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
793          p_src2_v += i_src2_pitch )
794     {
795         /* Draw until we reach the end of the line */
796         for( i_x = 0; i_x < i_width; i_x++, b_even = !b_even )
797         {
798             i_trans = vlc_alpha( p_trans[i_x], i_alpha );
799             if( !i_trans )
800                 continue;
801
802             /* Blending */
803             p_dst[i_x * 2 + i_l_offset] = vlc_blend( p_src2_y[i_x], p_src1[i_x * 2 + i_l_offset], i_trans );
804             if( b_even )
805             {
806                 int i_u;
807                 int i_v;
808                 /* FIXME what's with 0xaa ? */
809                 if( p_trans[i_x+1] > 0xaa )
810                 {
811                     i_u = (p_src2_u[i_x]+p_src2_u[i_x+1])>>1;
812                     i_v = (p_src2_v[i_x]+p_src2_v[i_x+1])>>1;
813                 }
814                 else
815                 {
816                     i_u = p_src2_u[i_x];
817                     i_v = p_src2_v[i_x];
818                 }
819                 p_dst[i_x * 2 + i_u_offset] = vlc_blend( i_u, p_src1[i_x * 2 + i_u_offset], i_trans );
820                 p_dst[i_x * 2 + i_v_offset] = vlc_blend( i_v, p_src1[i_x * 2 + i_v_offset], i_trans );
821             }
822         }
823     }
824 }
825 /***********************************************************************
826  * I420, YV12
827  ***********************************************************************/
828 static void BlendI420I420( filter_t *p_filter, picture_t *p_dst,
829                            picture_t *p_dst_orig, picture_t *p_src,
830                            int i_x_offset, int i_y_offset,
831                            int i_width, int i_height, int i_alpha )
832 {
833     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
834     uint8_t *p_src1_y, *p_src2_y, *p_dst_y;
835     uint8_t *p_src1_u, *p_src2_u, *p_dst_u;
836     uint8_t *p_src1_v, *p_src2_v, *p_dst_v;
837     int i_x, i_y;
838     bool b_even_scanline = i_y_offset % 2;
839
840     i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
841     p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
842               p_filter->fmt_out.video.i_x_offset +
843               p_dst->p[Y_PLANE].i_pitch *
844               ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
845     p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
846               p_filter->fmt_out.video.i_x_offset/2 +
847               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
848               p_dst->p[U_PLANE].i_pitch;
849     p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
850               p_filter->fmt_out.video.i_x_offset/2 +
851               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
852               p_dst->p[V_PLANE].i_pitch;
853
854     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
855     p_src1_y = p_dst_orig->p[Y_PLANE].p_pixels + i_x_offset +
856                p_filter->fmt_out.video.i_x_offset +
857                p_dst_orig->p[Y_PLANE].i_pitch *
858                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
859     p_src1_u = p_dst_orig->p[U_PLANE].p_pixels + i_x_offset/2 +
860                p_filter->fmt_out.video.i_x_offset/2 +
861                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
862                p_dst_orig->p[U_PLANE].i_pitch;
863     p_src1_v = p_dst_orig->p[V_PLANE].p_pixels + i_x_offset/2 +
864                p_filter->fmt_out.video.i_x_offset/2 +
865                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
866                p_dst_orig->p[V_PLANE].i_pitch;
867
868     p_src2_y = vlc_plane_start( &i_src2_pitch, p_src, Y_PLANE,
869                                 0, 0, &p_filter->fmt_in.video, 1 );
870     p_src2_u = vlc_plane_start( NULL, p_src, U_PLANE,
871                                 0, 0, &p_filter->fmt_in.video, 2 );
872     p_src2_v = vlc_plane_start( NULL, p_src, V_PLANE,
873                                 0, 0, &p_filter->fmt_in.video, 2 );
874     i_width &= ~1;
875
876     /* Draw until we reach the bottom of the subtitle */
877     for( i_y = 0; i_y < i_height; i_y++,
878          p_dst_y += i_dst_pitch, p_src1_y += i_src1_pitch,
879          p_src2_y += i_src2_pitch )
880     {
881         if( b_even_scanline )
882         {
883             p_dst_u  += i_dst_pitch/2;
884             p_dst_v  += i_dst_pitch/2;
885             p_src1_u += i_src1_pitch/2;
886             p_src1_v += i_src1_pitch/2;
887         }
888         b_even_scanline = !b_even_scanline;
889
890         /* Draw until we reach the end of the line */
891         for( i_x = 0; i_x < i_width; i_x++ )
892         {
893             if( !i_alpha )
894                 continue;
895
896             /* Blending */
897             p_dst_y[i_x] = vlc_blend( p_src2_y[i_x], p_src1_y[i_x], i_alpha );
898             if( b_even_scanline && i_x % 2 == 0 )
899             {
900                 p_dst_u[i_x/2] = vlc_blend( p_src2_u[i_x/2], p_src1_u[i_x/2], i_alpha );
901                 p_dst_v[i_x/2] = vlc_blend( p_src2_v[i_x/2], p_src1_v[i_x/2], i_alpha );
902             }
903         }
904         if( i_y%2 == 1 )
905         {
906             p_src2_u += i_src2_pitch/2;
907             p_src2_v += i_src2_pitch/2;
908         }
909     }
910 }
911 static void BlendI420I420_no_alpha( filter_t *p_filter, picture_t *p_dst,
912                                     picture_t *p_dst_orig, picture_t *p_src,
913                                     int i_x_offset, int i_y_offset,
914                                     int i_width, int i_height )
915 {
916     int i_src2_pitch, i_dst_pitch;
917     uint8_t *p_src2_y, *p_dst_y;
918     uint8_t *p_src2_u, *p_dst_u;
919     uint8_t *p_src2_v, *p_dst_v;
920     int i_y;
921     bool b_even_scanline = i_y_offset % 2;
922
923     i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
924     p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
925               p_filter->fmt_out.video.i_x_offset +
926               p_dst->p[Y_PLANE].i_pitch *
927               ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
928     p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
929               p_filter->fmt_out.video.i_x_offset/2 +
930               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
931               p_dst->p[U_PLANE].i_pitch;
932     p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
933               p_filter->fmt_out.video.i_x_offset/2 +
934               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
935               p_dst->p[V_PLANE].i_pitch;
936
937     p_src2_y = vlc_plane_start( &i_src2_pitch, p_src, Y_PLANE,
938                                 0, 0, &p_filter->fmt_in.video, 1 );
939     p_src2_u = vlc_plane_start( NULL, p_src, U_PLANE,
940                                 0, 0, &p_filter->fmt_in.video, 2 );
941     p_src2_v = vlc_plane_start( NULL, p_src, V_PLANE,
942                                 0, 0, &p_filter->fmt_in.video, 2 );
943
944     i_width &= ~1;
945
946     /* Draw until we reach the bottom of the subtitle */
947     for( i_y = 0; i_y < i_height;
948             i_y++, p_dst_y += i_dst_pitch, p_src2_y += i_src2_pitch )
949     {
950         /* Completely opaque. Completely overwrite underlying pixel */
951         vlc_memcpy( p_dst_y, p_src2_y, i_width );
952         if( b_even_scanline )
953         {
954             p_dst_u  += i_dst_pitch/2;
955             p_dst_v  += i_dst_pitch/2;
956         }
957         else
958         {
959             vlc_memcpy( p_dst_u, p_src2_u, i_width/2 );
960             vlc_memcpy( p_dst_v, p_src2_v, i_width/2 );
961         }
962         b_even_scanline = !b_even_scanline;
963         if( i_y%2 == 1 )
964         {
965             p_src2_u += i_src2_pitch/2;
966             p_src2_v += i_src2_pitch/2;
967         }
968     }
969 }
970
971 static void BlendI420R16( filter_t *p_filter, picture_t *p_dst_pic,
972                           picture_t *p_dst_orig, picture_t *p_src,
973                           int i_x_offset, int i_y_offset,
974                           int i_width, int i_height, int i_alpha )
975 {
976     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
977     uint8_t *p_dst, *p_src1, *p_src2_y;
978     uint8_t *p_src2_u, *p_src2_v;
979     int i_x, i_y, i_pix_pitch;
980     int r, g, b;
981
982     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
983     i_dst_pitch = p_dst_pic->p->i_pitch;
984     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
985             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
986             p_dst_pic->p->i_pitch *
987             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
988
989     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
990     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
991                p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
992                p_dst_orig->p->i_pitch *
993                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
994
995     p_src2_y = vlc_plane_start( &i_src2_pitch, p_src, Y_PLANE,
996                                 0, 0, &p_filter->fmt_in.video, 1 );
997     p_src2_u = vlc_plane_start( NULL, p_src, U_PLANE,
998                                 0, 0, &p_filter->fmt_in.video, 2 );
999     p_src2_v = vlc_plane_start( NULL, p_src, V_PLANE,
1000                                 0, 0, &p_filter->fmt_in.video, 2 );
1001
1002     /* Draw until we reach the bottom of the subtitle */
1003     for( i_y = 0; i_y < i_height; i_y++,
1004          p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
1005          p_src2_y += i_src2_pitch )
1006     {
1007         /* Draw until we reach the end of the line */
1008         for( i_x = 0; i_x < i_width; i_x++ )
1009         {
1010             if( i_alpha == MAX_TRANS )
1011             {
1012                 /* Completely opaque. Completely overwrite underlying pixel */
1013                 yuv_to_rgb( &r, &g, &b,
1014                             p_src2_y[i_x], p_src2_u[i_x/2], p_src2_v[i_x/2] );
1015
1016     ((uint16_t *)(&p_dst[i_x * i_pix_pitch]))[0] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
1017                 continue;
1018             }
1019
1020             /* Blending */
1021             /* FIXME: do the blending
1022              * FIXME use rgb shifts */
1023             yuv_to_rgb( &r, &g, &b,
1024                         p_src2_y[i_x], p_src2_u[i_x/2], p_src2_v[i_x/2] );
1025
1026     ((uint16_t *)(&p_dst[i_x * i_pix_pitch]))[0] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
1027         }
1028         if( i_y%2 == 1 )
1029         {
1030             p_src2_u += i_src2_pitch/2;
1031             p_src2_v += i_src2_pitch/2;
1032         }
1033     }
1034 }
1035
1036 static void BlendI420R24( filter_t *p_filter, picture_t *p_dst_pic,
1037                           picture_t *p_dst_orig, picture_t *p_src,
1038                           int i_x_offset, int i_y_offset,
1039                           int i_width, int i_height, int i_alpha )
1040 {
1041     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
1042     uint8_t *p_dst, *p_src1, *p_src2_y;
1043     uint8_t *p_src2_u, *p_src2_v;
1044     int i_x, i_y, i_pix_pitch;
1045     int r, g, b;
1046
1047     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
1048     i_dst_pitch = p_dst_pic->p->i_pitch;
1049     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
1050             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1051             p_dst_pic->p->i_pitch *
1052             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1053
1054     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
1055     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
1056                p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1057                p_dst_orig->p->i_pitch *
1058                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1059
1060     p_src2_y = vlc_plane_start( &i_src2_pitch, p_src, Y_PLANE,
1061                                 0, 0, &p_filter->fmt_in.video, 1 );
1062     p_src2_u = vlc_plane_start( NULL, p_src, U_PLANE,
1063                                 0, 0, &p_filter->fmt_in.video, 2 );
1064     p_src2_v = vlc_plane_start( NULL, p_src, V_PLANE,
1065                                 0, 0, &p_filter->fmt_in.video, 2 );
1066
1067
1068     /* Draw until we reach the bottom of the subtitle */
1069     for( i_y = 0; i_y < i_height; i_y++,
1070          p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
1071          p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
1072          p_src2_v += i_src2_pitch )
1073     {
1074         /* Draw until we reach the end of the line */
1075         for( i_x = 0; i_x < i_width; i_x++ )
1076         {
1077             if( !i_alpha )
1078                 continue;
1079
1080             /* Blending */
1081             yuv_to_rgb( &r, &g, &b,
1082                         p_src2_y[i_x], p_src2_u[i_x/2], p_src2_v[i_x/2] );
1083
1084             p_dst[i_x * i_pix_pitch + 0] = vlc_blend( r, p_src1[i_x * i_pix_pitch + 0], i_alpha );
1085             p_dst[i_x * i_pix_pitch + 1] = vlc_blend( g, p_src1[i_x * i_pix_pitch + 1], i_alpha );
1086             p_dst[i_x * i_pix_pitch + 2] = vlc_blend( b, p_src1[i_x * i_pix_pitch + 2], i_alpha );
1087         }
1088         if( i_y%2 == 1 )
1089         {
1090             p_src2_u += i_src2_pitch/2;
1091             p_src2_v += i_src2_pitch/2;
1092         }
1093     }
1094 }
1095
1096 static void BlendI420YUVPacked( filter_t *p_filter, picture_t *p_dst_pic,
1097                                 picture_t *p_dst_orig, picture_t *p_src,
1098                                 int i_x_offset, int i_y_offset,
1099                                 int i_width, int i_height, int i_alpha )
1100 {
1101     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
1102     uint8_t *p_dst, *p_src1, *p_src2_y;
1103     uint8_t *p_src2_u, *p_src2_v;
1104     int i_x, i_y, i_pix_pitch;
1105     bool b_even = !((i_x_offset + p_filter->fmt_out.video.i_x_offset)%2);
1106     int i_l_offset = 0, i_u_offset = 0, i_v_offset = 0;
1107
1108     if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','U','Y','2') )
1109     {
1110         i_l_offset = 0;
1111         i_u_offset = 1;
1112         i_v_offset = 3;
1113     }
1114     else if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('U','Y','V','Y') )
1115     {
1116         i_l_offset = 1;
1117         i_u_offset = 0;
1118         i_v_offset = 2;
1119     }
1120     else if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','Y','U') )
1121     {
1122         i_l_offset = 0;
1123         i_u_offset = 3;
1124         i_v_offset = 1;
1125     }
1126
1127     i_pix_pitch = 2;
1128     i_dst_pitch = p_dst_pic->p->i_pitch;
1129     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
1130             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1131             p_dst_pic->p->i_pitch *
1132             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1133
1134     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
1135     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
1136                p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1137                p_dst_orig->p->i_pitch *
1138                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1139
1140     p_src2_y = vlc_plane_start( &i_src2_pitch, p_src, Y_PLANE,
1141                                 0, 0, &p_filter->fmt_in.video, 1 );
1142     p_src2_u = vlc_plane_start( NULL, p_src, U_PLANE,
1143                                 0, 0, &p_filter->fmt_in.video, 2 );
1144     p_src2_v = vlc_plane_start( NULL, p_src, V_PLANE,
1145                                 0, 0, &p_filter->fmt_in.video, 2 );
1146
1147     i_width &= ~1; /* Needs to be a multiple of 2 */
1148
1149     /* Draw until we reach the bottom of the subtitle */
1150     for( i_y = 0; i_y < i_height; i_y++,
1151          p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
1152          p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
1153          p_src2_v += i_src2_pitch )
1154     {
1155         /* Draw until we reach the end of the line */
1156         for( i_x = 0; i_x < i_width; i_x++, b_even = !b_even )
1157         {
1158             if( !i_alpha )
1159                 continue;
1160
1161             /* Blending */
1162             p_dst[i_x * 2 + i_l_offset] = vlc_blend( p_src2_y[i_x], p_src1[i_x * 2 + i_l_offset], i_alpha );
1163             if( b_even )
1164             {
1165                 uint16_t i_u = p_src2_u[i_x/2];
1166                 uint16_t i_v = p_src2_v[i_x/2];
1167                 p_dst[i_x * 2 + i_u_offset] = vlc_blend( i_u, p_src1[i_x * 2 + i_u_offset], i_alpha );
1168                 p_dst[i_x * 2 + i_v_offset] = vlc_blend( i_v, p_src1[i_x * 2 + i_v_offset], i_alpha );
1169             }
1170         }
1171         if( i_y%2 == 1 )
1172         {
1173             p_src2_u += i_src2_pitch/2;
1174             p_src2_v += i_src2_pitch/2;
1175         }
1176     }
1177 }
1178
1179 /***********************************************************************
1180  * YUVP
1181  ***********************************************************************/
1182 static void BlendPalI420( filter_t *p_filter, picture_t *p_dst,
1183                           picture_t *p_dst_orig, picture_t *p_src,
1184                           int i_x_offset, int i_y_offset,
1185                           int i_width, int i_height, int i_alpha )
1186 {
1187     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
1188     uint8_t *p_src1_y, *p_src2, *p_dst_y;
1189     uint8_t *p_src1_u, *p_dst_u;
1190     uint8_t *p_src1_v, *p_dst_v;
1191     int i_x, i_y, i_trans;
1192     bool b_even_scanline = i_y_offset % 2;
1193
1194     i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
1195     p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
1196               p_filter->fmt_out.video.i_x_offset +
1197               p_dst->p[Y_PLANE].i_pitch *
1198               ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1199     p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
1200               p_filter->fmt_out.video.i_x_offset/2 +
1201               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
1202               p_dst->p[U_PLANE].i_pitch;
1203     p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
1204               p_filter->fmt_out.video.i_x_offset/2 +
1205               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
1206               p_dst->p[V_PLANE].i_pitch;
1207
1208     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
1209     p_src1_y = p_dst_orig->p[Y_PLANE].p_pixels + i_x_offset +
1210                p_filter->fmt_out.video.i_x_offset +
1211                p_dst_orig->p[Y_PLANE].i_pitch *
1212                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1213     p_src1_u = p_dst_orig->p[U_PLANE].p_pixels + i_x_offset/2 +
1214                p_filter->fmt_out.video.i_x_offset/2 +
1215                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
1216                p_dst_orig->p[U_PLANE].i_pitch;
1217     p_src1_v = p_dst_orig->p[V_PLANE].p_pixels + i_x_offset/2 +
1218                p_filter->fmt_out.video.i_x_offset/2 +
1219                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
1220                p_dst_orig->p[V_PLANE].i_pitch;
1221
1222     i_src2_pitch = p_src->p->i_pitch;
1223     p_src2 = p_src->p->p_pixels + p_filter->fmt_in.video.i_x_offset +
1224              i_src2_pitch * p_filter->fmt_in.video.i_y_offset;
1225
1226     const uint8_t *p_trans = p_src2;
1227 #define p_pal p_filter->fmt_in.video.p_palette->palette
1228
1229     /* Draw until we reach the bottom of the subtitle */
1230     for( i_y = 0; i_y < i_height; i_y++,
1231          p_dst_y += i_dst_pitch, p_src1_y += i_src1_pitch,
1232          p_src2 += i_src2_pitch,
1233          p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
1234          p_src1_u += b_even_scanline ? i_src1_pitch/2 : 0,
1235          p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
1236          p_src1_v += b_even_scanline ? i_src1_pitch/2 : 0 )
1237     {
1238         b_even_scanline = !b_even_scanline;
1239
1240         /* Draw until we reach the end of the line */
1241         for( i_x = 0; i_x < i_width; i_x++ )
1242         {
1243             i_trans = vlc_alpha( p_pal[p_trans[i_x]][3], i_alpha );
1244             if( !i_trans )
1245                 continue;
1246
1247             /* Blending */
1248             p_dst_y[i_x] = vlc_blend( p_pal[p_src2[i_x]][0], p_src1_y[i_x], i_trans );
1249             if( b_even_scanline && ((i_x % 2) == 0) )
1250             {
1251                 p_dst_u[i_x/2] = vlc_blend( p_pal[p_src2[i_x]][1], p_src1_u[i_x/2], i_trans );
1252                 p_dst_v[i_x/2] = vlc_blend( p_pal[p_src2[i_x]][2], p_src1_v[i_x/2], i_trans );
1253             }
1254         }
1255     }
1256 #undef p_pal
1257 }
1258
1259 static void BlendPalYUVPacked( filter_t *p_filter, picture_t *p_dst_pic,
1260                                picture_t *p_dst_orig, picture_t *p_src,
1261                                int i_x_offset, int i_y_offset,
1262                                int i_width, int i_height, int i_alpha )
1263 {
1264     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
1265     uint8_t *p_src1, *p_src2, *p_dst;
1266     int i_x, i_y, i_pix_pitch, i_trans;
1267     bool b_even = !((i_x_offset + p_filter->fmt_out.video.i_x_offset)%2);
1268     int i_l_offset = 0, i_u_offset = 0, i_v_offset = 0;
1269
1270     if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','U','Y','2') )
1271     {
1272         i_l_offset = 0;
1273         i_u_offset = 1;
1274         i_v_offset = 3;
1275     }
1276     else if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('U','Y','V','Y') )
1277     {
1278         i_l_offset = 1;
1279         i_u_offset = 0;
1280         i_v_offset = 2;
1281     }
1282     else if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','Y','U') )
1283     {
1284         i_l_offset = 0;
1285         i_u_offset = 3;
1286         i_v_offset = 1;
1287     }
1288
1289     i_pix_pitch = 2;
1290     i_dst_pitch = p_dst_pic->p->i_pitch;
1291     p_dst = p_dst_pic->p->p_pixels + i_pix_pitch * (i_x_offset +
1292             p_filter->fmt_out.video.i_x_offset) + p_dst_pic->p->i_pitch *
1293             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1294
1295     i_src1_pitch = p_dst_orig->p->i_pitch;
1296     p_src1 = p_dst_orig->p->p_pixels + i_pix_pitch * (i_x_offset +
1297              p_filter->fmt_out.video.i_x_offset) + p_dst_orig->p->i_pitch *
1298              ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1299
1300     i_src2_pitch = p_src->p->i_pitch;
1301     p_src2 = p_src->p->p_pixels + p_filter->fmt_in.video.i_x_offset +
1302              i_src2_pitch * p_filter->fmt_in.video.i_y_offset;
1303
1304     i_width = (i_width >> 1) << 1; /* Needs to be a multiple of 2 */
1305
1306     const uint8_t *p_trans = p_src2;
1307 #define p_pal p_filter->fmt_in.video.p_palette->palette
1308
1309     /* Draw until we reach the bottom of the subtitle */
1310     for( i_y = 0; i_y < i_height; i_y++,
1311          p_dst += i_dst_pitch, p_src1 += i_src1_pitch, p_src2 += i_src2_pitch )
1312     {
1313         /* Draw until we reach the end of the line */
1314         for( i_x = 0; i_x < i_width; i_x++, b_even = !b_even )
1315         {
1316             i_trans = vlc_alpha( p_pal[p_trans[i_x]][3], i_alpha );
1317             if( !i_trans )
1318                 continue;
1319
1320             /* Blending */
1321             p_dst[i_x * 2 + i_l_offset] = vlc_blend( p_pal[p_src2[i_x]][0], p_src1[i_x * 2 + i_l_offset], i_trans );
1322             if( b_even )
1323             {
1324                 uint16_t i_u;
1325                 uint16_t i_v;
1326                 if( p_trans[i_x+1] > 0xaa )
1327                 {
1328                     i_u = (p_pal[p_src2[i_x]][1] + p_pal[p_src2[i_x+1]][1]) >> 1;
1329                     i_v = (p_pal[p_src2[i_x]][2] + p_pal[p_src2[i_x+1]][2]) >> 1;
1330                 }
1331                 else
1332                 {
1333                     i_u = p_pal[p_src2[i_x]][1];
1334                     i_v = p_pal[p_src2[i_x]][2];
1335                 }
1336
1337                 p_dst[i_x * 2 + i_u_offset] = vlc_blend( i_u, p_src1[i_x * 2 + i_u_offset], i_trans );
1338                 p_dst[i_x * 2 + i_v_offset] = vlc_blend( i_v, p_src1[i_x * 2 + i_v_offset], i_trans );
1339             }
1340         }
1341     }
1342 #undef p_pal
1343 }
1344
1345 static void BlendPalRV( filter_t *p_filter, picture_t *p_dst_pic,
1346                         picture_t *p_dst_orig, picture_t *p_src,
1347                         int i_x_offset, int i_y_offset,
1348                         int i_width, int i_height, int i_alpha )
1349 {
1350     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
1351     uint8_t *p_src1, *p_src2, *p_dst;
1352     int i_x, i_y, i_pix_pitch, i_trans;
1353     int r, g, b;
1354     video_palette_t rgbpalette;
1355
1356     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
1357     i_dst_pitch = p_dst_pic->p->i_pitch;
1358     p_dst = p_dst_pic->p->p_pixels + i_pix_pitch * (i_x_offset +
1359             p_filter->fmt_out.video.i_x_offset) + p_dst_pic->p->i_pitch *
1360             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1361
1362     i_src1_pitch = p_dst_orig->p->i_pitch;
1363     p_src1 = p_dst_orig->p->p_pixels + i_pix_pitch * (i_x_offset +
1364              p_filter->fmt_out.video.i_x_offset) + p_dst_orig->p->i_pitch *
1365              ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1366
1367     i_src2_pitch = p_src->p->i_pitch;
1368     p_src2 = p_src->p->p_pixels + p_filter->fmt_in.video.i_x_offset +
1369              i_src2_pitch * p_filter->fmt_in.video.i_y_offset;
1370
1371     const uint8_t *p_trans = p_src2;
1372 #define p_pal p_filter->fmt_in.video.p_palette->palette
1373 #define rgbpal rgbpalette.palette
1374
1375     /* Convert palette first */
1376     for( i_y = 0; i_y < p_filter->fmt_in.video.p_palette->i_entries &&
1377          i_y < 256; i_y++ )
1378     {
1379         yuv_to_rgb( &r, &g, &b, p_pal[i_y][0], p_pal[i_y][1], p_pal[i_y][2] );
1380
1381         if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('R','V','1','6') )
1382         {
1383             *(uint16_t *)rgbpal[i_y] =
1384                 ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
1385         }
1386         else
1387         {
1388             rgbpal[i_y][0] = r; rgbpal[i_y][1] = g; rgbpal[i_y][2] = b;
1389         }
1390     }
1391
1392     /* Draw until we reach the bottom of the subtitle */
1393     for( i_y = 0; i_y < i_height; i_y++,
1394          p_dst += i_dst_pitch, p_src1 += i_src1_pitch, p_src2 += i_src2_pitch )
1395     {
1396         /* Draw until we reach the end of the line */
1397         for( i_x = 0; i_x < i_width; i_x++ )
1398         {
1399             i_trans = vlc_alpha( p_pal[p_trans[i_x]][3], i_alpha );
1400             if( !i_trans )
1401                 continue;
1402
1403             if( i_trans == MAX_TRANS ||
1404                 p_filter->fmt_out.video.i_chroma == VLC_FOURCC('R','V','1','6') )
1405             {
1406                 /* FIXME implement blending for RV16 */
1407                 /* Completely opaque. Completely overwrite underlying pixel */
1408                 p_dst[i_x * i_pix_pitch]     = rgbpal[p_src2[i_x]][0];
1409                 p_dst[i_x * i_pix_pitch + 1] = rgbpal[p_src2[i_x]][1];
1410                 if( p_filter->fmt_out.video.i_chroma != VLC_FOURCC('R','V','1','6') )
1411                     p_dst[i_x * i_pix_pitch + 2] = rgbpal[p_src2[i_x]][2];
1412                 continue;
1413             }
1414
1415             /* Blending */
1416             p_dst[i_x * i_pix_pitch + 0] = vlc_blend( rgbpal[p_src2[i_x]][0], p_src1[i_x * i_pix_pitch + 0], i_trans );
1417             p_dst[i_x * i_pix_pitch + 1] = vlc_blend( rgbpal[p_src2[i_x]][1], p_src1[i_x * i_pix_pitch + 1], i_trans );
1418             p_dst[i_x * i_pix_pitch + 2] = vlc_blend( rgbpal[p_src2[i_x]][2], p_src1[i_x * i_pix_pitch + 2], i_trans );
1419         }
1420     }
1421
1422 #undef p_pal
1423 #undef rgbpal
1424 }
1425
1426 /***********************************************************************
1427  * RGBA
1428  ***********************************************************************/
1429 static void BlendRGBAI420( filter_t *p_filter, picture_t *p_dst,
1430                            picture_t *p_dst_orig, picture_t *p_src,
1431                            int i_x_offset, int i_y_offset,
1432                            int i_width, int i_height, int i_alpha )
1433 {
1434     int i_src1_pitch, i_src2_pitch, i_dst_pitch, i_src_pix_pitch;
1435     uint8_t *p_src1_y, *p_dst_y;
1436     uint8_t *p_src1_u, *p_dst_u;
1437     uint8_t *p_src1_v, *p_dst_v;
1438     uint8_t *p_src2;
1439     int i_x, i_y, i_trans;
1440     uint8_t y, u, v;
1441
1442     bool b_even_scanline = i_y_offset % 2;
1443
1444     i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
1445     p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
1446               p_filter->fmt_out.video.i_x_offset +
1447               p_dst->p[Y_PLANE].i_pitch *
1448               ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1449     p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
1450               p_filter->fmt_out.video.i_x_offset/2 +
1451               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
1452               p_dst->p[U_PLANE].i_pitch;
1453     p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
1454               p_filter->fmt_out.video.i_x_offset/2 +
1455               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
1456               p_dst->p[V_PLANE].i_pitch;
1457
1458     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
1459     p_src1_y = p_dst_orig->p[Y_PLANE].p_pixels + i_x_offset +
1460                p_filter->fmt_out.video.i_x_offset +
1461                p_dst_orig->p[Y_PLANE].i_pitch *
1462                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1463     p_src1_u = p_dst_orig->p[U_PLANE].p_pixels + i_x_offset/2 +
1464                p_filter->fmt_out.video.i_x_offset/2 +
1465                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
1466                p_dst_orig->p[U_PLANE].i_pitch;
1467     p_src1_v = p_dst_orig->p[V_PLANE].p_pixels + i_x_offset/2 +
1468                p_filter->fmt_out.video.i_x_offset/2 +
1469                ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
1470                p_dst_orig->p[V_PLANE].i_pitch;
1471
1472     i_src_pix_pitch = p_src->p->i_pixel_pitch;
1473     i_src2_pitch = p_src->p->i_pitch;
1474     p_src2 = p_src->p->p_pixels +
1475              p_filter->fmt_in.video.i_x_offset * i_src2_pitch +
1476              p_src->p->i_pitch * p_filter->fmt_in.video.i_y_offset;
1477
1478
1479     /* Draw until we reach the bottom of the subtitle */
1480     for( i_y = 0; i_y < i_height; i_y++,
1481          p_dst_y += i_dst_pitch, p_src1_y += i_src1_pitch,
1482          p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
1483          p_src1_u += b_even_scanline ? i_src1_pitch/2 : 0,
1484          p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
1485          p_src1_v += b_even_scanline ? i_src1_pitch/2 : 0,
1486          p_src2 += i_src2_pitch )
1487     {
1488         b_even_scanline = !b_even_scanline;
1489
1490         /* Draw until we reach the end of the line */
1491         for( i_x = 0; i_x < i_width; i_x++ )
1492         {
1493             const int R = p_src2[i_x * i_src_pix_pitch + 0];
1494             const int G = p_src2[i_x * i_src_pix_pitch + 1];
1495             const int B = p_src2[i_x * i_src_pix_pitch + 2];
1496
1497             i_trans = vlc_alpha( p_src2[i_x * i_src_pix_pitch + 3], i_alpha );
1498             if( !i_trans )
1499                 continue;
1500
1501             /* Blending */
1502             rgb_to_yuv( &y, &u, &v, R, G, B );
1503
1504             p_dst_y[i_x] = vlc_blend( y, p_src1_y[i_x], i_trans );
1505             if( b_even_scanline && i_x % 2 == 0 )
1506             {
1507                 p_dst_u[i_x/2] = vlc_blend( u, p_src1_u[i_x/2], i_trans );
1508                 p_dst_v[i_x/2] = vlc_blend( v, p_src1_v[i_x/2], i_trans );
1509             }
1510         }
1511     }
1512 }
1513
1514 static void BlendRGBAR24( filter_t *p_filter, picture_t *p_dst_pic,
1515                           picture_t *p_dst_orig, picture_t *p_src,
1516                           int i_x_offset, int i_y_offset,
1517                           int i_width, int i_height, int i_alpha )
1518 {
1519     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
1520     uint8_t *p_dst, *p_src1, *p_src2;
1521     int i_x, i_y, i_pix_pitch, i_trans, i_src_pix_pitch;
1522
1523     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
1524     i_dst_pitch = p_dst_pic->p->i_pitch;
1525     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
1526             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1527             p_dst_pic->p->i_pitch *
1528             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1529
1530     i_src1_pitch = p_dst_orig->p->i_pitch;
1531     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
1532              p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1533              p_dst_orig->p->i_pitch *
1534              ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1535
1536     i_src_pix_pitch = p_src->p->i_pixel_pitch;
1537     i_src2_pitch = p_src->p->i_pitch;
1538     p_src2 = p_src->p->p_pixels +
1539              p_filter->fmt_in.video.i_x_offset * i_pix_pitch +
1540              p_src->p->i_pitch * p_filter->fmt_in.video.i_y_offset;
1541
1542     /* Draw until we reach the bottom of the subtitle */
1543     for( i_y = 0; i_y < i_height; i_y++,
1544          p_dst += i_dst_pitch, p_src1 += i_src1_pitch, p_src2 += i_src2_pitch )
1545     {
1546         /* Draw until we reach the end of the line */
1547         for( i_x = 0; i_x < i_width; i_x++ )
1548         {
1549             const int R = p_src2[i_x * i_src_pix_pitch + 0];
1550             const int G = p_src2[i_x * i_src_pix_pitch + 1];
1551             const int B = p_src2[i_x * i_src_pix_pitch + 2];
1552
1553             i_trans = vlc_alpha( p_src2[i_x * i_src_pix_pitch + 3], i_alpha );
1554             if( !i_trans )
1555                 continue;
1556
1557             /* Blending */
1558             p_dst[i_x * i_pix_pitch + 0] = vlc_blend( R, p_src1[i_x * i_pix_pitch + 0], i_trans );
1559             p_dst[i_x * i_pix_pitch + 1] = vlc_blend( G, p_src1[i_x * i_pix_pitch + 1], i_trans );
1560             p_dst[i_x * i_pix_pitch + 2] = vlc_blend( B, p_src1[i_x * i_pix_pitch + 2], i_trans );
1561         }
1562     }
1563 }
1564
1565 static void BlendRGBAR16( filter_t *p_filter, picture_t *p_dst_pic,
1566                           picture_t *p_dst_orig, picture_t *p_src,
1567                           int i_x_offset, int i_y_offset,
1568                           int i_width, int i_height, int i_alpha )
1569 {
1570     int i_src1_pitch, i_src2_pitch, i_dst_pitch;
1571     uint8_t *p_dst, *p_src1, *p_src2;
1572     int i_x, i_y, i_pix_pitch, i_trans, i_src_pix_pitch;
1573     uint16_t i_pix;
1574
1575     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
1576     i_dst_pitch = p_dst_pic->p->i_pitch;
1577     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
1578             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1579             p_dst_pic->p->i_pitch *
1580             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1581
1582     i_src1_pitch = p_dst_orig->p->i_pitch;
1583     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
1584              p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1585              p_dst_orig->p->i_pitch *
1586              ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1587
1588     i_src_pix_pitch = p_src->p->i_pixel_pitch;
1589     i_src2_pitch = p_src->p->i_pitch;
1590     p_src2 = p_src->p->p_pixels +
1591              p_filter->fmt_in.video.i_x_offset * i_pix_pitch +
1592              p_src->p->i_pitch * p_filter->fmt_in.video.i_y_offset;
1593
1594     /* Draw until we reach the bottom of the subtitle */
1595     for( i_y = 0; i_y < i_height; i_y++,
1596          p_dst += i_dst_pitch, p_src1 += i_src1_pitch, p_src2 += i_src2_pitch )
1597     {
1598         /* Draw until we reach the end of the line */
1599         for( i_x = 0; i_x < i_width; i_x++ )
1600         {
1601             const int R = p_src2[i_x * i_src_pix_pitch + 0];
1602             const int G = p_src2[i_x * i_src_pix_pitch + 1];
1603             const int B = p_src2[i_x * i_src_pix_pitch + 2];
1604
1605             i_trans = vlc_alpha( p_src2[i_x * i_src_pix_pitch + 3], i_alpha );
1606             if( !i_trans )
1607                 continue;
1608
1609             /* Blending */
1610             i_pix = *((uint16_t *)(&p_dst[i_x * i_pix_pitch]));
1611             *((uint16_t *)(&p_dst[i_x * i_pix_pitch])) =
1612                 ( vlc_blend( R >> 3, ((i_pix         )>> 11), i_trans ) << 11 ) |
1613                 ( vlc_blend( G >> 2, ((i_pix & 0x07e0)>>  5), i_trans ) <<  5 ) |
1614                 ( vlc_blend( B >> 3, ((i_pix & 0x001f)     ), i_trans )       );
1615         }
1616     }
1617 }
1618
1619 static void BlendRGBAYUVPacked( filter_t *p_filter, picture_t *p_dst_pic,
1620                                 picture_t *p_dst_orig, picture_t *p_src,
1621                                 int i_x_offset, int i_y_offset,
1622                                 int i_width, int i_height, int i_alpha )
1623 {
1624     int i_src1_pitch, i_src2_pitch, i_dst_pitch, i_src_pix_pitch;
1625     uint8_t *p_dst, *p_src1, *p_src2;
1626     uint8_t *p_trans;
1627     int i_x, i_y, i_pix_pitch, i_trans;
1628     bool b_even = !((i_x_offset + p_filter->fmt_out.video.i_x_offset)%2);
1629     int i_l_offset = 0, i_u_offset = 0, i_v_offset = 0;
1630     uint8_t y, u, v;
1631
1632     if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','U','Y','2') )
1633     {
1634         i_l_offset = 0;
1635         i_u_offset = 1;
1636         i_v_offset = 3;
1637     }
1638     else if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('U','Y','V','Y') )
1639     {
1640         i_l_offset = 1;
1641         i_u_offset = 0;
1642         i_v_offset = 2;
1643     }
1644     else if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','Y','U') )
1645     {
1646         i_l_offset = 0;
1647         i_u_offset = 3;
1648         i_v_offset = 1;
1649     }
1650
1651     i_pix_pitch = 2;
1652     i_dst_pitch = p_dst_pic->p->i_pitch;
1653     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
1654             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1655             p_dst_pic->p->i_pitch *
1656             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1657
1658     i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
1659     p_src1 = p_dst_orig->p->p_pixels + i_x_offset * i_pix_pitch +
1660                p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1661                p_dst_orig->p->i_pitch *
1662                ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1663
1664     i_src_pix_pitch = p_src->p->i_pixel_pitch;
1665     i_src2_pitch = p_src->p->i_pitch;
1666     p_src2 = p_src->p->p_pixels +
1667              p_filter->fmt_in.video.i_x_offset * i_src2_pitch +
1668              p_src->p->i_pitch * p_filter->fmt_in.video.i_y_offset;
1669
1670     i_width &= ~1; /* Needs to be a multiple of 2 */
1671
1672     /* Draw until we reach the bottom of the subtitle */
1673     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
1674          p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
1675          p_src2 += i_src2_pitch )
1676     {
1677         /* Draw until we reach the end of the line */
1678         for( i_x = 0; i_x < i_width; i_x++, b_even = !b_even )
1679         {
1680             const int R = p_src2[i_x * i_src_pix_pitch + 0];
1681             const int G = p_src2[i_x * i_src_pix_pitch + 1];
1682             const int B = p_src2[i_x * i_src_pix_pitch + 2];
1683
1684             i_trans = vlc_alpha( p_src2[i_x * i_src_pix_pitch + 3], i_alpha );
1685             if( !i_trans )
1686                 continue;
1687
1688             /* Blending */
1689             rgb_to_yuv( &y, &u, &v, R, G, B );
1690             p_dst[i_x * 2 + i_l_offset]     = vlc_blend( y, p_src1[i_x * 2 + i_l_offset], i_trans );
1691             if( b_even )
1692             {
1693                 p_dst[i_x * 2 + i_u_offset] = vlc_blend( u, p_src1[i_x * 2 + i_u_offset], i_trans );
1694                 p_dst[i_x * 2 + i_v_offset] = vlc_blend( v, p_src1[i_x * 2 + i_v_offset], i_trans );
1695             }
1696         }
1697     }
1698 }