blend.c 53.7 KB
Newer Older
1 2 3
/*****************************************************************************
 * blend.c: alpha blend 2 pictures together
 *****************************************************************************
4
 * Copyright (C) 2003-2008 the VideoLAN team
5 6
 * $Id$
 *
7 8
 * Authors: Gildas Bazin <gbazin@videolan.org>
 *          Antoine Cellerier <dionoea @t videolan dot org>
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25 26 27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
28 29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include <assert.h>
33
#include <vlc_common.h>
34
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
35
#include <vlc_vout.h>
36 37
#include "vlc_filter.h"

38 39 40 41 42 43 44 45 46 47 48 49 50
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  OpenFilter ( vlc_object_t * );
static void CloseFilter( vlc_object_t * );

vlc_module_begin();
    set_description( N_("Video pictures blending") );
    set_capability( "video blending", 100 );
    set_callbacks( OpenFilter, CloseFilter );
vlc_module_end();


51 52 53 54 55
/*****************************************************************************
 * filter_sys_t : filter descriptor
 *****************************************************************************/
struct filter_sys_t
{
56
    int i_dummy;
57 58
};

59 60 61 62 63 64 65 66 67 68 69 70 71 72
#define FCC_YUVA VLC_FOURCC('Y','U','V','A')
#define FCC_YUVP VLC_FOURCC('Y','U','V','P')
#define FCC_RGBA VLC_FOURCC('R','G','B','A')

#define FCC_I420 VLC_FOURCC('I','4','2','0')
#define FCC_YV12 VLC_FOURCC('Y','V','1','2')
#define FCC_YUY2 VLC_FOURCC('Y','U','Y','2')
#define FCC_UYVY VLC_FOURCC('U','Y','V','Y')
#define FCC_YVYU VLC_FOURCC('Y','V','Y','U')
#define FCC_RV15 VLC_FOURCC('R','V','1','5')
#define FCC_RV16 VLC_FOURCC('R','V','1','6')
#define FCC_RV24 VLC_FOURCC('R','V','2','4')
#define FCC_RV32 VLC_FOURCC('R','V','3','2')

73 74 75
/****************************************************************************
 * Local prototypes
 ****************************************************************************/
76
static void Blend( filter_t *, picture_t *, picture_t *,
77
                   int, int, int );
78

79
/* YUVA */
80
static void BlendYUVAI420( filter_t *, picture_t *, picture_t *,
Laurent Aimar's avatar
Laurent Aimar committed
81
                           int, int, int, int, int );
82
static void BlendYUVARV16( filter_t *, picture_t *, picture_t *,
Laurent Aimar's avatar
Laurent Aimar committed
83
                           int, int, int, int, int );
84
static void BlendYUVARV24( filter_t *, picture_t *, picture_t *,
Laurent Aimar's avatar
Laurent Aimar committed
85
                           int, int, int, int, int );
86
static void BlendYUVAYUVPacked( filter_t *, picture_t *, picture_t *,
Laurent Aimar's avatar
Laurent Aimar committed
87
                                int, int, int, int, int );
88

89
/* I420, YV12 */
90
static void BlendI420I420( filter_t *, picture_t *, picture_t *,
91 92
                           int, int, int, int, int );
static void BlendI420I420_no_alpha(
93
                           filter_t *, picture_t *, picture_t *,
94
                           int, int, int, int );
95
static void BlendI420R16( filter_t *, picture_t *, picture_t *,
96
                           int, int, int, int, int );
97
static void BlendI420R24( filter_t *, picture_t *, picture_t *,
98
                          int, int, int, int, int );
99
static void BlendI420YUVPacked( filter_t *, picture_t *,
100 101
                                picture_t *, int, int, int, int, int );

102
/* YUVP */
103
static void BlendPalI420( filter_t *, picture_t *, picture_t *,
104
                          int, int, int, int, int );
105
static void BlendPalYUVPacked( filter_t *, picture_t *, picture_t *,
106
                               int, int, int, int, int );
107
static void BlendPalRV( filter_t *, picture_t *, picture_t *,
108
                        int, int, int, int, int );
109

110
/* RGBA */
111
static void BlendRGBAI420( filter_t *, picture_t *, picture_t *,
112
                           int, int, int, int, int );
113
static void BlendRGBAYUVPacked( filter_t *, picture_t *,
114
                                picture_t *, int, int, int, int, int );
115
static void BlendRGBAR16( filter_t *, picture_t *, picture_t *,
116
                          int, int, int, int, int );
117
static void BlendRGBAR24( filter_t *, picture_t *, picture_t *,
118 119
                          int, int, int, int, int );

120 121 122 123 124 125 126 127 128 129
/*****************************************************************************
 * OpenFilter: probe the filter and return score
 *****************************************************************************/
static int OpenFilter( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t*)p_this;
    filter_sys_t *p_sys;

    /* Check if we can handle that format.
     * We could try to use a chroma filter if we can't. */
130 131
    int in_chroma = p_filter->fmt_in.video.i_chroma;
    int out_chroma = p_filter->fmt_out.video.i_chroma;
132 133 134 135 136 137 138 139
    if( ( in_chroma  != FCC_YUVA && in_chroma  != FCC_I420 &&
          in_chroma  != FCC_YV12 && in_chroma  != FCC_YUVP &&
          in_chroma  != FCC_RGBA ) ||
        ( out_chroma != FCC_I420 && out_chroma != FCC_YUY2 &&
          out_chroma != FCC_YV12 && out_chroma != FCC_UYVY &&
          out_chroma != FCC_YVYU && out_chroma != FCC_RV15 &&
          out_chroma != FCC_YVYU && out_chroma != FCC_RV16 &&
          out_chroma != FCC_RV24 && out_chroma != FCC_RV32 ) )
140 141 142 143 144
    {
        return VLC_EGENERIC;
    }

    /* Allocate the memory needed to store the decoder's structure */
145 146
    p_filter->p_sys = p_sys = malloc(sizeof(filter_sys_t));
    if( !p_sys )
Rémi Duraffort's avatar
Rémi Duraffort committed
147
        return VLC_ENOMEM;
148 149 150 151 152 153 154 155 156 157 158

    /* Misc init */
    p_filter->pf_video_blend = Blend;

    msg_Dbg( p_filter, "chroma: %4.4s -> %4.4s",
             (char *)&p_filter->fmt_in.video.i_chroma,
             (char *)&p_filter->fmt_out.video.i_chroma );

    return VLC_SUCCESS;
}

159 160 161 162 163 164 165 166 167 168 169
/*****************************************************************************
 * CloseFilter: clean up the filter
 *****************************************************************************/
static void CloseFilter( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t*)p_this;
    filter_sys_t *p_sys = p_filter->p_sys;

    free( p_sys );
}

170 171 172 173 174
/****************************************************************************
 * Blend: the whole thing
 ****************************************************************************
 * This function is called just after the thread is launched.
 ****************************************************************************/
175
typedef void (*BlendFunction)( filter_t *,
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
                       picture_t *, picture_t *,
                       int , int , int , int , int );

#define FCC_PLANAR_420 { FCC_I420, FCC_YV12, 0 }
#define FCC_PACKED_422 { FCC_YUY2, FCC_UYVY, FCC_YVYU, 0 }
#define FCC_RGB_16 { FCC_RV15, FCC_RV16, 0 }
#define FCC_RGB_24 { FCC_RV24, FCC_RV32, 0 }

#define BLEND_CFG( fccSrc, fctPlanar, fctPacked, fctRgb16, fctRgb24  ) \
    { .src = fccSrc, .p_dst = FCC_PLANAR_420, .pf_blend = fctPlanar }, \
    { .src = fccSrc, .p_dst = FCC_PACKED_422, .pf_blend = fctPacked }, \
    { .src = fccSrc, .p_dst = FCC_RGB_16,     .pf_blend = fctRgb16  }, \
    { .src = fccSrc, .p_dst = FCC_RGB_24,     .pf_blend = fctRgb24  }

static const struct
{
    vlc_fourcc_t src;
    vlc_fourcc_t p_dst[16];
    BlendFunction pf_blend;
} p_blend_cfg[] = {

Laurent Aimar's avatar
Laurent Aimar committed
197
    BLEND_CFG( FCC_YUVA, BlendYUVAI420, BlendYUVAYUVPacked, BlendYUVARV16, BlendYUVARV24 ),
198 199 200 201 202 203 204 205 206 207 208 209

    BLEND_CFG( FCC_YUVP, BlendPalI420, BlendPalYUVPacked, BlendPalRV, BlendPalRV ),

    BLEND_CFG( FCC_RGBA, BlendRGBAI420, BlendRGBAYUVPacked, BlendRGBAR16, BlendRGBAR24 ),

    BLEND_CFG( FCC_I420, BlendI420I420, BlendI420YUVPacked, BlendI420R16, BlendI420R24 ),

    BLEND_CFG( FCC_YV12, BlendI420I420, BlendI420YUVPacked, BlendI420R16, BlendI420R24 ),

    { 0, {0,}, NULL }
};

210 211
static void Blend( filter_t *p_filter,
                   picture_t *p_dst, picture_t *p_src,
212
                   int i_x_offset, int i_y_offset, int i_alpha )
213
{
214 215
    int i_width, i_height;

216 217
    if( i_alpha == 0 )
        return;
218

219 220
    i_width = __MIN((int)p_filter->fmt_out.video.i_visible_width - i_x_offset,
                    (int)p_filter->fmt_in.video.i_visible_width);
221

222 223
    i_height = __MIN((int)p_filter->fmt_out.video.i_visible_height -i_y_offset,
                     (int)p_filter->fmt_in.video.i_visible_height);
224

225 226 227 228 229
    if( i_width <= 0 || i_height <= 0 )
        return;

    video_format_FixRgb( &p_filter->fmt_out.video );
    video_format_FixRgb( &p_filter->fmt_in.video );
230

231
#if 0
Jean-Paul Saman's avatar
Jean-Paul Saman committed
232
    msg_Dbg( p_filter, "chroma: %4.4s -> %4.4s\n",
233 234 235 236
             (char *)&p_filter->fmt_in.video.i_chroma,
             (char *)&p_filter->fmt_out.video.i_chroma );
#endif

237
    for( int i = 0; p_blend_cfg[i].src != 0; i++ )
238
    {
239 240 241 242 243 244 245
        if( p_blend_cfg[i].src != p_filter->fmt_in.video.i_chroma )
            continue;
        for( int j = 0; p_blend_cfg[i].p_dst[j] != 0; j++ )
        {
            if( p_blend_cfg[i].p_dst[j] != p_filter->fmt_out.video.i_chroma )
                continue;

246 247 248
            p_blend_cfg[i].pf_blend( p_filter, p_dst, p_src,
                                     i_x_offset, i_y_offset,
                                     i_width, i_height, i_alpha );
249 250
            return;
        }
251
    }
252

253 254 255 256
    msg_Dbg( p_filter, "no matching alpha blending routine "
             "(chroma: %4.4s -> %4.4s)",
             (char *)&p_filter->fmt_in.video.i_chroma,
             (char *)&p_filter->fmt_out.video.i_chroma );
257 258
}

259 260 261
/***********************************************************************
 * Utils
 ***********************************************************************/
262 263 264 265 266 267 268 269 270
static inline uint8_t vlc_uint8( int v )
{
    if( v > 255 )
        return 255;
    else if( v < 0 )
        return 0;
    return v;
}

271 272 273 274 275 276 277 278 279 280 281 282 283
#define MAX_TRANS 255
#define TRANS_BITS  8

static inline int vlc_blend( int v1, int v2, int a )
{
    /* TODO bench if the tests really increase speed */
    if( a == 0 )
        return v2;
    else if( a == MAX_TRANS )
        return v1;
    return ( v1 * a + v2 * (MAX_TRANS - a ) ) >> TRANS_BITS;
}

Laurent Aimar's avatar
Laurent Aimar committed
284 285
static inline int vlc_alpha( int t, int a )
{
286 287
    if( a == 255 )
        return t;
Laurent Aimar's avatar
Laurent Aimar committed
288 289 290
    return (t * a) / 255;
}

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
static inline void yuv_to_rgb( int *r, int *g, int *b,
                               uint8_t y1, uint8_t u1, uint8_t v1 )
{
    /* macros used for YUV pixel conversions */
#   define SCALEBITS 10
#   define ONE_HALF  (1 << (SCALEBITS - 1))
#   define FIX(x)    ((int) ((x) * (1<<SCALEBITS) + 0.5))

    int y, cb, cr, r_add, g_add, b_add;

    cb = u1 - 128;
    cr = v1 - 128;
    r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;
    g_add = - FIX(0.34414*255.0/224.0) * cb
            - FIX(0.71414*255.0/224.0) * cr + ONE_HALF;
    b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;
    y = (y1 - 16) * FIX(255.0/219.0);
308 309 310 311 312 313
    *r = vlc_uint8( (y + r_add) >> SCALEBITS );
    *g = vlc_uint8( (y + g_add) >> SCALEBITS );
    *b = vlc_uint8( (y + b_add) >> SCALEBITS );
#undef FIX
#undef ONE_HALF
#undef SCALEBITS
314 315 316 317 318 319 320 321 322 323
}

static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
                               int r, int g, int b )
{
    *y = ( ( (  66 * r + 129 * g +  25 * b + 128 ) >> 8 ) + 16 );
    *u =   ( ( -38 * r -  74 * g + 112 * b + 128 ) >> 8 ) + 128 ;
    *v =   ( ( 112 * r -  94 * g -  18 * b + 128 ) >> 8 ) + 128 ;
}

Laurent Aimar's avatar
Laurent Aimar committed
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
static uint8_t *vlc_plane_start( int *pi_pitch,
                                 picture_t *p_picture,
                                 int i_plane,
                                 int i_x_offset, int i_y_offset,
                                 const video_format_t *p_fmt,
                                 int r )
{
    const int i_pitch = p_picture->p[i_plane].i_pitch;
    uint8_t *p_pixels = p_picture->p[i_plane].p_pixels;

    const int i_dx = ( i_x_offset + p_fmt->i_x_offset ) / r;
    const int i_dy = ( i_y_offset + p_fmt->i_y_offset ) / r;

    if( pi_pitch )
        *pi_pitch = i_pitch;
    return &p_pixels[ i_dy * i_pitch + i_dx ];
}

342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
static void vlc_yuv_packed_index( int *pi_y, int *pi_u, int *pi_v, vlc_fourcc_t i_chroma )
{
    static const struct {
        vlc_fourcc_t chroma;
        int y, u ,v;
    } p_index[] = {
        { FCC_YUY2, 0, 1, 3 },
        { FCC_UYVY, 1, 0, 2 },
        { FCC_YVYU, 0, 3, 1 },
        { 0, 0, 0, 0 }
    };
    int i;

    for( i = 0; p_index[i].chroma != 0; i++ )
    {
        if( p_index[i].chroma == i_chroma )
            break;
    }
    *pi_y = p_index[i].y;
    *pi_u = p_index[i].u;
    *pi_v = p_index[i].v;
}

static void vlc_blend_packed( uint8_t *p_dst, const uint8_t *p_src,
                              int i_offset0, int i_offset1, int i_offset2,
                              int c0, int c1, int c2, int i_alpha,
                              bool b_do12 )
{
    p_dst[i_offset0] = vlc_blend( c0, p_src[i_offset0], i_alpha );
    if( b_do12 )
    {
        p_dst[i_offset1] = vlc_blend( c1, p_src[i_offset1], i_alpha );
        p_dst[i_offset2] = vlc_blend( c2, p_src[i_offset2], i_alpha );
    }
}

static void vlc_blend_rgb16( uint16_t *p_dst, const uint16_t *p_src,
                             int R, int G, int B, int i_alpha,
                             const video_format_t *p_fmt )
{
    const int i_pix = *p_src;
    const int r = ( i_pix & p_fmt->i_rmask ) >> p_fmt->i_lrshift;
    const int g = ( i_pix & p_fmt->i_gmask ) >> p_fmt->i_lgshift;
    const int b = ( i_pix & p_fmt->i_bmask ) >> p_fmt->i_lbshift;

    *p_dst = ( vlc_blend( R >> p_fmt->i_rrshift, r, i_alpha ) << p_fmt->i_lrshift ) |
             ( vlc_blend( G >> p_fmt->i_rgshift, g, i_alpha ) << p_fmt->i_lgshift ) |
             ( vlc_blend( B >> p_fmt->i_rbshift, b, i_alpha ) << p_fmt->i_lbshift );
}

static void vlc_rgb_index( int *pi_rindex, int *pi_gindex, int *pi_bindex,
                           const video_format_t *p_fmt )
{
    if( p_fmt->i_chroma != FCC_RV24 && p_fmt->i_chroma != FCC_RV32 )
        return;

    /* XXX it will works only if mask are 8 bits aligned */
#ifdef WORDS_BIGENDIAN
    const int i_mask_bits = p_fmt->i_chroma == FCC_RV24 ? 24 : 32;
    *pi_rindex = ( i_mask_bits - p_fmt->i_lrshift ) / 8;
    *pi_gindex = ( i_mask_bits - p_fmt->i_lgshift ) / 8;
    *pi_bindex = ( i_mask_bits - p_fmt->i_lbshift ) / 8;
#else
    *pi_rindex = p_fmt->i_lrshift / 8;
    *pi_gindex = p_fmt->i_lgshift / 8;
    *pi_bindex = p_fmt->i_lbshift / 8;
#endif
}

411 412 413
/***********************************************************************
 * YUVA
 ***********************************************************************/
414 415
static void BlendYUVAI420( filter_t *p_filter,
                           picture_t *p_dst, picture_t *p_src,
Laurent Aimar's avatar
Laurent Aimar committed
416 417
                           int i_x_offset, int i_y_offset,
                           int i_width, int i_height, int i_alpha )
418
{
419 420 421 422
    int i_src_pitch, i_dst_pitch;
    uint8_t *p_src_y, *p_dst_y;
    uint8_t *p_src_u, *p_dst_u;
    uint8_t *p_src_v, *p_dst_v;
423
    uint8_t *p_trans;
424
    int i_x, i_y, i_trans = 0;
425
    bool b_even_scanline = i_y_offset % 2;
426

Laurent Aimar's avatar
Laurent Aimar committed
427 428 429 430 431 432 433
    p_dst_y = vlc_plane_start( &i_dst_pitch, p_dst, Y_PLANE,
                               i_x_offset, i_y_offset, &p_filter->fmt_out.video, 1 );
    p_dst_u = vlc_plane_start( NULL, p_dst, U_PLANE,
                               i_x_offset, i_y_offset, &p_filter->fmt_out.video, 2 );
    p_dst_v = vlc_plane_start( NULL, p_dst, V_PLANE,
                               i_x_offset, i_y_offset, &p_filter->fmt_out.video, 2 );

434 435 436 437 438 439
    p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
                               0, 0, &p_filter->fmt_in.video, 1 );
    p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
    p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
Laurent Aimar's avatar
Laurent Aimar committed
440 441
    p_trans = vlc_plane_start( NULL, p_src, A_PLANE,
                               0, 0, &p_filter->fmt_in.video, 1 );
442 443

    /* Draw until we reach the bottom of the subtitle */
444 445
    for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src_pitch,
         p_dst_y += i_dst_pitch, p_src_y += i_src_pitch,
446
         p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
447
         p_src_u += i_src_pitch,
448
         p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
449
         p_src_v += i_src_pitch )
450 451 452 453 454 455
    {
        b_even_scanline = !b_even_scanline;

        /* Draw until we reach the end of the line */
        for( i_x = 0; i_x < i_width; i_x++ )
        {
456
            if( p_trans )
Laurent Aimar's avatar
Laurent Aimar committed
457
                i_trans = vlc_alpha( p_trans[i_x], i_alpha );
458

459
            if( !i_trans )
460 461 462
                continue;

            /* Blending */
463
            p_dst_y[i_x] = vlc_blend( p_src_y[i_x], p_dst_y[i_x], i_trans );
464 465
            if( b_even_scanline && i_x % 2 == 0 )
            {
466 467
                p_dst_u[i_x/2] = vlc_blend( p_src_u[i_x], p_dst_u[i_x/2], i_trans );
                p_dst_v[i_x/2] = vlc_blend( p_src_v[i_x], p_dst_v[i_x/2], i_trans );
468 469 470 471 472
            }
        }
    }
}

473 474
static void BlendYUVARV16( filter_t *p_filter,
                           picture_t *p_dst_pic, picture_t *p_src,
Laurent Aimar's avatar
Laurent Aimar committed
475 476
                           int i_x_offset, int i_y_offset,
                           int i_width, int i_height, int i_alpha )
477
{
478 479 480
    int i_src_pitch, i_dst_pitch;
    uint8_t *p_dst, *p_src_y;
    uint8_t *p_src_u, *p_src_v;
481
    uint8_t *p_trans;
482
    int i_x, i_y, i_pix_pitch, i_trans = 0;
483 484 485 486
    int r, g, b;

    i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
    i_dst_pitch = p_dst_pic->p->i_pitch;
487 488
    p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
            p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
489 490 491
            p_dst_pic->p->i_pitch *
            ( i_y_offset + p_filter->fmt_out.video.i_y_offset );

492 493 494 495 496 497
    p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
                               0, 0, &p_filter->fmt_in.video, 1 );
    p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
    p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
Laurent Aimar's avatar
Laurent Aimar committed
498 499
    p_trans = vlc_plane_start( NULL, p_src, A_PLANE,
                               0, 0, &p_filter->fmt_in.video, 1 );
500 501

    /* Draw until we reach the bottom of the subtitle */
502 503 504 505
    for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src_pitch,
         p_dst += i_dst_pitch,
         p_src_y += i_src_pitch, p_src_u += i_src_pitch,
         p_src_v += i_src_pitch )
506 507 508 509
    {
        /* Draw until we reach the end of the line */
        for( i_x = 0; i_x < i_width; i_x++ )
        {
510
            if( p_trans )
Laurent Aimar's avatar
Laurent Aimar committed
511
                i_trans = vlc_alpha( p_trans[i_x], i_alpha );
512
            if( !i_trans )
513 514 515 516
                continue;

            /* Blending */
            yuv_to_rgb( &r, &g, &b,
517
                        p_src_y[i_x], p_src_u[i_x], p_src_v[i_x] );
518

519
            vlc_blend_rgb16( (uint16_t*)&p_dst[i_x * i_pix_pitch],
520
                             (const uint16_t*)&p_dst[i_x * i_pix_pitch],
521
                             r, g, b, i_trans, &p_filter->fmt_out.video );
522 523 524 525
        }
    }
}

526 527
static void BlendYUVARV24( filter_t *p_filter,
                           picture_t *p_dst_pic, picture_t *p_src,
Laurent Aimar's avatar
Laurent Aimar committed
528 529
                           int i_x_offset, int i_y_offset,
                           int i_width, int i_height, int i_alpha )
530
{
531 532 533
    int i_src_pitch, i_dst_pitch;
    uint8_t *p_dst, *p_src_y;
    uint8_t *p_src_u, *p_src_v;
534
    uint8_t *p_trans;
535
    int i_x, i_y, i_pix_pitch, i_trans = 0;
536 537 538 539 540 541 542 543 544
    int r, g, b;

    i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
    i_dst_pitch = p_dst_pic->p->i_pitch;
    p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
            p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
            p_dst_pic->p->i_pitch *
            ( i_y_offset + p_filter->fmt_out.video.i_y_offset );

545 546 547 548 549 550
    p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
                               0, 0, &p_filter->fmt_in.video, 1 );
    p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
    p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
Laurent Aimar's avatar
Laurent Aimar committed
551 552
    p_trans = vlc_plane_start( NULL, p_src, A_PLANE,
                               0, 0, &p_filter->fmt_in.video, 1 );
553

554
    if( (i_pix_pitch == 4)
555
     && (((((intptr_t)p_dst)|i_dst_pitch) /* FIXME? */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
556
          & 3) == 0) )
557
    {
558
        /*
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
559 560
        ** if picture pixels are 32 bits long and lines addresses are 32 bit
        ** aligned, optimize rendering
561 562 563 564 565 566 567
        */
        uint32_t *p32_dst = (uint32_t *)p_dst;
        uint32_t i32_dst_pitch = (uint32_t)(i_dst_pitch>>2);

        int i_rshift, i_gshift, i_bshift;
        uint32_t i_rmask, i_gmask, i_bmask;

568 569 570 571 572 573
        i_rmask = p_filter->fmt_out.video.i_rmask;
        i_gmask = p_filter->fmt_out.video.i_gmask;
        i_bmask = p_filter->fmt_out.video.i_bmask;
        i_rshift = p_filter->fmt_out.video.i_lrshift;
        i_gshift = p_filter->fmt_out.video.i_lgshift;
        i_bshift = p_filter->fmt_out.video.i_lbshift;
574 575

        /* Draw until we reach the bottom of the subtitle */
576 577 578 579
        for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src_pitch,
             p32_dst += i32_dst_pitch,
             p_src_y += i_src_pitch, p_src_u += i_src_pitch,
             p_src_v += i_src_pitch )
580 581 582 583 584
        {
            /* Draw until we reach the end of the line */
            for( i_x = 0; i_x < i_width; i_x++ )
            {
                if( p_trans )
Laurent Aimar's avatar
Laurent Aimar committed
585
                    i_trans = vlc_alpha( p_trans[i_x], i_alpha );
586 587
                if( !i_trans )
                    continue;
588 589

                if( i_trans == MAX_TRANS )
590 591 592
                {
                    /* Completely opaque. Completely overwrite underlying pixel */
                    yuv_to_rgb( &r, &g, &b,
593
                                p_src_y[i_x], p_src_u[i_x], p_src_v[i_x] );
594

595 596 597
                    p32_dst[i_x] = (r<<i_rshift) |
                                   (g<<i_gshift) |
                                   (b<<i_bshift);
598 599 600 601
                }
                else
                {
                    /* Blending */
602
                    uint32_t i_pix_dst = p32_dst[i_x];
603
                    yuv_to_rgb( &r, &g, &b,
604
                                p_src_y[i_x], p_src_u[i_x], p_src_v[i_x] );
605

606 607 608
                    p32_dst[i_x] = ( vlc_blend( r, (i_pix_dst & i_rmask)>>i_rshift, i_trans ) << i_rshift ) |
                                   ( vlc_blend( g, (i_pix_dst & i_gmask)>>i_gshift, i_trans ) << i_gshift ) |
                                   ( vlc_blend( b, (i_pix_dst & i_bmask)>>i_bshift, i_trans ) << i_bshift );
609 610 611 612 613 614
                }
            }
        }
    }
    else
    {
615
        int i_rindex, i_gindex, i_bindex;
616 617
        uint32_t i_rmask, i_gmask, i_bmask;

618 619 620
        i_rmask = p_filter->fmt_out.video.i_rmask;
        i_gmask = p_filter->fmt_out.video.i_gmask;
        i_bmask = p_filter->fmt_out.video.i_bmask;
621

622
        vlc_rgb_index( &i_rindex, &i_gindex, &i_bindex, &p_filter->fmt_out.video );
623

624
        /* Draw until we reach the bottom of the subtitle */
625 626 627 628
        for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src_pitch,
             p_dst += i_dst_pitch,
             p_src_y += i_src_pitch, p_src_u += i_src_pitch,
             p_src_v += i_src_pitch )
629 630 631 632 633
        {
            /* Draw until we reach the end of the line */
            for( i_x = 0; i_x < i_width; i_x++ )
            {
                if( p_trans )
Laurent Aimar's avatar
Laurent Aimar committed
634
                    i_trans = vlc_alpha( p_trans[i_x], i_alpha );
635 636
                if( !i_trans )
                    continue;
637

638 639
                /* Blending */
                yuv_to_rgb( &r, &g, &b,
640
                            p_src_y[i_x], p_src_u[i_x], p_src_v[i_x] );
641

642
                vlc_blend_packed( &p_dst[ i_x * i_pix_pitch],
643
                                  &p_dst[i_x * i_pix_pitch],
644 645
                                  i_rindex, i_gindex, i_bindex,
                                  r, g, b, i_alpha, true );
646
            }
647 648 649 650
        }
    }
}

651 652
static void BlendYUVAYUVPacked( filter_t *p_filter,
                                picture_t *p_dst_pic, picture_t *p_src,
Laurent Aimar's avatar
Laurent Aimar committed
653 654
                                int i_x_offset, int i_y_offset,
                                int i_width, int i_height, int i_alpha )
655
{
656 657 658
    int i_src_pitch, i_dst_pitch;
    uint8_t *p_dst, *p_src_y;
    uint8_t *p_src_u, *p_src_v;
659
    uint8_t *p_trans;
660
    int i_x, i_y, i_pix_pitch, i_trans = 0;
661
    bool b_even = !((i_x_offset + p_filter->fmt_out.video.i_x_offset)%2);
662
    int i_l_offset, i_u_offset, i_v_offset;
663

664 665
    vlc_yuv_packed_index( &i_l_offset, &i_u_offset, &i_v_offset,
                          p_filter->fmt_out.video.i_chroma );
666 667 668 669 670 671 672 673

    i_pix_pitch = 2;
    i_dst_pitch = p_dst_pic->p->i_pitch;
    p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
            p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
            p_dst_pic->p->i_pitch *
            ( i_y_offset + p_filter->fmt_out.video.i_y_offset );

674 675 676 677 678 679
    p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
                               0, 0, &p_filter->fmt_in.video, 1 );
    p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
    p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
Laurent Aimar's avatar
Laurent Aimar committed
680 681
    p_trans = vlc_plane_start( NULL, p_src, A_PLANE,
                               0, 0, &p_filter->fmt_in.video, 1 );
682

Laurent Aimar's avatar
Laurent Aimar committed
683
    i_width &= ~1; /* Needs to be a multiple of 2 */
684

685
    /* Draw until we reach the bottom of the subtitle */
686 687 688 689
    for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src_pitch,
         p_dst += i_dst_pitch,
         p_src_y += i_src_pitch, p_src_u += i_src_pitch,
         p_src_v += i_src_pitch )
690 691
    {
        /* Draw until we reach the end of the line */
692
        for( i_x = 0; i_x < i_width; i_x++, b_even = !b_even )
693
        {
Laurent Aimar's avatar
Laurent Aimar committed
694
            i_trans = vlc_alpha( p_trans[i_x], i_alpha );
695
            if( !i_trans )
696
                continue;
697

698 699 700 701 702 703 704
            /* Blending */
            if( b_even )
            {
                int i_u;
                int i_v;
                /* FIXME what's with 0xaa ? */
                if( p_trans[i_x+1] > 0xaa )
705
                {
706 707
                    i_u = (p_src_u[i_x]+p_src_u[i_x+1])>>1;
                    i_v = (p_src_v[i_x]+p_src_v[i_x+1])>>1;
708
                }
709
                else
710
                {
711 712
                    i_u = p_src_u[i_x];
                    i_v = p_src_v[i_x];
713
                }
Laurent Aimar's avatar
Laurent Aimar committed
714

715
                vlc_blend_packed( &p_dst[i_x * 2], &p_dst[i_x * 2],
716
                                  i_l_offset, i_u_offset, i_v_offset,
717
                                  p_src_y[i_x], i_u, i_v, i_trans, true );
718 719 720
            }
            else
            {
721
                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 );
722
            }
723 724
        }
    }
725 726 727 728
}
/***********************************************************************
 * I420, YV12
 ***********************************************************************/
729 730
static void BlendI420I420( filter_t *p_filter,
                           picture_t *p_dst, picture_t *p_src,
731 732 733
                           int i_x_offset, int i_y_offset,
                           int i_width, int i_height, int i_alpha )
{
734 735 736 737
    int i_src_pitch, i_dst_pitch;
    uint8_t *p_src_y, *p_dst_y;
    uint8_t *p_src_u, *p_dst_u;
    uint8_t *p_src_v, *p_dst_v;
738
    int i_x, i_y;
739
    bool b_even_scanline = i_y_offset % 2;
740

741 742
    if( i_alpha == 0xff )
    {
743
        BlendI420I420_no_alpha( p_filter, p_dst, p_src,
744 745 746 747 748
                                i_x_offset, i_y_offset, i_width, i_height );
        return;
    }


749 750 751 752 753 754 755 756 757 758 759 760 761 762
    i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
    p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
              p_filter->fmt_out.video.i_x_offset +
              p_dst->p[Y_PLANE].i_pitch *
              ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
    p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
              p_filter->fmt_out.video.i_x_offset/2 +
              ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
              p_dst->p[U_PLANE].i_pitch;
    p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
              p_filter->fmt_out.video.i_x_offset/2 +
              ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
              p_dst->p[V_PLANE].i_pitch;

763 764 765 766 767 768
    p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
                               0, 0, &p_filter->fmt_in.video, 1 );
    p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
    p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
Laurent Aimar's avatar
Laurent Aimar committed
769
    i_width &= ~1;
770 771 772

    /* Draw until we reach the bottom of the subtitle */
    for( i_y = 0; i_y < i_height; i_y++,
773 774
         p_dst_y += i_dst_pitch,
         p_src_y += i_src_pitch )
775 776 777 778 779 780 781 782 783 784 785
    {
        if( b_even_scanline )
        {
            p_dst_u  += i_dst_pitch/2;
            p_dst_v  += i_dst_pitch/2;
        }
        b_even_scanline = !b_even_scanline;

        /* Draw until we reach the end of the line */
        for( i_x = 0; i_x < i_width; i_x++ )
        {
786
            if( !i_alpha )
787 788 789
                continue;

            /* Blending */
790
            p_dst_y[i_x] = vlc_blend( p_src_y[i_x], p_dst_y[i_x], i_alpha );
791 792
            if( b_even_scanline && i_x % 2 == 0 )
            {
793 794
                p_dst_u[i_x/2] = vlc_blend( p_src_u[i_x/2], p_dst_u[i_x/2], i_alpha );
                p_dst_v[i_x/2] = vlc_blend( p_src_v[i_x/2], p_dst_v[i_x/2], i_alpha );
795 796 797 798
            }
        }
        if( i_y%2 == 1 )
        {
799 800
            p_src_u += i_src_pitch/2;
            p_src_v += i_src_pitch/2;
801 802 803
        }
    }
}
804 805
static void BlendI420I420_no_alpha( filter_t *p_filter,
                                    picture_t *p_dst, picture_t *p_src,
806 807 808
                                    int i_x_offset, int i_y_offset,
                                    int i_width, int i_height )
{
809 810 811 812
    int i_src_pitch, i_dst_pitch;
    uint8_t *p_src_y, *p_dst_y;
    uint8_t *p_src_u, *p_dst_u;
    uint8_t *p_src_v, *p_dst_v;
813
    int i_y;
814
    bool b_even_scanline = i_y_offset % 2;
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829

    i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
    p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
              p_filter->fmt_out.video.i_x_offset +
              p_dst->p[Y_PLANE].i_pitch *
              ( i_y_offset + p_filter->fmt_out.video.i_y_offset );
    p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
              p_filter->fmt_out.video.i_x_offset/2 +
              ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
              p_dst->p[U_PLANE].i_pitch;
    p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
              p_filter->fmt_out.video.i_x_offset/2 +
              ( i_y_offset + p_filter->fmt_out.video.i_y_offset ) / 2 *
              p_dst->p[V_PLANE].i_pitch;

830 831 832 833 834 835
    p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
                               0, 0, &p_filter->fmt_in.video, 1 );
    p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
    p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
836 837 838 839

    i_width &= ~1;

    /* Draw until we reach the bottom of the subtitle */
Laurent Aimar's avatar
Laurent Aimar committed
840
    for( i_y = 0; i_y < i_height;
841
            i_y++, p_dst_y += i_dst_pitch, p_src_y += i_src_pitch )
842 843
    {
        /* Completely opaque. Completely overwrite underlying pixel */
844
        vlc_memcpy( p_dst_y, p_src_y, i_width );
845 846 847 848 849 850 851
        if( b_even_scanline )
        {
            p_dst_u  += i_dst_pitch/2;
            p_dst_v  += i_dst_pitch/2;
        }
        else
        {
852 853
            vlc_memcpy( p_dst_u, p_src_u, i_width/2 );
            vlc_memcpy( p_dst_v, p_src_v, i_width/2 );
854 855 856 857
        }
        b_even_scanline = !b_even_scanline;
        if( i_y%2 == 1 )
        {
858 859
            p_src_u += i_src_pitch/2;
            p_src_v += i_src_pitch/2;
860 861 862 863
        }
    }
}

864 865
static void BlendI420R16( filter_t *p_filter,
                          picture_t *p_dst_pic, picture_t *p_src,
866 867 868
                          int i_x_offset, int i_y_offset,
                          int i_width, int i_height, int i_alpha )
{
869 870 871
    int i_src_pitch, i_dst_pitch;
    uint8_t *p_dst, *p_src_y;
    uint8_t *p_src_u, *p_src_v;
872 873 874 875 876 877 878 879 880 881
    int i_x, i_y, i_pix_pitch;
    int r, g, b;

    i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
    i_dst_pitch = p_dst_pic->p->i_pitch;
    p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
            p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
            p_dst_pic->p->i_pitch *
            ( i_y_offset + p_filter->fmt_out.video.i_y_offset );

882 883 884 885 886
    p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
                               0, 0, &p_filter->fmt_in.video, 1 );
    p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
    p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
Laurent Aimar's avatar
Laurent Aimar committed
887
                                0, 0, &p_filter->fmt_in.video, 2 );
888 889 890

    /* Draw until we reach the bottom of the subtitle */
    for( i_y = 0; i_y < i_height; i_y++,
891 892
         p_dst += i_dst_pitch,
         p_src_y += i_src_pitch )
893 894 895 896 897 898
    {
        /* Draw until we reach the end of the line */
        for( i_x = 0; i_x < i_width; i_x++ )
        {
            /* Blending */
            yuv_to_rgb( &r, &g, &b,
899
                        p_src_y[i_x], p_src_u[i_x/2], p_src_v[i_x/2] );
900

901
            vlc_blend_rgb16( (uint16_t*)&p_dst[i_x * i_pix_pitch],
902
                             (const uint16_t*)&p_dst[i_x * i_pix_pitch],
903
                             r, g, b, i_alpha, &p_filter->fmt_out.video );
904 905 906
        }
        if( i_y%2 == 1 )
        {
907 908
            p_src_u += i_src_pitch/2;
            p_src_v += i_src_pitch/2;
909 910 911 912
        }
    }
}

913 914
static void BlendI420R24( filter_t *p_filter,
                          picture_t *p_dst_pic, picture_t *p_src,
915 916 917
                          int i_x_offset, int i_y_offset,
                          int i_width, int i_height, int i_alpha )
{
918 919 920
    int i_src_pitch, i_dst_pitch;
    uint8_t *p_dst, *p_src_y;
    uint8_t *p_src_u, *p_src_v;
921
    int i_x, i_y, i_pix_pitch;
922
    int i_rindex, i_gindex, i_bindex;
923 924 925 926 927 928 929 930 931
    int r, g, b;

    i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
    i_dst_pitch = p_dst_pic->p->i_pitch;
    p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
            p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
            p_dst_pic->p->i_pitch *
            ( i_y_offset + p_filter->fmt_out.video.i_y_offset );

932 933 934 935 936 937
    p_src_y = vlc_plane_start( &i_src_pitch, p_src, Y_PLANE,
                               0, 0, &p_filter->fmt_in.video, 1 );
    p_src_u = vlc_plane_start( NULL, p_src, U_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
    p_src_v = vlc_plane_start( NULL, p_src, V_PLANE,
                               0, 0, &p_filter->fmt_in.video, 2 );
Laurent Aimar's avatar
Laurent Aimar committed
938

939
    vlc_rgb_index( &i_rindex, &i_gindex, &i_bindex, &p_filter->fmt_out.video );
940 941 942

    /* Draw until we reach the bottom of the subtitle */
    for( i_y = 0; i_y < i_height; i_y++,
943 944 945
         p_dst += i_dst_pitch,
         p_src_y += i_src_pitch, p_src_u += i_src_pitch,
         p_src_v += i_src_pitch )
946 947 948 949
    {
        /* Draw until we reach the end of the line */
        for( i_x = 0; i_x < i_width; i_x++ )
        {
950
            if( !i_alpha )
951 952 953 954
                continue;

            /* Blending */
            yuv_to_rgb( &r, &g, &b,
955
                        p_src_y[i_x], p_src_u[i_x/2], p_src_v[i_x/2] );
956

957
            vlc_blend_packed( &p_dst[i_x * i_pix_pitch], &p_dst[i_x * i_pix_pitch],
958
                              i_rindex, i_gindex, i_bindex, r, g, b, i_alpha, true );
959 960 961
        }
        if( i_y%2 == 1 )
        {
962 963
            p_src_u += i_src_pitch/2;
            p_src_v += i_src_pitch/2;
964 965 966 967
        }
    }
}

968 969
static void BlendI420YUVPacked( filter_t *p_filter,
                                picture_t *p_dst_pic, picture_t *p_src,
970 971 972
                                int i_x_offset, int i_y_offset,
                                int i_width, int i_height, int i_alpha )
{
973 974 975
    int i_src_pitch, i_dst_pitch;
    uint8_t *p_dst, *p_src_y;
    uint8_t *p_src_u, *p_src_v;
976
    int i_x, i_y, i_pix_pitch;
977
    bool b_even = !((i_x_offset + p_filter->fmt_out.video.i_x_offset)%2);
978
    int i_l_offset, i_u_offset, i_v_offset;
979

980 981
    vlc_yuv_packed_index( &i_l_offset, &i_u_offset, &i_v_offset,
                          p_filter->fmt_out.video.i_chroma );
982 983 984 985 986 987 988 989

    i_pix_pitch = 2;
    i_dst_pitch = p_dst_pic->p->i_pitch;
    p_dst = p_dst_pic->p->p_pixels + i_x_offset * i_pix_pitch +
            p_filter->fmt_out.video.i_x_offset * i_pix_pitch +
            p_dst_pic->p->i_pitch *