]> git.sesse.net Git - vlc/blob - modules/video_filter/blend.c
Fixed RSS text positionning (close #3115).
[vlc] / modules / video_filter / blend.c
1 /*****************************************************************************
2  * blend.c: alpha blend 2 pictures together
3  *****************************************************************************
4  * Copyright (C) 2003-2009 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 <assert.h>
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_filter.h>
36
37 /*****************************************************************************
38  * Module descriptor
39  *****************************************************************************/
40 static int  OpenFilter ( vlc_object_t * );
41 static void CloseFilter( vlc_object_t * );
42
43 vlc_module_begin ()
44     set_description( N_("Video pictures blending") )
45     set_capability( "video blending", 100 )
46     set_callbacks( OpenFilter, CloseFilter )
47 vlc_module_end ()
48
49 /****************************************************************************
50  * Local prototypes
51  ****************************************************************************/
52 static void Blend( filter_t *, picture_t *, const picture_t *,
53                    int, int, int );
54
55 /* YUVA */
56 static void BlendYUVAI420( filter_t *, picture_t *, const picture_t *,
57                            int, int, int, int, int );
58 static void BlendYUVARV16( filter_t *, picture_t *, const picture_t *,
59                            int, int, int, int, int );
60 static void BlendYUVARV24( filter_t *, picture_t *, const picture_t *,
61                            int, int, int, int, int );
62 static void BlendYUVAYUVPacked( filter_t *, picture_t *, const picture_t *,
63                                 int, int, int, int, int );
64
65 /* I420, YV12 */
66 static void BlendI420I420( filter_t *, picture_t *, const picture_t *,
67                            int, int, int, int, int );
68 static void BlendI420I420_no_alpha(
69                            filter_t *, picture_t *, const picture_t *,
70                            int, int, int, int );
71 static void BlendI420R16( filter_t *, picture_t *, const picture_t *,
72                            int, int, int, int, int );
73 static void BlendI420R24( filter_t *, picture_t *, const picture_t *,
74                           int, int, int, int, int );
75 static void BlendI420YUVPacked( filter_t *, picture_t *,
76                                 const picture_t *, int, int, int, int, int );
77
78 /* YUVP */
79 static void BlendPalI420( filter_t *, picture_t *, const picture_t *,
80                           int, int, int, int, int );
81 static void BlendPalYUVPacked( filter_t *, picture_t *, const picture_t *,
82                                int, int, int, int, int );
83 static void BlendPalRV( filter_t *, picture_t *, const picture_t *,
84                         int, int, int, int, int );
85
86 /* RGBA */
87 static void BlendRGBAI420( filter_t *, picture_t *, const picture_t *,
88                            int, int, int, int, int );
89 static void BlendRGBAYUVPacked( filter_t *, picture_t *,
90                                 const picture_t *, int, int, int, int, int );
91 static void BlendRGBAR16( filter_t *, picture_t *, const picture_t *,
92                           int, int, int, int, int );
93 static void BlendRGBAR24( filter_t *, picture_t *, const picture_t *,
94                           int, int, int, int, int );
95
96 /*****************************************************************************
97  * OpenFilter: probe the filter and return score
98  *****************************************************************************/
99 static int OpenFilter( vlc_object_t *p_this )
100 {
101     filter_t *p_filter = (filter_t*)p_this;
102
103     /* Check if we can handle that format.
104      * We could try to use a chroma filter if we can't. */
105     int in_chroma = p_filter->fmt_in.video.i_chroma;
106     int out_chroma = p_filter->fmt_out.video.i_chroma;
107     if( ( in_chroma  != VLC_CODEC_YUVA && in_chroma  != VLC_CODEC_I420 &&
108           in_chroma  != VLC_CODEC_YV12 && in_chroma  != VLC_CODEC_YUVP &&
109           in_chroma  != VLC_CODEC_RGBA ) ||
110         ( out_chroma != VLC_CODEC_I420 && out_chroma != VLC_CODEC_J420 &&
111           out_chroma != VLC_CODEC_YV12 &&
112           out_chroma != VLC_CODEC_YUYV && out_chroma != VLC_CODEC_YVYU &&
113           out_chroma != VLC_CODEC_UYVY && out_chroma != VLC_CODEC_VYUY &&
114           out_chroma != VLC_CODEC_RGB15 &&
115           out_chroma != VLC_CODEC_RGB16 &&
116           out_chroma != VLC_CODEC_RGB24 &&
117           out_chroma != VLC_CODEC_RGB32 ) )
118     {
119         return VLC_EGENERIC;
120     }
121
122     /* Misc init */
123     p_filter->pf_video_blend = Blend;
124
125     msg_Dbg( p_filter, "chroma: %4.4s -> %4.4s",
126              (char *)&p_filter->fmt_in.video.i_chroma,
127              (char *)&p_filter->fmt_out.video.i_chroma );
128
129     return VLC_SUCCESS;
130 }
131
132 /*****************************************************************************
133  * CloseFilter: clean up the filter
134  *****************************************************************************/
135 static void CloseFilter( vlc_object_t *p_this )
136 {
137     (void)p_this;
138 }
139
140 /****************************************************************************
141  * Blend: the whole thing
142  ****************************************************************************
143  * This function is called just after the thread is launched.
144  ****************************************************************************/
145 typedef void (*BlendFunction)( filter_t *,
146                        picture_t *, const picture_t *,
147                        int , int , int , int , int );
148
149 #define VLC_CODEC_PLANAR_420 { VLC_CODEC_I420, VLC_CODEC_J420, VLC_CODEC_YV12, 0 }
150 #define VLC_CODEC_PACKED_422 { VLC_CODEC_YUYV, VLC_CODEC_UYVY, VLC_CODEC_YVYU, VLC_CODEC_VYUY, 0 }
151 #define VLC_CODEC_RGB_16 { VLC_CODEC_RGB15, VLC_CODEC_RGB16, 0 }
152 #define VLC_CODEC_RGB_24 { VLC_CODEC_RGB24, VLC_CODEC_RGB32, 0 }
153
154 #define BLEND_CFG( fccSrc, fctPlanar, fctPacked, fctRgb16, fctRgb24  ) \
155     { .src = fccSrc, .p_dst = VLC_CODEC_PLANAR_420, .pf_blend = fctPlanar }, \
156     { .src = fccSrc, .p_dst = VLC_CODEC_PACKED_422, .pf_blend = fctPacked }, \
157     { .src = fccSrc, .p_dst = VLC_CODEC_RGB_16,     .pf_blend = fctRgb16  }, \
158     { .src = fccSrc, .p_dst = VLC_CODEC_RGB_24,     .pf_blend = fctRgb24  }
159
160 static const struct
161 {
162     vlc_fourcc_t src;
163     vlc_fourcc_t p_dst[16];
164     BlendFunction pf_blend;
165 } p_blend_cfg[] = {
166
167     BLEND_CFG( VLC_CODEC_YUVA, BlendYUVAI420, BlendYUVAYUVPacked, BlendYUVARV16, BlendYUVARV24 ),
168
169     BLEND_CFG( VLC_CODEC_YUVP, BlendPalI420, BlendPalYUVPacked, BlendPalRV, BlendPalRV ),
170
171     BLEND_CFG( VLC_CODEC_RGBA, BlendRGBAI420, BlendRGBAYUVPacked, BlendRGBAR16, BlendRGBAR24 ),
172
173     BLEND_CFG( VLC_CODEC_I420, BlendI420I420, BlendI420YUVPacked, BlendI420R16, BlendI420R24 ),
174
175     BLEND_CFG( VLC_CODEC_YV12, BlendI420I420, BlendI420YUVPacked, BlendI420R16, BlendI420R24 ),
176
177     { 0, {0,}, NULL }
178 };
179
180 static void Blend( filter_t *p_filter,
181                    picture_t *p_dst, const picture_t *p_src,
182                    int i_x_offset, int i_y_offset, int i_alpha )
183 {
184     int i_width, i_height;
185
186     if( i_alpha == 0 )
187         return;
188
189     i_width = __MIN((int)p_filter->fmt_out.video.i_visible_width - i_x_offset,
190                     (int)p_filter->fmt_in.video.i_visible_width);
191
192     i_height = __MIN((int)p_filter->fmt_out.video.i_visible_height -i_y_offset,
193                      (int)p_filter->fmt_in.video.i_visible_height);
194
195     if( i_width <= 0 || i_height <= 0 )
196         return;
197
198     video_format_FixRgb( &p_filter->fmt_out.video );
199     video_format_FixRgb( &p_filter->fmt_in.video );
200
201 #if 0
202     msg_Dbg( p_filter, "chroma: %4.4s -> %4.4s\n",
203              (char *)&p_filter->fmt_in.video.i_chroma,
204              (char *)&p_filter->fmt_out.video.i_chroma );
205 #endif
206
207     for( int i = 0; p_blend_cfg[i].src != 0; i++ )
208     {
209         if( p_blend_cfg[i].src != p_filter->fmt_in.video.i_chroma )
210             continue;
211         for( int j = 0; p_blend_cfg[i].p_dst[j] != 0; j++ )
212         {
213             if( p_blend_cfg[i].p_dst[j] != p_filter->fmt_out.video.i_chroma )
214                 continue;
215
216             p_blend_cfg[i].pf_blend( p_filter, p_dst, p_src,
217                                      i_x_offset, i_y_offset,
218                                      i_width, i_height, i_alpha );
219             return;
220         }
221     }
222
223     msg_Dbg( p_filter, "no matching alpha blending routine "
224              "(chroma: %4.4s -> %4.4s)",
225              (char *)&p_filter->fmt_in.video.i_chroma,
226              (char *)&p_filter->fmt_out.video.i_chroma );
227 }
228
229 /***********************************************************************
230  * Utils
231  ***********************************************************************/
232 static inline uint8_t vlc_uint8( int v )
233 {
234     if( v > 255 )
235         return 255;
236     else if( v < 0 )
237         return 0;
238     return v;
239 }
240
241 #define MAX_TRANS 255
242 #define TRANS_BITS  8
243
244 static inline int vlc_blend( int v1, int v2, int a )
245 {
246     /* TODO bench if the tests really increase speed */
247     if( a == 0 )
248         return v2;
249     else if( a == MAX_TRANS )
250         return v1;
251     return ( v1 * a + v2 * (MAX_TRANS - a ) ) >> TRANS_BITS;
252 }
253
254 static inline int vlc_alpha( int t, int a )
255 {
256     if( a == 255 )
257         return t;
258     return (t * a) / 255;
259 }
260
261 static inline void yuv_to_rgb( int *r, int *g, int *b,
262                                uint8_t y1, uint8_t u1, uint8_t v1 )
263 {
264     /* macros used for YUV pixel conversions */
265 #   define SCALEBITS 10
266 #   define ONE_HALF  (1 << (SCALEBITS - 1))
267 #   define FIX(x)    ((int) ((x) * (1<<SCALEBITS) + 0.5))
268
269     int y, cb, cr, r_add, g_add, b_add;
270
271     cb = u1 - 128;
272     cr = v1 - 128;
273     r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;
274     g_add = - FIX(0.34414*255.0/224.0) * cb
275             - FIX(0.71414*255.0/224.0) * cr + ONE_HALF;
276     b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;
277     y = (y1 - 16) * FIX(255.0/219.0);
278     *r = vlc_uint8( (y + r_add) >> SCALEBITS );
279     *g = vlc_uint8( (y + g_add) >> SCALEBITS );
280     *b = vlc_uint8( (y + b_add) >> SCALEBITS );
281 #undef FIX
282 #undef ONE_HALF
283 #undef SCALEBITS
284 }
285
286 static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
287                                int r, int g, int b )
288 {
289     *y = ( ( (  66 * r + 129 * g +  25 * b + 128 ) >> 8 ) + 16 );
290     *u =   ( ( -38 * r -  74 * g + 112 * b + 128 ) >> 8 ) + 128 ;
291     *v =   ( ( 112 * r -  94 * g -  18 * b + 128 ) >> 8 ) + 128 ;
292 }
293
294 static uint8_t *vlc_plane_start( int *pi_pitch,
295                                  const picture_t *p_picture,
296                                  int i_plane,
297                                  int i_x_offset, int i_y_offset,
298                                  const video_format_t *p_fmt,
299                                  int r )
300 {
301     const int i_pitch = p_picture->p[i_plane].i_pitch;
302     uint8_t *p_pixels = p_picture->p[i_plane].p_pixels;
303
304     const int i_dx = ( i_x_offset + p_fmt->i_x_offset ) / r;
305     const int i_dy = ( i_y_offset + p_fmt->i_y_offset ) / r;
306
307     if( pi_pitch )
308         *pi_pitch = i_pitch;
309     return &p_pixels[ i_dy * i_pitch + i_dx ];
310 }
311
312 static void vlc_yuv_packed_index( int *pi_y, int *pi_u, int *pi_v, vlc_fourcc_t i_chroma )
313 {
314     static const struct {
315         vlc_fourcc_t chroma;
316         int y, u ,v;
317     } p_index[] = {
318         { VLC_CODEC_YUYV, 0, 1, 3 },
319         { VLC_CODEC_UYVY, 1, 0, 2 },
320         { VLC_CODEC_YVYU, 0, 3, 1 },
321         { VLC_CODEC_VYUY, 1, 2, 0 },
322         { 0, 0, 0, 0 }
323     };
324     int i;
325
326     for( i = 0; p_index[i].chroma != 0; i++ )
327     {
328         if( p_index[i].chroma == i_chroma )
329             break;
330     }
331     *pi_y = p_index[i].y;
332     *pi_u = p_index[i].u;
333     *pi_v = p_index[i].v;
334 }
335
336 static void vlc_blend_packed( uint8_t *p_dst,
337                               int i_offset0, int i_offset1, int i_offset2,
338                               int c0, int c1, int c2, int i_alpha,
339                               bool b_do12 )
340 {
341     p_dst[i_offset0] = vlc_blend( c0, p_dst[i_offset0], i_alpha );
342     if( b_do12 )
343     {
344         p_dst[i_offset1] = vlc_blend( c1, p_dst[i_offset1], i_alpha );
345         p_dst[i_offset2] = vlc_blend( c2, p_dst[i_offset2], i_alpha );
346     }
347 }
348
349 static void vlc_blend_rgb16( uint16_t *p_dst,
350                              int R, int G, int B, int i_alpha,
351                              const video_format_t *p_fmt )
352 {
353     const int i_pix = *p_dst;
354     const int r = ( i_pix & p_fmt->i_rmask ) >> p_fmt->i_lrshift;
355     const int g = ( i_pix & p_fmt->i_gmask ) >> p_fmt->i_lgshift;
356     const int b = ( i_pix & p_fmt->i_bmask ) >> p_fmt->i_lbshift;
357
358     *p_dst = ( vlc_blend( R >> p_fmt->i_rrshift, r, i_alpha ) << p_fmt->i_lrshift ) |
359              ( vlc_blend( G >> p_fmt->i_rgshift, g, i_alpha ) << p_fmt->i_lgshift ) |
360              ( vlc_blend( B >> p_fmt->i_rbshift, b, i_alpha ) << p_fmt->i_lbshift );
361 }
362
363 static void vlc_rgb_index( int *pi_rindex, int *pi_gindex, int *pi_bindex,
364                            const video_format_t *p_fmt )
365 {
366     if( p_fmt->i_chroma != VLC_CODEC_RGB24 && p_fmt->i_chroma != VLC_CODEC_RGB32 )
367         return;
368
369     /* XXX it will works only if mask are 8 bits aligned */
370 #ifdef WORDS_BIGENDIAN
371     const int i_mask_bits = p_fmt->i_chroma == VLC_CODEC_RGB24 ? 24 : 32;
372     *pi_rindex = ( i_mask_bits - p_fmt->i_lrshift ) / 8;
373     *pi_gindex = ( i_mask_bits - p_fmt->i_lgshift ) / 8;
374     *pi_bindex = ( i_mask_bits - p_fmt->i_lbshift ) / 8;
375 #else
376     *pi_rindex = p_fmt->i_lrshift / 8;
377     *pi_gindex = p_fmt->i_lgshift / 8;
378     *pi_bindex = p_fmt->i_lbshift / 8;
379 #endif
380 }
381
382 /***********************************************************************
383  * YUVA
384  ***********************************************************************/
385 static void BlendYUVAI420( filter_t *p_filter,
386                            picture_t *p_dst, const picture_t *p_src,
387                            int i_x_offset, int i_y_offset,
388                            int i_width, int i_height, int i_alpha )
389 {
390     int i_src_pitch, i_dst_pitch;
391     uint8_t *p_src_y, *p_dst_y;
392     uint8_t *p_src_u, *p_dst_u;
393     uint8_t *p_src_v, *p_dst_v;
394     uint8_t *p_trans;
395     int i_x, i_y, i_trans = 0;
396     bool b_even_scanline = i_y_offset % 2;
397
398     p_dst_y = vlc_plane_start( &i_dst_pitch, p_dst, Y_PLANE,
399                                i_x_offset, i_y_offset, &p_filter->fmt_out.video, 1 );
400     p_dst_u = vlc_plane_start( NULL, p_dst, U_PLANE,
401                                i_x_offset, i_y_offset, &p_filter->fmt_out.video, 2 );
402     p_dst_v = vlc_plane_start( NULL, p_dst, V_PLANE,
403                                i_x_offset, i_y_offset, &p_filter->fmt_out.video, 2 );
404
405     p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
406                                0, 0, &p_filter->fmt_in.video, 1 );
407     p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
408                                0, 0, &p_filter->fmt_in.video, 2 );
409     p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
410                                0, 0, &p_filter->fmt_in.video, 2 );
411     p_trans = vlc_plane_start( NULL, p_src, A_PLANE,
412                                0, 0, &p_filter->fmt_in.video, 1 );
413
414     /* Draw until we reach the bottom of the subtitle */
415     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src_pitch,
416          p_dst_y += i_dst_pitch, p_src_y += i_src_pitch,
417          p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
418          p_src_u += i_src_pitch,
419          p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
420          p_src_v += i_src_pitch )
421     {
422         b_even_scanline = !b_even_scanline;
423
424         /* Draw until we reach the end of the line */
425         for( i_x = 0; i_x < i_width; i_x++ )
426         {
427             if( p_trans )
428                 i_trans = vlc_alpha( p_trans[i_x], i_alpha );
429
430             if( !i_trans )
431                 continue;
432
433             /* Blending */
434             p_dst_y[i_x] = vlc_blend( p_src_y[i_x], p_dst_y[i_x], i_trans );
435             if( b_even_scanline && i_x % 2 == 0 )
436             {
437                 p_dst_u[i_x/2] = vlc_blend( p_src_u[i_x], p_dst_u[i_x/2], i_trans );
438                 p_dst_v[i_x/2] = vlc_blend( p_src_v[i_x], p_dst_v[i_x/2], i_trans );
439             }
440         }
441     }
442 }
443
444 static void BlendYUVARV16( filter_t *p_filter,
445                            picture_t *p_dst_pic, const picture_t *p_src,
446                            int i_x_offset, int i_y_offset,
447                            int i_width, int i_height, int i_alpha )
448 {
449     int i_src_pitch, i_dst_pitch;
450     uint8_t *p_dst, *p_src_y;
451     uint8_t *p_src_u, *p_src_v;
452     uint8_t *p_trans;
453     int i_x, i_y, i_pix_pitch, i_trans = 0;
454     int r, g, b;
455
456     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
457     i_dst_pitch = p_dst_pic->p->i_pitch;
458     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
459             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
460             p_dst_pic->p->i_pitch *
461             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
462
463     p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
464                                0, 0, &p_filter->fmt_in.video, 1 );
465     p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
466                                0, 0, &p_filter->fmt_in.video, 2 );
467     p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
468                                0, 0, &p_filter->fmt_in.video, 2 );
469     p_trans = vlc_plane_start( NULL, p_src, A_PLANE,
470                                0, 0, &p_filter->fmt_in.video, 1 );
471
472     /* Draw until we reach the bottom of the subtitle */
473     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src_pitch,
474          p_dst += i_dst_pitch,
475          p_src_y += i_src_pitch, p_src_u += i_src_pitch,
476          p_src_v += i_src_pitch )
477     {
478         /* Draw until we reach the end of the line */
479         for( i_x = 0; i_x < i_width; i_x++ )
480         {
481             if( p_trans )
482                 i_trans = vlc_alpha( p_trans[i_x], i_alpha );
483             if( !i_trans )
484                 continue;
485
486             /* Blending */
487             yuv_to_rgb( &r, &g, &b,
488                         p_src_y[i_x], p_src_u[i_x], p_src_v[i_x] );
489
490             vlc_blend_rgb16( (uint16_t*)&p_dst[i_x * i_pix_pitch],
491                              r, g, b, i_trans, &p_filter->fmt_out.video );
492         }
493     }
494 }
495
496 static void BlendYUVARV24( filter_t *p_filter,
497                            picture_t *p_dst_pic, const picture_t *p_src,
498                            int i_x_offset, int i_y_offset,
499                            int i_width, int i_height, int i_alpha )
500 {
501     int i_src_pitch, i_dst_pitch;
502     uint8_t *p_dst, *p_src_y;
503     uint8_t *p_src_u, *p_src_v;
504     uint8_t *p_trans;
505     int i_x, i_y, i_pix_pitch, i_trans = 0;
506     int r, g, b;
507
508     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
509     i_dst_pitch = p_dst_pic->p->i_pitch;
510     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
511             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
512             p_dst_pic->p->i_pitch *
513             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
514
515     p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
516                                0, 0, &p_filter->fmt_in.video, 1 );
517     p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
518                                0, 0, &p_filter->fmt_in.video, 2 );
519     p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
520                                0, 0, &p_filter->fmt_in.video, 2 );
521     p_trans = vlc_plane_start( NULL, p_src, A_PLANE,
522                                0, 0, &p_filter->fmt_in.video, 1 );
523
524     if( (i_pix_pitch == 4)
525      && (((((intptr_t)p_dst)|i_dst_pitch) /* FIXME? */
526           & 3) == 0) )
527     {
528         /*
529         ** if picture pixels are 32 bits long and lines addresses are 32 bit
530         ** aligned, optimize rendering
531         */
532         uint32_t *p32_dst = (uint32_t *)p_dst;
533         uint32_t i32_dst_pitch = (uint32_t)(i_dst_pitch>>2);
534
535         int i_rshift, i_gshift, i_bshift;
536         uint32_t i_rmask, i_gmask, i_bmask;
537
538         i_rmask = p_filter->fmt_out.video.i_rmask;
539         i_gmask = p_filter->fmt_out.video.i_gmask;
540         i_bmask = p_filter->fmt_out.video.i_bmask;
541         i_rshift = p_filter->fmt_out.video.i_lrshift;
542         i_gshift = p_filter->fmt_out.video.i_lgshift;
543         i_bshift = p_filter->fmt_out.video.i_lbshift;
544
545         /* Draw until we reach the bottom of the subtitle */
546         for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src_pitch,
547              p32_dst += i32_dst_pitch,
548              p_src_y += i_src_pitch, p_src_u += i_src_pitch,
549              p_src_v += i_src_pitch )
550         {
551             /* Draw until we reach the end of the line */
552             for( i_x = 0; i_x < i_width; i_x++ )
553             {
554                 if( p_trans )
555                     i_trans = vlc_alpha( p_trans[i_x], i_alpha );
556                 if( !i_trans )
557                     continue;
558
559                 if( i_trans == MAX_TRANS )
560                 {
561                     /* Completely opaque. Completely overwrite underlying pixel */
562                     yuv_to_rgb( &r, &g, &b,
563                                 p_src_y[i_x], p_src_u[i_x], p_src_v[i_x] );
564
565                     p32_dst[i_x] = (r<<i_rshift) |
566                                    (g<<i_gshift) |
567                                    (b<<i_bshift);
568                 }
569                 else
570                 {
571                     /* Blending */
572                     uint32_t i_pix_dst = p32_dst[i_x];
573                     yuv_to_rgb( &r, &g, &b,
574                                 p_src_y[i_x], p_src_u[i_x], p_src_v[i_x] );
575
576                     p32_dst[i_x] = ( vlc_blend( r, (i_pix_dst & i_rmask)>>i_rshift, i_trans ) << i_rshift ) |
577                                    ( vlc_blend( g, (i_pix_dst & i_gmask)>>i_gshift, i_trans ) << i_gshift ) |
578                                    ( vlc_blend( b, (i_pix_dst & i_bmask)>>i_bshift, i_trans ) << i_bshift );
579                 }
580             }
581         }
582     }
583     else
584     {
585         int i_rindex, i_gindex, i_bindex;
586         uint32_t i_rmask, i_gmask, i_bmask;
587
588         i_rmask = p_filter->fmt_out.video.i_rmask;
589         i_gmask = p_filter->fmt_out.video.i_gmask;
590         i_bmask = p_filter->fmt_out.video.i_bmask;
591
592         vlc_rgb_index( &i_rindex, &i_gindex, &i_bindex, &p_filter->fmt_out.video );
593
594         /* Draw until we reach the bottom of the subtitle */
595         for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src_pitch,
596              p_dst += i_dst_pitch,
597              p_src_y += i_src_pitch, p_src_u += i_src_pitch,
598              p_src_v += i_src_pitch )
599         {
600             /* Draw until we reach the end of the line */
601             for( i_x = 0; i_x < i_width; i_x++ )
602             {
603                 if( p_trans )
604                     i_trans = vlc_alpha( p_trans[i_x], i_alpha );
605                 if( !i_trans )
606                     continue;
607
608                 /* Blending */
609                 yuv_to_rgb( &r, &g, &b,
610                             p_src_y[i_x], p_src_u[i_x], p_src_v[i_x] );
611
612                 vlc_blend_packed( &p_dst[ i_x * i_pix_pitch],
613                                   i_rindex, i_gindex, i_bindex,
614                                   r, g, b, i_alpha, true );
615             }
616         }
617     }
618 }
619
620 static void BlendYUVAYUVPacked( filter_t *p_filter,
621                                 picture_t *p_dst_pic, const picture_t *p_src,
622                                 int i_x_offset, int i_y_offset,
623                                 int i_width, int i_height, int i_alpha )
624 {
625     int i_src_pitch, i_dst_pitch;
626     uint8_t *p_dst, *p_src_y;
627     uint8_t *p_src_u, *p_src_v;
628     uint8_t *p_trans;
629     int i_x, i_y, i_pix_pitch, i_trans = 0;
630     bool b_even = !((i_x_offset + p_filter->fmt_out.video.i_x_offset)%2);
631     int i_l_offset, i_u_offset, i_v_offset;
632
633     vlc_yuv_packed_index( &i_l_offset, &i_u_offset, &i_v_offset,
634                           p_filter->fmt_out.video.i_chroma );
635
636     i_pix_pitch = 2;
637     i_dst_pitch = p_dst_pic->p->i_pitch;
638     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
639             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
640             p_dst_pic->p->i_pitch *
641             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
642
643     p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
644                                0, 0, &p_filter->fmt_in.video, 1 );
645     p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
646                                0, 0, &p_filter->fmt_in.video, 2 );
647     p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
648                                0, 0, &p_filter->fmt_in.video, 2 );
649     p_trans = vlc_plane_start( NULL, p_src, A_PLANE,
650                                0, 0, &p_filter->fmt_in.video, 1 );
651
652     i_width &= ~1; /* Needs to be a multiple of 2 */
653
654     /* Draw until we reach the bottom of the subtitle */
655     for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src_pitch,
656          p_dst += i_dst_pitch,
657          p_src_y += i_src_pitch, p_src_u += i_src_pitch,
658          p_src_v += i_src_pitch )
659     {
660         /* Draw until we reach the end of the line */
661         for( i_x = 0; i_x < i_width; i_x++, b_even = !b_even )
662         {
663             i_trans = vlc_alpha( p_trans[i_x], i_alpha );
664             if( !i_trans )
665                 continue;
666
667             /* Blending */
668             if( b_even )
669             {
670                 int i_u;
671                 int i_v;
672                 /* FIXME what's with 0xaa ? */
673                 if( p_trans[i_x+1] > 0xaa )
674                 {
675                     i_u = (p_src_u[i_x]+p_src_u[i_x+1])>>1;
676                     i_v = (p_src_v[i_x]+p_src_v[i_x+1])>>1;
677                 }
678                 else
679                 {
680                     i_u = p_src_u[i_x];
681                     i_v = p_src_v[i_x];
682                 }
683
684                 vlc_blend_packed( &p_dst[i_x * 2],
685                                   i_l_offset, i_u_offset, i_v_offset,
686                                   p_src_y[i_x], i_u, i_v, i_trans, true );
687             }
688             else
689             {
690                 p_dst[i_x * 2 + i_l_offset] = vlc_blend( p_src_y[i_x], p_dst[i_x * 2 + i_l_offset], i_trans );
691             }
692         }
693     }
694 }
695 /***********************************************************************
696  * I420, YV12
697  ***********************************************************************/
698 static void BlendI420I420( filter_t *p_filter,
699                            picture_t *p_dst, const picture_t *p_src,
700                            int i_x_offset, int i_y_offset,
701                            int i_width, int i_height, int i_alpha )
702 {
703     int i_src_pitch, i_dst_pitch;
704     uint8_t *p_src_y, *p_dst_y;
705     uint8_t *p_src_u, *p_dst_u;
706     uint8_t *p_src_v, *p_dst_v;
707     int i_x, i_y;
708     bool b_even_scanline = i_y_offset % 2;
709
710     if( i_alpha == 0xff )
711     {
712         BlendI420I420_no_alpha( p_filter, p_dst, p_src,
713                                 i_x_offset, i_y_offset, i_width, i_height );
714         return;
715     }
716
717
718     i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
719     p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
720               p_filter->fmt_out.video.i_x_offset +
721               p_dst->p[Y_PLANE].i_pitch *
722               ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
723     p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
724               p_filter->fmt_out.video.i_x_offset/2 +
725               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
726               p_dst->p[U_PLANE].i_pitch;
727     p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
728               p_filter->fmt_out.video.i_x_offset/2 +
729               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
730               p_dst->p[V_PLANE].i_pitch;
731
732     p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
733                                0, 0, &p_filter->fmt_in.video, 1 );
734     p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
735                                0, 0, &p_filter->fmt_in.video, 2 );
736     p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
737                                0, 0, &p_filter->fmt_in.video, 2 );
738     i_width &= ~1;
739
740     /* Draw until we reach the bottom of the subtitle */
741     for( i_y = 0; i_y < i_height; i_y++,
742          p_dst_y += i_dst_pitch,
743          p_src_y += i_src_pitch )
744     {
745         if( b_even_scanline )
746         {
747             p_dst_u  += i_dst_pitch/2;
748             p_dst_v  += i_dst_pitch/2;
749         }
750         b_even_scanline = !b_even_scanline;
751
752         /* Draw until we reach the end of the line */
753         for( i_x = 0; i_x < i_width; i_x++ )
754         {
755             if( !i_alpha )
756                 continue;
757
758             /* Blending */
759             p_dst_y[i_x] = vlc_blend( p_src_y[i_x], p_dst_y[i_x], i_alpha );
760             if( b_even_scanline && i_x % 2 == 0 )
761             {
762                 p_dst_u[i_x/2] = vlc_blend( p_src_u[i_x/2], p_dst_u[i_x/2], i_alpha );
763                 p_dst_v[i_x/2] = vlc_blend( p_src_v[i_x/2], p_dst_v[i_x/2], i_alpha );
764             }
765         }
766         if( i_y%2 == 1 )
767         {
768             p_src_u += i_src_pitch/2;
769             p_src_v += i_src_pitch/2;
770         }
771     }
772 }
773 static void BlendI420I420_no_alpha( filter_t *p_filter,
774                                     picture_t *p_dst, const picture_t *p_src,
775                                     int i_x_offset, int i_y_offset,
776                                     int i_width, int i_height )
777 {
778     int i_src_pitch, i_dst_pitch;
779     uint8_t *p_src_y, *p_dst_y;
780     uint8_t *p_src_u, *p_dst_u;
781     uint8_t *p_src_v, *p_dst_v;
782     int i_y;
783     bool b_even_scanline = i_y_offset % 2;
784
785     i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
786     p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
787               p_filter->fmt_out.video.i_x_offset +
788               p_dst->p[Y_PLANE].i_pitch *
789               ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
790     p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
791               p_filter->fmt_out.video.i_x_offset/2 +
792               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
793               p_dst->p[U_PLANE].i_pitch;
794     p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
795               p_filter->fmt_out.video.i_x_offset/2 +
796               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
797               p_dst->p[V_PLANE].i_pitch;
798
799     p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
800                                0, 0, &p_filter->fmt_in.video, 1 );
801     p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
802                                0, 0, &p_filter->fmt_in.video, 2 );
803     p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
804                                0, 0, &p_filter->fmt_in.video, 2 );
805
806     i_width &= ~1;
807
808     /* Draw until we reach the bottom of the subtitle */
809     for( i_y = 0; i_y < i_height;
810             i_y++, p_dst_y += i_dst_pitch, p_src_y += i_src_pitch )
811     {
812         /* Completely opaque. Completely overwrite underlying pixel */
813         vlc_memcpy( p_dst_y, p_src_y, i_width );
814         if( b_even_scanline )
815         {
816             p_dst_u  += i_dst_pitch/2;
817             p_dst_v  += i_dst_pitch/2;
818         }
819         else
820         {
821             vlc_memcpy( p_dst_u, p_src_u, i_width/2 );
822             vlc_memcpy( p_dst_v, p_src_v, i_width/2 );
823         }
824         b_even_scanline = !b_even_scanline;
825         if( i_y%2 == 1 )
826         {
827             p_src_u += i_src_pitch/2;
828             p_src_v += i_src_pitch/2;
829         }
830     }
831 }
832
833 static void BlendI420R16( filter_t *p_filter,
834                           picture_t *p_dst_pic, const picture_t *p_src,
835                           int i_x_offset, int i_y_offset,
836                           int i_width, int i_height, int i_alpha )
837 {
838     int i_src_pitch, i_dst_pitch;
839     uint8_t *p_dst, *p_src_y;
840     uint8_t *p_src_u, *p_src_v;
841     int i_x, i_y, i_pix_pitch;
842     int r, g, b;
843
844     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
845     i_dst_pitch = p_dst_pic->p->i_pitch;
846     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
847             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
848             p_dst_pic->p->i_pitch *
849             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
850
851     p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
852                                0, 0, &p_filter->fmt_in.video, 1 );
853     p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
854                                0, 0, &p_filter->fmt_in.video, 2 );
855     p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
856                                 0, 0, &p_filter->fmt_in.video, 2 );
857
858     /* Draw until we reach the bottom of the subtitle */
859     for( i_y = 0; i_y < i_height; i_y++,
860          p_dst += i_dst_pitch,
861          p_src_y += i_src_pitch )
862     {
863         /* Draw until we reach the end of the line */
864         for( i_x = 0; i_x < i_width; i_x++ )
865         {
866             /* Blending */
867             yuv_to_rgb( &r, &g, &b,
868                         p_src_y[i_x], p_src_u[i_x/2], p_src_v[i_x/2] );
869
870             vlc_blend_rgb16( (uint16_t*)&p_dst[i_x * i_pix_pitch],
871                              r, g, b, i_alpha, &p_filter->fmt_out.video );
872         }
873         if( i_y%2 == 1 )
874         {
875             p_src_u += i_src_pitch/2;
876             p_src_v += i_src_pitch/2;
877         }
878     }
879 }
880
881 static void BlendI420R24( filter_t *p_filter,
882                           picture_t *p_dst_pic, const picture_t *p_src,
883                           int i_x_offset, int i_y_offset,
884                           int i_width, int i_height, int i_alpha )
885 {
886     int i_src_pitch, i_dst_pitch;
887     uint8_t *p_dst, *p_src_y;
888     uint8_t *p_src_u, *p_src_v;
889     int i_x, i_y, i_pix_pitch;
890     int i_rindex, i_gindex, i_bindex;
891     int r, g, b;
892
893     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
894     i_dst_pitch = p_dst_pic->p->i_pitch;
895     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
896             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
897             p_dst_pic->p->i_pitch *
898             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
899
900     p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
901                                0, 0, &p_filter->fmt_in.video, 1 );
902     p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
903                                0, 0, &p_filter->fmt_in.video, 2 );
904     p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
905                                0, 0, &p_filter->fmt_in.video, 2 );
906
907     vlc_rgb_index( &i_rindex, &i_gindex, &i_bindex, &p_filter->fmt_out.video );
908
909     /* Draw until we reach the bottom of the subtitle */
910     for( i_y = 0; i_y < i_height; i_y++,
911          p_dst += i_dst_pitch,
912          p_src_y += i_src_pitch, p_src_u += i_src_pitch,
913          p_src_v += i_src_pitch )
914     {
915         /* Draw until we reach the end of the line */
916         for( i_x = 0; i_x < i_width; i_x++ )
917         {
918             if( !i_alpha )
919                 continue;
920
921             /* Blending */
922             yuv_to_rgb( &r, &g, &b,
923                         p_src_y[i_x], p_src_u[i_x/2], p_src_v[i_x/2] );
924
925             vlc_blend_packed( &p_dst[i_x * i_pix_pitch],
926                               i_rindex, i_gindex, i_bindex, r, g, b, i_alpha, true );
927         }
928         if( i_y%2 == 1 )
929         {
930             p_src_u += i_src_pitch/2;
931             p_src_v += i_src_pitch/2;
932         }
933     }
934 }
935
936 static void BlendI420YUVPacked( filter_t *p_filter,
937                                 picture_t *p_dst_pic, const picture_t *p_src,
938                                 int i_x_offset, int i_y_offset,
939                                 int i_width, int i_height, int i_alpha )
940 {
941     int i_src_pitch, i_dst_pitch;
942     uint8_t *p_dst, *p_src_y;
943     uint8_t *p_src_u, *p_src_v;
944     int i_x, i_y, i_pix_pitch;
945     bool b_even = !((i_x_offset + p_filter->fmt_out.video.i_x_offset)%2);
946     int i_l_offset, i_u_offset, i_v_offset;
947
948     vlc_yuv_packed_index( &i_l_offset, &i_u_offset, &i_v_offset,
949                           p_filter->fmt_out.video.i_chroma );
950
951     i_pix_pitch = 2;
952     i_dst_pitch = p_dst_pic->p->i_pitch;
953     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
954             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
955             p_dst_pic->p->i_pitch *
956             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
957
958     p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
959                                0, 0, &p_filter->fmt_in.video, 1 );
960     p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
961                                0, 0, &p_filter->fmt_in.video, 2 );
962     p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
963                                0, 0, &p_filter->fmt_in.video, 2 );
964
965     i_width &= ~1; /* Needs to be a multiple of 2 */
966
967     /* Draw until we reach the bottom of the subtitle */
968     for( i_y = 0; i_y < i_height; i_y++,
969          p_dst += i_dst_pitch,
970          p_src_y += i_src_pitch, p_src_u += i_src_pitch,
971          p_src_v += i_src_pitch )
972     {
973         /* Draw until we reach the end of the line */
974         for( i_x = 0; i_x < i_width; i_x++, b_even = !b_even )
975         {
976             if( !i_alpha )
977                 continue;
978
979             /* Blending */
980             vlc_blend_packed( &p_dst[i_x * 2],
981                               i_l_offset, i_u_offset, i_v_offset,
982                               p_src_y[i_x], p_src_u[i_x/2], p_src_v[i_x/2], i_alpha, b_even );
983         }
984         if( i_y%2 == 1 )
985         {
986             p_src_u += i_src_pitch/2;
987             p_src_v += i_src_pitch/2;
988         }
989     }
990 }
991
992 /***********************************************************************
993  * YUVP
994  ***********************************************************************/
995 static void BlendPalI420( filter_t *p_filter,
996                           picture_t *p_dst, const picture_t *p_src_pic,
997                           int i_x_offset, int i_y_offset,
998                           int i_width, int i_height, int i_alpha )
999 {
1000     int i_src_pitch, i_dst_pitch;
1001     uint8_t *p_src, *p_dst_y;
1002     uint8_t *p_dst_u;
1003     uint8_t *p_dst_v;
1004     int i_x, i_y, i_trans;
1005     bool b_even_scanline = i_y_offset % 2;
1006
1007     i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
1008     p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
1009               p_filter->fmt_out.video.i_x_offset +
1010               p_dst->p[Y_PLANE].i_pitch *
1011               ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1012     p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
1013               p_filter->fmt_out.video.i_x_offset/2 +
1014               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
1015               p_dst->p[U_PLANE].i_pitch;
1016     p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
1017               p_filter->fmt_out.video.i_x_offset/2 +
1018               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
1019               p_dst->p[V_PLANE].i_pitch;
1020
1021     i_src_pitch = p_src_pic->p->i_pitch;
1022     p_src = p_src_pic->p->p_pixels + p_filter->fmt_in.video.i_x_offset +
1023             i_src_pitch * p_filter->fmt_in.video.i_y_offset;
1024
1025 #define p_pal p_filter->fmt_in.video.p_palette->palette
1026
1027     /* Draw until we reach the bottom of the subtitle */
1028     for( i_y = 0; i_y < i_height; i_y++,
1029          p_dst_y += i_dst_pitch,
1030          p_src += i_src_pitch,
1031          p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
1032          p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0 )
1033     {
1034         const uint8_t *p_trans = p_src;
1035         b_even_scanline = !b_even_scanline;
1036
1037         /* Draw until we reach the end of the line */
1038         for( i_x = 0; i_x < i_width; i_x++ )
1039         {
1040             i_trans = vlc_alpha( p_pal[p_trans[i_x]][3], i_alpha );
1041             if( !i_trans )
1042                 continue;
1043
1044             /* Blending */
1045             p_dst_y[i_x] = vlc_blend( p_pal[p_src[i_x]][0], p_dst_y[i_x], i_trans );
1046             if( b_even_scanline && ((i_x % 2) == 0) )
1047             {
1048                 p_dst_u[i_x/2] = vlc_blend( p_pal[p_src[i_x]][1], p_dst_u[i_x/2], i_trans );
1049                 p_dst_v[i_x/2] = vlc_blend( p_pal[p_src[i_x]][2], p_dst_v[i_x/2], i_trans );
1050             }
1051         }
1052     }
1053 #undef p_pal
1054 }
1055
1056 static void BlendPalYUVPacked( filter_t *p_filter,
1057                                picture_t *p_dst_pic, const picture_t *p_src_pic,
1058                                int i_x_offset, int i_y_offset,
1059                                int i_width, int i_height, int i_alpha )
1060 {
1061     int i_src_pitch, i_dst_pitch;
1062     uint8_t *p_src, *p_dst;
1063     int i_x, i_y, i_pix_pitch, i_trans;
1064     bool b_even = !((i_x_offset + p_filter->fmt_out.video.i_x_offset)%2);
1065     int i_l_offset, i_u_offset, i_v_offset;
1066
1067     vlc_yuv_packed_index( &i_l_offset, &i_u_offset, &i_v_offset,
1068                           p_filter->fmt_out.video.i_chroma );
1069
1070     i_pix_pitch = 2;
1071     i_dst_pitch = p_dst_pic->p->i_pitch;
1072     p_dst = p_dst_pic->p->p_pixels + i_pix_pitch * (i_x_offset +
1073             p_filter->fmt_out.video.i_x_offset) + p_dst_pic->p->i_pitch *
1074             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1075
1076     i_src_pitch = p_src_pic->p->i_pitch;
1077     p_src = p_src_pic->p->p_pixels + p_filter->fmt_in.video.i_x_offset +
1078             i_src_pitch * p_filter->fmt_in.video.i_y_offset;
1079
1080     i_width &= ~1; /* Needs to be a multiple of 2 */
1081
1082 #define p_pal p_filter->fmt_in.video.p_palette->palette
1083
1084     /* Draw until we reach the bottom of the subtitle */
1085     for( i_y = 0; i_y < i_height; i_y++,
1086          p_dst += i_dst_pitch, p_src += i_src_pitch )
1087     {
1088         const uint8_t *p_trans = p_src;
1089         /* Draw until we reach the end of the line */
1090         for( i_x = 0; i_x < i_width; i_x++, b_even = !b_even )
1091         {
1092             i_trans = vlc_alpha( p_pal[p_trans[i_x]][3], i_alpha );
1093             if( !i_trans )
1094                 continue;
1095
1096             /* Blending */
1097             if( b_even )
1098             {
1099                 uint16_t i_u;
1100                 uint16_t i_v;
1101                 if( p_trans[i_x+1] > 0xaa )
1102                 {
1103                     i_u = (p_pal[p_src[i_x]][1] + p_pal[p_src[i_x+1]][1]) >> 1;
1104                     i_v = (p_pal[p_src[i_x]][2] + p_pal[p_src[i_x+1]][2]) >> 1;
1105                 }
1106                 else
1107                 {
1108                     i_u = p_pal[p_src[i_x]][1];
1109                     i_v = p_pal[p_src[i_x]][2];
1110                 }
1111
1112                 vlc_blend_packed( &p_dst[i_x * 2],
1113                                   i_l_offset, i_u_offset, i_v_offset,
1114                                   p_pal[p_src[i_x]][0], i_u, i_v, i_trans, true );
1115             }
1116             else
1117             {
1118                 p_dst[i_x * 2 + i_l_offset] = vlc_blend( p_pal[p_src[i_x]][0], p_dst[i_x * 2 + i_l_offset], i_trans );
1119             }
1120         }
1121     }
1122 #undef p_pal
1123 }
1124
1125 static void BlendPalRV( filter_t *p_filter,
1126                         picture_t *p_dst_pic, const picture_t *p_src_pic,
1127                         int i_x_offset, int i_y_offset,
1128                         int i_width, int i_height, int i_alpha )
1129 {
1130     int i_src_pitch, i_dst_pitch;
1131     uint8_t *p_src, *p_dst;
1132     int i_x, i_y, i_pix_pitch, i_trans;
1133     video_palette_t rgbpalette;
1134     int i_rindex, i_gindex, i_bindex;
1135
1136     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
1137     i_dst_pitch = p_dst_pic->p->i_pitch;
1138     p_dst = p_dst_pic->p->p_pixels + i_pix_pitch * (i_x_offset +
1139             p_filter->fmt_out.video.i_x_offset) + p_dst_pic->p->i_pitch *
1140             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1141
1142     i_src_pitch = p_src_pic->p->i_pitch;
1143     p_src = p_src_pic->p->p_pixels + p_filter->fmt_in.video.i_x_offset +
1144             i_src_pitch * p_filter->fmt_in.video.i_y_offset;
1145
1146 #define p_pal p_filter->fmt_in.video.p_palette->palette
1147 #define rgbpal rgbpalette.palette
1148
1149     /* Convert palette first */
1150     for( i_y = 0; i_y < p_filter->fmt_in.video.p_palette->i_entries && i_y < 256; i_y++ )
1151     {
1152         int r, g, b;
1153
1154         yuv_to_rgb( &r, &g, &b, p_pal[i_y][0], p_pal[i_y][1], p_pal[i_y][2] );
1155         rgbpal[i_y][0] = r;
1156         rgbpal[i_y][1] = g;
1157         rgbpal[i_y][2] = b;
1158     }
1159
1160     /* */
1161     vlc_rgb_index( &i_rindex, &i_gindex, &i_bindex, &p_filter->fmt_out.video );
1162
1163     /* Draw until we reach the bottom of the subtitle */
1164     for( i_y = 0; i_y < i_height; i_y++,
1165          p_dst += i_dst_pitch, p_src += i_src_pitch )
1166     {
1167         const uint8_t *p_trans = p_src;
1168         /* Draw until we reach the end of the line */
1169         for( i_x = 0; i_x < i_width; i_x++ )
1170         {
1171             i_trans = vlc_alpha( p_pal[p_trans[i_x]][3], i_alpha );
1172             if( !i_trans )
1173                 continue;
1174
1175             /* Blending */
1176             if( p_filter->fmt_out.video.i_chroma == VLC_CODEC_RGB15 || p_filter->fmt_out.video.i_chroma == VLC_CODEC_RGB16 )
1177                 vlc_blend_rgb16( (uint16_t*)&p_dst[i_x * i_pix_pitch],
1178                                   rgbpal[p_src[i_x]][0], rgbpal[p_src[i_x]][1], rgbpal[p_src[i_x]][2],
1179                                   i_trans,
1180                                   &p_filter->fmt_out.video );
1181             else
1182                 vlc_blend_packed( &p_dst[i_x * i_pix_pitch],
1183                                   i_rindex, i_gindex, i_bindex,
1184                                   rgbpal[p_src[i_x]][0], rgbpal[p_src[i_x]][1], rgbpal[p_src[i_x]][2],
1185                                   i_trans, true );
1186         }
1187     }
1188
1189 #undef p_pal
1190 #undef rgbpal
1191 }
1192
1193 /***********************************************************************
1194  * RGBA
1195  ***********************************************************************/
1196 static void BlendRGBAI420( filter_t *p_filter,
1197                            picture_t *p_dst, const picture_t *p_src_pic,
1198                            int i_x_offset, int i_y_offset,
1199                            int i_width, int i_height, int i_alpha )
1200 {
1201     int i_src_pitch, i_dst_pitch, i_src_pix_pitch;
1202     uint8_t *p_dst_y;
1203     uint8_t *p_dst_u;
1204     uint8_t *p_dst_v;
1205     uint8_t *p_src;
1206     int i_x, i_y, i_trans;
1207     uint8_t y, u, v;
1208
1209     bool b_even_scanline = i_y_offset % 2;
1210
1211     i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
1212     p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
1213               p_filter->fmt_out.video.i_x_offset +
1214               p_dst->p[Y_PLANE].i_pitch *
1215               ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1216     p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
1217               p_filter->fmt_out.video.i_x_offset/2 +
1218               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
1219               p_dst->p[U_PLANE].i_pitch;
1220     p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
1221               p_filter->fmt_out.video.i_x_offset/2 +
1222               ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
1223               p_dst->p[V_PLANE].i_pitch;
1224
1225     i_src_pix_pitch = p_src_pic->p->i_pixel_pitch;
1226     i_src_pitch = p_src_pic->p->i_pitch;
1227     p_src = p_src_pic->p->p_pixels +
1228             p_filter->fmt_in.video.i_x_offset * i_src_pix_pitch +
1229             p_src_pic->p->i_pitch * p_filter->fmt_in.video.i_y_offset;
1230
1231
1232     /* Draw until we reach the bottom of the subtitle */
1233     for( i_y = 0; i_y < i_height; i_y++,
1234          p_dst_y += i_dst_pitch,
1235          p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
1236          p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
1237          p_src += i_src_pitch )
1238     {
1239         b_even_scanline = !b_even_scanline;
1240
1241         /* Draw until we reach the end of the line */
1242         for( i_x = 0; i_x < i_width; i_x++ )
1243         {
1244             const int R = p_src[i_x * i_src_pix_pitch + 0];
1245             const int G = p_src[i_x * i_src_pix_pitch + 1];
1246             const int B = p_src[i_x * i_src_pix_pitch + 2];
1247
1248             i_trans = vlc_alpha( p_src[i_x * i_src_pix_pitch + 3], i_alpha );
1249             if( !i_trans )
1250                 continue;
1251
1252             /* Blending */
1253             rgb_to_yuv( &y, &u, &v, R, G, B );
1254
1255             p_dst_y[i_x] = vlc_blend( y, p_dst_y[i_x], i_trans );
1256             if( b_even_scanline && i_x % 2 == 0 )
1257             {
1258                 p_dst_u[i_x/2] = vlc_blend( u, p_dst_u[i_x/2], i_trans );
1259                 p_dst_v[i_x/2] = vlc_blend( v, p_dst_v[i_x/2], i_trans );
1260             }
1261         }
1262     }
1263 }
1264
1265 static void BlendRGBAR24( filter_t *p_filter,
1266                           picture_t *p_dst_pic, const picture_t *p_src_pic,
1267                           int i_x_offset, int i_y_offset,
1268                           int i_width, int i_height, int i_alpha )
1269 {
1270     int i_src_pitch, i_dst_pitch;
1271     uint8_t *p_dst, *p_src;
1272     int i_x, i_y, i_pix_pitch, i_trans, i_src_pix_pitch;
1273     int i_rindex, i_gindex, i_bindex;
1274
1275     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
1276     i_dst_pitch = p_dst_pic->p->i_pitch;
1277     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
1278             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1279             p_dst_pic->p->i_pitch *
1280             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1281
1282     i_src_pix_pitch = p_src_pic->p->i_pixel_pitch;
1283     i_src_pitch = p_src_pic->p->i_pitch;
1284     p_src = p_src_pic->p->p_pixels +
1285             p_filter->fmt_in.video.i_x_offset * i_src_pix_pitch +
1286             p_src_pic->p->i_pitch * p_filter->fmt_in.video.i_y_offset;
1287
1288     vlc_rgb_index( &i_rindex, &i_gindex, &i_bindex, &p_filter->fmt_out.video );
1289
1290     /* Draw until we reach the bottom of the subtitle */
1291     for( i_y = 0; i_y < i_height; i_y++,
1292          p_dst += i_dst_pitch, p_src += i_src_pitch )
1293     {
1294         /* Draw until we reach the end of the line */
1295         for( i_x = 0; i_x < i_width; i_x++ )
1296         {
1297             const int R = p_src[i_x * i_src_pix_pitch + 0];
1298             const int G = p_src[i_x * i_src_pix_pitch + 1];
1299             const int B = p_src[i_x * i_src_pix_pitch + 2];
1300
1301             i_trans = vlc_alpha( p_src[i_x * i_src_pix_pitch + 3], i_alpha );
1302             if( !i_trans )
1303                 continue;
1304
1305             /* Blending */
1306             vlc_blend_packed( &p_dst[i_x * i_pix_pitch],
1307                               i_rindex, i_gindex, i_bindex,
1308                               R, G, B, i_trans, true );
1309         }
1310     }
1311 }
1312
1313 static void BlendRGBAR16( filter_t *p_filter,
1314                           picture_t *p_dst_pic, const picture_t *p_src_pic,
1315                           int i_x_offset, int i_y_offset,
1316                           int i_width, int i_height, int i_alpha )
1317 {
1318     int i_src_pitch, i_dst_pitch;
1319     uint8_t *p_dst, *p_src;
1320     int i_x, i_y, i_pix_pitch, i_trans, i_src_pix_pitch;
1321
1322     i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
1323     i_dst_pitch = p_dst_pic->p->i_pitch;
1324     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
1325             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1326             p_dst_pic->p->i_pitch *
1327             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1328
1329     i_src_pix_pitch = p_src_pic->p->i_pixel_pitch;
1330     i_src_pitch = p_src_pic->p->i_pitch;
1331     p_src = p_src_pic->p->p_pixels +
1332             p_filter->fmt_in.video.i_x_offset * i_src_pix_pitch +
1333             p_src_pic->p->i_pitch * p_filter->fmt_in.video.i_y_offset;
1334
1335     /* Draw until we reach the bottom of the subtitle */
1336     for( i_y = 0; i_y < i_height; i_y++,
1337          p_dst += i_dst_pitch, p_src += i_src_pitch )
1338     {
1339         /* Draw until we reach the end of the line */
1340         for( i_x = 0; i_x < i_width; i_x++ )
1341         {
1342             const int R = p_src[i_x * i_src_pix_pitch + 0];
1343             const int G = p_src[i_x * i_src_pix_pitch + 1];
1344             const int B = p_src[i_x * i_src_pix_pitch + 2];
1345
1346             i_trans = vlc_alpha( p_src[i_x * i_src_pix_pitch + 3], i_alpha );
1347             if( !i_trans )
1348                 continue;
1349
1350             /* Blending */
1351             vlc_blend_rgb16( (uint16_t*)&p_dst[i_x * i_pix_pitch],
1352                              R, G, B, i_trans, &p_filter->fmt_out.video );
1353         }
1354     }
1355 }
1356
1357 static void BlendRGBAYUVPacked( filter_t *p_filter,
1358                                 picture_t *p_dst_pic, const picture_t *p_src_pic,
1359                                 int i_x_offset, int i_y_offset,
1360                                 int i_width, int i_height, int i_alpha )
1361 {
1362     int i_src_pitch, i_dst_pitch, i_src_pix_pitch;
1363     uint8_t *p_dst, *p_src;
1364     int i_x, i_y, i_pix_pitch, i_trans;
1365     bool b_even = !((i_x_offset + p_filter->fmt_out.video.i_x_offset)%2);
1366     int i_l_offset, i_u_offset, i_v_offset;
1367     uint8_t y, u, v;
1368
1369     vlc_yuv_packed_index( &i_l_offset, &i_u_offset, &i_v_offset,
1370                           p_filter->fmt_out.video.i_chroma );
1371
1372     i_pix_pitch = 2;
1373     i_dst_pitch = p_dst_pic->p->i_pitch;
1374     p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
1375             p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
1376             p_dst_pic->p->i_pitch *
1377             ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
1378
1379     i_src_pix_pitch = p_src_pic->p->i_pixel_pitch;
1380     i_src_pitch = p_src_pic->p->i_pitch;
1381     p_src = p_src_pic->p->p_pixels +
1382             p_filter->fmt_in.video.i_x_offset * i_src_pitch +
1383             p_src_pic->p->i_pitch * p_filter->fmt_in.video.i_y_offset;
1384
1385     i_width &= ~1; /* Needs to be a multiple of 2 */
1386
1387     /* Draw until we reach the bottom of the subtitle */
1388     for( i_y = 0; i_y < i_height; i_y++,
1389          p_dst += i_dst_pitch,
1390          p_src += i_src_pitch )
1391     {
1392         /* Draw until we reach the end of the line */
1393         for( i_x = 0; i_x < i_width; i_x++, b_even = !b_even )
1394         {
1395             const int R = p_src[i_x * i_src_pix_pitch + 0];
1396             const int G = p_src[i_x * i_src_pix_pitch + 1];
1397             const int B = p_src[i_x * i_src_pix_pitch + 2];
1398
1399             i_trans = vlc_alpha( p_src[i_x * i_src_pix_pitch + 3], i_alpha );
1400             if( !i_trans )
1401                 continue;
1402
1403             /* Blending */
1404             rgb_to_yuv( &y, &u, &v, R, G, B );
1405
1406             vlc_blend_packed( &p_dst[i_x * 2],
1407                               i_l_offset, i_u_offset, i_v_offset,
1408                               y, u, v, i_trans, b_even );
1409         }
1410     }
1411 }