vout_subpictures.c 51.7 KB
Newer Older
Sam Hocevar's avatar
 
Sam Hocevar committed
1 2 3
/*****************************************************************************
 * vout_subpictures.c : subpicture management functions
 *****************************************************************************
4
 * Copyright (C) 2000-2007 the VideoLAN team
5
 * $Id$
Sam Hocevar's avatar
 
Sam Hocevar committed
6 7 8
 *
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
 *          Samuel Hocevar <sam@zoy.org>
9
 *          Gildas Bazin <gbazin@videolan.org>
Sam Hocevar's avatar
 
Sam Hocevar committed
10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * 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
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Sam Hocevar's avatar
 
Sam Hocevar committed
24 25 26 27 28
 *****************************************************************************/

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

33
#include <vlc_common.h>
Clément Stenac's avatar
Clément Stenac committed
34 35 36 37
#include <vlc_vout.h>
#include <vlc_block.h>
#include <vlc_filter.h>
#include <vlc_osd.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
38
#include "../libvlc.h"
39

40 41
#include <assert.h>

42 43 44 45 46 47
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static void UpdateSPU   ( spu_t *, vlc_object_t * );
static int  CropCallback( vlc_object_t *, char const *,
                          vlc_value_t, vlc_value_t, void * );
48

49 50
static int spu_vaControlDefault( spu_t *, int, va_list );

51 52
static subpicture_t *sub_new_buffer( filter_t * );
static void sub_del_buffer( filter_t *, subpicture_t * );
53 54
static subpicture_t *spu_new_buffer( filter_t * );
static void spu_del_buffer( filter_t *, subpicture_t * );
55 56
static picture_t *spu_new_video_buffer( filter_t * );
static void spu_del_video_buffer( filter_t *, picture_t * );
57

58 59 60 61
static int spu_ParseChain( spu_t * );
static int SubFilterCallback( vlc_object_t *, char const *,
                              vlc_value_t, vlc_value_t, void * );

62 63
static int sub_filter_allocation_init( filter_t *, void * );
static void sub_filter_allocation_clear( filter_t * );
64 65 66 67 68 69
struct filter_owner_sys_t
{
    spu_t *p_spu;
    int i_channel;
};

70 71 72 73 74
enum {
    SCALE_DEFAULT,
    SCALE_TEXT,
    SCALE_SIZE
};
75

Laurent Aimar's avatar
Laurent Aimar committed
76 77
#define SCALE_UNIT (1000)

78 79 80 81 82 83 84 85 86
static void FilterRelease( filter_t *p_filter )
{
    if( p_filter->p_module )
        module_Unneed( p_filter, p_filter->p_module );

    vlc_object_detach( p_filter );
    vlc_object_release( p_filter );
}

87
/**
88
 * Creates the subpicture unit
89
 *
90
 * \param p_this the parent object which creates the subpicture unit
91
 */
92
spu_t *__spu_Create( vlc_object_t *p_this )
93 94
{
    int i_index;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
95 96
    spu_t *p_spu = vlc_custom_create( p_this, sizeof( spu_t ),
                                      VLC_OBJECT_GENERIC, "subpicture" );
97 98 99

    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
    {
100
        p_spu->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
101 102
    }

103 104 105
    p_spu->p_blend = NULL;
    p_spu->p_text = NULL;
    p_spu->p_scale = NULL;
106
    p_spu->p_scale_yuvp = NULL;
107
    p_spu->pf_control = spu_vaControlDefault;
108 109

    /* Register the default subpicture channel */
110
    p_spu->i_channel = 2;
111

112
    vlc_mutex_init( &p_spu->subpicture_lock );
113

114 115
    vlc_object_attach( p_spu, p_this );

116 117 118 119
    p_spu->p_chain = filter_chain_New( p_spu, "sub filter", false,
                                       sub_filter_allocation_init,
                                       sub_filter_allocation_clear,
                                       p_spu );
120 121 122 123 124 125 126 127 128 129 130 131
    return p_spu;
}

/**
 * Initialise the subpicture unit
 *
 * \param p_spu the subpicture unit object
 */
int spu_Init( spu_t *p_spu )
{
    vlc_value_t val;

132 133 134
    /* If the user requested a sub margin, we force the position. */
    var_Create( p_spu, "sub-margin", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_spu, "sub-margin", &val );
135 136 137
    p_spu->i_margin = val.i_int;

    var_Create( p_spu, "sub-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
138 139 140 141
    var_AddCallback( p_spu, "sub-filter", SubFilterCallback, p_spu );

    spu_ParseChain( p_spu );

142
    return VLC_SUCCESS;
143
}
Antoine Cellerier's avatar
 
Antoine Cellerier committed
144

145 146
int spu_ParseChain( spu_t *p_spu )
{
147 148
    char *psz_parser = var_GetString( p_spu, "sub-filter" );
    if( filter_chain_AppendFromString( p_spu->p_chain, psz_parser ) < 0 )
149
    {
150 151
        free( psz_parser );
        return VLC_EGENERIC;
152
    }
153

154
    free( psz_parser );
155
    return VLC_SUCCESS;
156 157 158
}

/**
159
 * Destroy the subpicture unit
160
 *
161
 * \param p_this the parent object which destroys the subpicture unit
162
 */
163
void spu_Destroy( spu_t *p_spu )
164 165 166 167 168 169
{
    int i_index;

    /* Destroy all remaining subpictures */
    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
    {
170
        if( p_spu->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
171
        {
172
            spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
173 174 175
        }
    }

176
    if( p_spu->p_blend )
177
        FilterRelease( p_spu->p_blend );
178

179
    if( p_spu->p_text )
180
        FilterRelease( p_spu->p_text );
181

182 183
    if( p_spu->p_scale_yuvp )
        FilterRelease( p_spu->p_scale_yuvp );
184

185
    if( p_spu->p_scale )
186
        FilterRelease( p_spu->p_scale );
187

188
    filter_chain_Delete( p_spu->p_chain );
189 190

    vlc_mutex_destroy( &p_spu->subpicture_lock );
191
    vlc_object_release( p_spu );
192 193
}

194 195 196
/**
 * Attach/Detach the SPU from any input
 *
197
 * \param p_this the object in which to destroy the subpicture unit
198 199
 * \param b_attach to select attach or detach
 */
200
void spu_Attach( spu_t *p_spu, vlc_object_t *p_this, bool b_attach )
201 202 203
{
    vlc_object_t *p_input;

204
    p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
205 206 207
    if( !p_input ) return;

    if( b_attach )
208
    {
209 210
        UpdateSPU( p_spu, VLC_OBJECT(p_input) );
        var_AddCallback( p_input, "highlight", CropCallback, p_spu );
211 212 213 214 215
        vlc_object_release( p_input );
    }
    else
    {
        /* Delete callback */
216
        var_DelCallback( p_input, "highlight", CropCallback, p_spu );
217 218 219 220 221 222 223 224 225 226
        vlc_object_release( p_input );
    }
}

/**
 * Create a subpicture region
 *
 * \param p_this vlc_object_t
 * \param p_fmt the format that this subpicture region should have
 */
227 228
static void RegionPictureRelease( picture_t *p_pic )
{
229
    free( p_pic->p_data_orig );
230 231
    /* We use pf_release nullity to know if the picture has already been released. */
    p_pic->pf_release = NULL;
232
}
233 234 235 236
subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this,
                                         video_format_t *p_fmt )
{
    subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
237 238
    if( !p_region ) return NULL;

239
    memset( p_region, 0, sizeof(subpicture_region_t) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
240
    p_region->i_alpha = 0xff;
241 242
    p_region->p_next = NULL;
    p_region->p_cache = NULL;
243
    p_region->fmt = *p_fmt;
244
    p_region->psz_text = NULL;
245
    p_region->p_style = NULL;
246 247 248 249 250 251

    if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
        p_fmt->p_palette = p_region->fmt.p_palette =
            malloc( sizeof(video_palette_t) );
    else p_fmt->p_palette = p_region->fmt.p_palette = NULL;

252
    p_region->picture.p_data_orig = NULL;
253 254

    if( p_fmt->i_chroma == VLC_FOURCC('T','E','X','T') ) return p_region;
255 256 257 258 259 260 261

    vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma,
                          p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );

    if( !p_region->picture.i_planes )
    {
        free( p_region );
262
        free( p_fmt->p_palette );
263 264 265
        return NULL;
    }

266 267
    p_region->picture.pf_release = RegionPictureRelease;

268 269 270
    return p_region;
}

271 272 273 274 275 276 277 278 279 280 281 282
/**
 * Make a subpicture region from an existing picture_t
 *
 * \param p_this vlc_object_t
 * \param p_fmt the format that this subpicture region should have
 * \param p_pic a pointer to the picture creating the region (not freed)
 */
subpicture_region_t *__spu_MakeRegion( vlc_object_t *p_this,
                                       video_format_t *p_fmt,
                                       picture_t *p_pic )
{
    subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
283
    (void)p_this;
284
    if( !p_region ) return NULL;
285
    memset( p_region, 0, sizeof(subpicture_region_t) );
286
    p_region->i_alpha = 0xff;
287 288 289 290
    p_region->p_next = 0;
    p_region->p_cache = 0;
    p_region->fmt = *p_fmt;
    p_region->psz_text = 0;
291
    p_region->p_style = NULL;
292 293 294 295 296 297 298 299 300 301 302 303

    if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
        p_fmt->p_palette = p_region->fmt.p_palette =
            malloc( sizeof(video_palette_t) );
    else p_fmt->p_palette = p_region->fmt.p_palette = NULL;

    memcpy( &p_region->picture, p_pic, sizeof(picture_t) );
    p_region->picture.pf_release = RegionPictureRelease;

    return p_region;
}

304 305 306 307 308 309 310 311 312
/**
 * Destroy a subpicture region
 *
 * \param p_this vlc_object_t
 * \param p_region the subpicture region to destroy
 */
void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region )
{
    if( !p_region ) return;
313 314
    if( p_region->picture.pf_release )
        p_region->picture.pf_release( &p_region->picture );
315
    free( p_region->fmt.p_palette );
316
    if( p_region->p_cache ) __spu_DestroyRegion( p_this, p_region->p_cache );
317

318 319
    free( p_region->psz_text );
    free( p_region->psz_html );
320
    //free( p_region->p_style ); FIXME --fenrir plugin does not allocate the memory for it. I think it might lead to segfault, video renderer can live longer than the decoder
321 322
    free( p_region );
}
Sam Hocevar's avatar
 
Sam Hocevar committed
323

324
/**
325
 * Display a subpicture
326
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
327 328
 * Remove the reservation flag of a subpicture, which will cause it to be
 * ready for display.
329
 * \param p_spu the subpicture unit object
330 331
 * \param p_subpic the subpicture to display
 */
332
void spu_DisplaySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
Sam Hocevar's avatar
 
Sam Hocevar committed
333 334 335 336
{
    /* Check if status is valid */
    if( p_subpic->i_status != RESERVED_SUBPICTURE )
    {
337 338
        msg_Err( p_spu, "subpicture %p has invalid status #%d",
                 p_subpic, p_subpic->i_status );
Sam Hocevar's avatar
 
Sam Hocevar committed
339 340
    }

341 342
    /* Remove reservation flag */
    p_subpic->i_status = READY_SUBPICTURE;
Sam Hocevar's avatar
 
Sam Hocevar committed
343

344
    if( p_subpic->i_channel == DEFAULT_CHAN )
Sam Hocevar's avatar
 
Sam Hocevar committed
345
    {
346 347 348
        p_subpic->i_channel = 0xFFFF;
        spu_Control( p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
        p_subpic->i_channel = DEFAULT_CHAN;
Sam Hocevar's avatar
 
Sam Hocevar committed
349
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
350 351
}

352
/**
353
 * Allocate a subpicture in the spu heap.
354
 *
355
 * This function create a reserved subpicture in the spu heap.
Sam Hocevar's avatar
 
Sam Hocevar committed
356 357 358
 * A null pointer is returned if the function fails. This method provides an
 * already allocated zone of memory in the spu data fields. It needs locking
 * since several pictures can be created by several producers threads.
359
 * \param p_spu the subpicture unit in which to create the subpicture
360 361
 * \return NULL on error, a reserved subpicture otherwise
 */
362
subpicture_t *spu_CreateSubpicture( spu_t *p_spu )
Sam Hocevar's avatar
 
Sam Hocevar committed
363 364
{
    int                 i_subpic;                        /* subpicture index */
365
    subpicture_t *      p_subpic = NULL;            /* first free subpicture */
Sam Hocevar's avatar
 
Sam Hocevar committed
366

367
    /* Get lock */
368
    vlc_mutex_lock( &p_spu->subpicture_lock );
369

Sam Hocevar's avatar
 
Sam Hocevar committed
370 371 372
    /*
     * Look for an empty place
     */
373
    p_subpic = NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
374 375
    for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
    {
376
        if( p_spu->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
Sam Hocevar's avatar
 
Sam Hocevar committed
377 378
        {
            /* Subpicture is empty and ready for allocation */
379 380
            p_subpic = &p_spu->p_subpicture[i_subpic];
            p_spu->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
381
            break;
Sam Hocevar's avatar
 
Sam Hocevar committed
382 383 384
        }
    }

385 386
    /* If no free subpicture could be found */
    if( p_subpic == NULL )
Sam Hocevar's avatar
 
Sam Hocevar committed
387
    {
388 389
        msg_Err( p_spu, "subpicture heap is full" );
        vlc_mutex_unlock( &p_spu->subpicture_lock );
390
        return NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
391
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
392

393
    /* Copy subpicture information, set some default values */
394 395
    memset( p_subpic, 0, sizeof(subpicture_t) );
    p_subpic->i_status   = RESERVED_SUBPICTURE;
396 397
    p_subpic->b_absolute = true;
    p_subpic->b_fade     = false;
398
    p_subpic->b_subtitle = false;
399
    p_subpic->i_alpha    = 0xFF;
400 401 402 403
    p_subpic->p_region   = NULL;
    p_subpic->pf_render  = NULL;
    p_subpic->pf_destroy = NULL;
    p_subpic->p_sys      = NULL;
404
    vlc_mutex_unlock( &p_spu->subpicture_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
405

406
    p_subpic->pf_create_region = __spu_CreateRegion;
407
    p_subpic->pf_make_region = __spu_MakeRegion;
408
    p_subpic->pf_destroy_region = __spu_DestroyRegion;
409

410
    return p_subpic;
Sam Hocevar's avatar
 
Sam Hocevar committed
411 412
}

413 414 415
/**
 * Remove a subpicture from the heap
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
416 417 418
 * This function frees a previously reserved subpicture.
 * It is meant to be used when the construction of a picture aborted.
 * This function does not need locking since reserved subpictures are ignored
419
 * by the spu.
420
 */
421
void spu_DestroySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
Sam Hocevar's avatar
 
Sam Hocevar committed
422
{
Gildas Bazin's avatar
 
Gildas Bazin committed
423
    /* Get lock */
424
    vlc_mutex_lock( &p_spu->subpicture_lock );
Gildas Bazin's avatar
 
Gildas Bazin committed
425 426 427 428

    /* There can be race conditions so we need to check the status */
    if( p_subpic->i_status == FREE_SUBPICTURE )
    {
429
        vlc_mutex_unlock( &p_spu->subpicture_lock );
Gildas Bazin's avatar
 
Gildas Bazin committed
430 431 432
        return;
    }

433 434 435 436
    /* Check if status is valid */
    if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
           && ( p_subpic->i_status != READY_SUBPICTURE ) )
    {
437
        msg_Err( p_spu, "subpicture %p has invalid status %d",
438 439 440
                         p_subpic, p_subpic->i_status );
    }

441 442 443 444
    while( p_subpic->p_region )
    {
        subpicture_region_t *p_region = p_subpic->p_region;
        p_subpic->p_region = p_region->p_next;
445
        spu_DestroyRegion( p_spu, p_region );
446 447
    }

448 449 450 451 452 453
    if( p_subpic->pf_destroy )
    {
        p_subpic->pf_destroy( p_subpic );
    }

    p_subpic->i_status = FREE_SUBPICTURE;
Gildas Bazin's avatar
 
Gildas Bazin committed
454

455
    vlc_mutex_unlock( &p_spu->subpicture_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
456 457 458
}

/*****************************************************************************
459
 * spu_RenderSubpictures: render a subpicture list
Sam Hocevar's avatar
 
Sam Hocevar committed
460
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
461
 * This function renders all sub picture units in the list.
Sam Hocevar's avatar
 
Sam Hocevar committed
462
 *****************************************************************************/
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
static void SpuRenderCreateBlend( spu_t *p_spu, vlc_fourcc_t i_chroma, int i_aspect )
{
    filter_t *p_blend;

    assert( !p_spu->p_blend );

    p_spu->p_blend =
    p_blend        = vlc_custom_create( p_spu, sizeof(filter_t),
                                        VLC_OBJECT_GENERIC, "blend" );
    if( !p_blend )
        return;

    es_format_Init( &p_blend->fmt_in, VIDEO_ES, 0 );

    es_format_Init( &p_blend->fmt_out, VIDEO_ES, 0 );
    p_blend->fmt_out.video.i_x_offset = 0;
    p_blend->fmt_out.video.i_y_offset = 0;
    p_blend->fmt_out.video.i_chroma = i_chroma;
    p_blend->fmt_out.video.i_aspect = i_aspect;

    /* The blend module will be loaded when needed with the real
    * input format */
    p_blend->p_module = NULL;

    /* */
    vlc_object_attach( p_blend, p_spu );
}
Laurent Aimar's avatar
Laurent Aimar committed
490
static void SpuRenderUpdateBlend( spu_t *p_spu, int i_out_width, int i_out_height, const video_format_t *p_in_fmt )
491 492 493 494 495 496
{
    filter_t *p_blend = p_spu->p_blend;

    assert( p_blend );

    /* */
Laurent Aimar's avatar
Laurent Aimar committed
497
    if( p_blend->p_module && p_blend->fmt_in.video.i_chroma != p_in_fmt->i_chroma )
498 499 500 501 502 503 504 505
    {
        /* The chroma is not the same, we need to reload the blend module
         * XXX to match the old behaviour just test !p_blend->fmt_in.video.i_chroma */
        module_Unneed( p_blend, p_blend->p_module );
        p_blend->p_module = NULL;
    }

    /* */
Laurent Aimar's avatar
Laurent Aimar committed
506 507 508 509 510 511 512
    p_blend->fmt_in.video = *p_in_fmt;

    /* */
    p_blend->fmt_out.video.i_width =
    p_blend->fmt_out.video.i_visible_width = i_out_width;
    p_blend->fmt_out.video.i_height =
    p_blend->fmt_out.video.i_visible_height = i_out_height;
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556

    /* */
    if( !p_blend->p_module )
        p_blend->p_module = module_Need( p_blend, "video blending", 0, 0 );
}
static void SpuRenderCreateAndLoadText( spu_t *p_spu, int i_width, int i_height )
{
    filter_t *p_text;

    assert( !p_spu->p_text );

    p_spu->p_text =
    p_text        = vlc_custom_create( p_spu, sizeof(filter_t),
                                       VLC_OBJECT_GENERIC, "spu text" );
    if( !p_text )
        return;

    es_format_Init( &p_text->fmt_in, VIDEO_ES, 0 );

    es_format_Init( &p_text->fmt_out, VIDEO_ES, 0 );
    p_text->fmt_out.video.i_width =
    p_text->fmt_out.video.i_visible_width = i_width;
    p_text->fmt_out.video.i_height =
    p_text->fmt_out.video.i_visible_height = i_height;

    p_text->pf_sub_buffer_new = spu_new_buffer;
    p_text->pf_sub_buffer_del = spu_del_buffer;

    vlc_object_attach( p_text, p_spu );

    /* FIXME TOCHECK shouldn't module_Need( , , psz_modulename, false ) do the
     * same than these 2 calls ? */
    char *psz_modulename = var_CreateGetString( p_spu, "text-renderer" );
    if( psz_modulename && *psz_modulename )
    {
        p_text->p_module = module_Need( p_text, "text renderer",
                                        psz_modulename, true );
    }
    free( psz_modulename );

    if( !p_text->p_module )
        p_text->p_module = module_Need( p_text, "text renderer", NULL, false );
}

557
static filter_t *CreateAndLoadScale( vlc_object_t *p_obj, vlc_fourcc_t i_chroma )
558 559 560
{
    filter_t *p_scale;

561 562
    p_scale = vlc_custom_create( p_obj, sizeof(filter_t),
                                 VLC_OBJECT_GENERIC, "scale" );
563
    if( !p_scale )
564
        return NULL;
565 566

    es_format_Init( &p_scale->fmt_in, VIDEO_ES, 0 );
567
    p_scale->fmt_in.video.i_chroma = i_chroma;
568 569 570 571
    p_scale->fmt_in.video.i_width =
    p_scale->fmt_in.video.i_height = 32;

    es_format_Init( &p_scale->fmt_out, VIDEO_ES, 0 );
572
    p_scale->fmt_out.video.i_chroma = i_chroma;
573 574 575 576 577 578
    p_scale->fmt_out.video.i_width =
    p_scale->fmt_out.video.i_height = 16;

    p_scale->pf_vout_buffer_new = spu_new_video_buffer;
    p_scale->pf_vout_buffer_del = spu_del_video_buffer;

579 580 581 582 583 584 585 586 587 588 589 590 591 592
    vlc_object_attach( p_scale, p_obj );
    p_scale->p_module = module_Need( p_scale, "video filter2", 0, 0 );

    return p_scale;
}

static void SpuRenderCreateAndLoadScale( spu_t *p_spu )
{
    /* FIXME: We'll also be using it for YUVA and RGBA blending ... */

    assert( !p_spu->p_scale );
    assert( !p_spu->p_scale_yuvp );
    p_spu->p_scale = CreateAndLoadScale( VLC_OBJECT(p_spu), VLC_FOURCC('Y','U','V','A') );
    p_spu->p_scale_yuvp = p_spu->p_scale_yuvp = CreateAndLoadScale( VLC_OBJECT(p_spu), VLC_FOURCC('Y','U','V','P') );
593 594
}

Laurent Aimar's avatar
Laurent Aimar committed
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
static void SpuRenderText( spu_t *p_spu, bool *pb_rerender_text,
                           subpicture_t *p_subpic, subpicture_region_t *p_region, int i_min_scale_ratio )
{
    assert( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') );

    if( !p_spu->p_text || !p_spu->p_text->p_module )
        goto exit;

    /* Setup 3 variables which can be used to render
     * time-dependent text (and effects). The first indicates
     * the total amount of time the text will be on screen,
     * the second the amount of time it has already been on
     * screen (can be a negative value as text is layed out
     * before it is rendered) and the third is a feedback
     * variable from the renderer - if the renderer sets it
     * then this particular text is time-dependent, eg. the
     * visual progress bar inside the text in karaoke and the
     * text needs to be rendered multiple times in order for
     * the effect to work - we therefore need to return the
     * region to its original state at the end of the loop,
     * instead of leaving it in YUVA or YUVP.
     * Any renderer which is unaware of how to render
     * time-dependent text can happily ignore the variables
     * and render the text the same as usual - it should at
     * least show up on screen, but the effect won't change
     * the text over time.
     */

    /* FIXME why these variables are recreated every time and not
     * when text renderer module was created ? */
    var_Create( p_spu->p_text, "spu-duration", VLC_VAR_TIME );
    var_Create( p_spu->p_text, "spu-elapsed", VLC_VAR_TIME );
    var_Create( p_spu->p_text, "text-rerender", VLC_VAR_BOOL );
    var_Create( p_spu->p_text, "scale", VLC_VAR_INTEGER );

    var_SetTime( p_spu->p_text, "spu-duration", p_subpic->i_stop - p_subpic->i_start );
    var_SetTime( p_spu->p_text, "spu-elapsed", mdate() - p_subpic->i_start );
    var_SetBool( p_spu->p_text, "text-rerender", false );
    var_SetInteger( p_spu->p_text, "scale", i_min_scale_ratio );

    if( p_spu->p_text->pf_render_html && p_region->psz_html )
    {
        p_spu->p_text->pf_render_html( p_spu->p_text,
                                       p_region, p_region );
    }
    else if( p_spu->p_text->pf_render_text )
    {
        p_spu->p_text->pf_render_text( p_spu->p_text,
                                       p_region, p_region );
    }
    *pb_rerender_text = var_GetBool( p_spu->p_text, "text-rerender" );

    var_Destroy( p_spu->p_text, "spu-duration" );
    var_Destroy( p_spu->p_text, "spu-elapsed" );
    var_Destroy( p_spu->p_text, "text-rerender" );
    var_Destroy( p_spu->p_text, "scale" );

exit:
    p_region->i_align |= SUBPICTURE_RENDERED;
}

Laurent Aimar's avatar
Laurent Aimar committed
656
static void SpuRenderRegion( spu_t *p_spu,
657
                             picture_t *p_pic_dst,
Laurent Aimar's avatar
Laurent Aimar committed
658 659 660 661 662 663 664
                             subpicture_t *p_subpic, subpicture_region_t *p_region,
                             const int i_scale_width_orig, const int i_scale_height_orig,
                             const int pi_subpic_x[SCALE_SIZE],
                             const int pi_scale_width[SCALE_SIZE],
                             const int pi_scale_height[SCALE_SIZE],
                             const video_format_t *p_fmt )
{
665
    video_format_t fmt_original;
Laurent Aimar's avatar
Laurent Aimar committed
666
    bool b_rerender_text;
667
    bool b_restore_format = false;
Laurent Aimar's avatar
Laurent Aimar committed
668
    int i_fade_alpha;
Laurent Aimar's avatar
Laurent Aimar committed
669 670
    int i_x_offset;
    int i_y_offset;
Laurent Aimar's avatar
Laurent Aimar committed
671 672 673
    int i_scale_idx;
    int i_inv_scale_x;
    int i_inv_scale_y;
674
    filter_t *p_scale;
Laurent Aimar's avatar
Laurent Aimar committed
675

676 677 678
    vlc_assert_locked( &p_spu->subpicture_lock );

    fmt_original = p_region->fmt;
Laurent Aimar's avatar
Laurent Aimar committed
679
    b_rerender_text = false;
Laurent Aimar's avatar
Laurent Aimar committed
680
    if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
Laurent Aimar's avatar
Laurent Aimar committed
681
    {
Laurent Aimar's avatar
Laurent Aimar committed
682
        SpuRenderText( p_spu, &b_rerender_text, p_subpic, p_region, __MIN(i_scale_width_orig, i_scale_height_orig) );
683
        b_restore_format = b_rerender_text;
Laurent Aimar's avatar
Laurent Aimar committed
684

Laurent Aimar's avatar
Laurent Aimar committed
685 686 687 688
        /* Check if the rendering has failed ... */
        if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
            goto exit;
    }
Laurent Aimar's avatar
Laurent Aimar committed
689

Laurent Aimar's avatar
Laurent Aimar committed
690 691
    if( p_region->i_align & SUBPICTURE_RENDERED )
    {
Laurent Aimar's avatar
Laurent Aimar committed
692
        /* We are using a region which come from rendered text */
Laurent Aimar's avatar
Laurent Aimar committed
693 694 695 696
        i_scale_idx   = SCALE_TEXT;
        i_inv_scale_x = i_scale_width_orig;
        i_inv_scale_y = i_scale_height_orig;
    }
Laurent Aimar's avatar
Laurent Aimar committed
697 698 699
    else
    {
        i_scale_idx   = SCALE_DEFAULT;
Laurent Aimar's avatar
Laurent Aimar committed
700 701
        i_inv_scale_x = SCALE_UNIT;
        i_inv_scale_y = SCALE_UNIT;
Laurent Aimar's avatar
Laurent Aimar committed
702
    }
Laurent Aimar's avatar
Laurent Aimar committed
703

Laurent Aimar's avatar
Laurent Aimar committed
704 705
    i_x_offset = (p_region->i_x + pi_subpic_x[ i_scale_idx ]) * i_inv_scale_x / SCALE_UNIT;
    i_y_offset = (p_region->i_y + p_subpic->i_y) * i_inv_scale_y / SCALE_UNIT;
Laurent Aimar's avatar
Laurent Aimar committed
706

707 708 709 710 711 712 713 714 715
    /* Force palette if requested
     * FIXME b_force_palette and b_force_crop are applied to all subpictures using palette
     * instead of only the right one (being the dvd spu).
     */
    const bool b_using_palette = p_region->fmt.i_chroma == VLC_FOURCC('Y','U','V','P');
    const bool b_force_palette = b_using_palette && p_spu->b_force_palette;
    const bool b_force_crop    = b_force_palette && p_spu->b_force_crop;

    if( b_force_palette )
Laurent Aimar's avatar
Laurent Aimar committed
716
    {
Laurent Aimar's avatar
Laurent Aimar committed
717
        /* It looks so wrong I won't comment
Laurent Aimar's avatar
Laurent Aimar committed
718 719 720
         * p_palette->palette is [256][4] with a int i_entries
         * p_spu->palette is [4][4]
         * */
721 722
        p_region->fmt.p_palette->i_entries = 4;
        memcpy( p_region->fmt.p_palette->palette, p_spu->palette, 4*sizeof(uint32_t) );
Laurent Aimar's avatar
Laurent Aimar committed
723 724
    }

725 726 727 728 729 730
    if( b_using_palette )
        p_scale = p_spu->p_scale_yuvp;
    else
        p_scale = p_spu->p_scale;

    if( p_scale &&
Laurent Aimar's avatar
Laurent Aimar committed
731 732
        ( ( pi_scale_width[i_scale_idx]  > 0 && pi_scale_width[i_scale_idx]  != SCALE_UNIT ) ||
          ( pi_scale_height[i_scale_idx] > 0 && pi_scale_height[i_scale_idx] != SCALE_UNIT ) ||
733
          ( b_force_palette ) ) )
Laurent Aimar's avatar
Laurent Aimar committed
734
    {
Laurent Aimar's avatar
Laurent Aimar committed
735 736
        const unsigned i_dst_width  = p_region->fmt.i_width  * pi_scale_width[i_scale_idx] / SCALE_UNIT;
        const unsigned i_dst_height = p_region->fmt.i_height * pi_scale_height[i_scale_idx] / SCALE_UNIT;
Laurent Aimar's avatar
Laurent Aimar committed
737 738 739 740 741

        /* Destroy if cache is unusable */
        if( p_region->p_cache )
        {
            if( p_region->p_cache->fmt.i_width  != i_dst_width ||
742
                p_region->p_cache->fmt.i_height != i_dst_height ||
743
                b_force_palette )
Laurent Aimar's avatar
Laurent Aimar committed
744 745 746 747 748 749 750 751
            {
                p_subpic->pf_destroy_region( VLC_OBJECT(p_spu),
                                             p_region->p_cache );
                p_region->p_cache = NULL;
            }
        }

        /* Scale if needed into cache */
Laurent Aimar's avatar
Laurent Aimar committed
752
        if( !p_region->p_cache )
Laurent Aimar's avatar
Laurent Aimar committed
753
        {
Laurent Aimar's avatar
Laurent Aimar committed
754 755
            picture_t *p_pic;

756 757
            p_scale->fmt_in.video = p_region->fmt;
            p_scale->fmt_out.video = p_region->fmt;
Laurent Aimar's avatar
Laurent Aimar committed
758 759 760

            p_region->p_cache =
                p_subpic->pf_create_region( VLC_OBJECT(p_spu),
761
                                            &p_scale->fmt_out.video );
Laurent Aimar's avatar
Laurent Aimar committed
762 763
            p_region->p_cache->p_next = p_region->p_next;

764 765
            if( p_scale->fmt_out.video.p_palette )
                *p_scale->fmt_out.video.p_palette =
Laurent Aimar's avatar
Laurent Aimar committed
766 767 768 769 770
                    *p_region->fmt.p_palette;

            vout_CopyPicture( p_spu, &p_region->p_cache->picture,
                              &p_region->picture );

771 772
            p_scale->fmt_out.video.i_width = i_dst_width;
            p_scale->fmt_out.video.i_height = i_dst_height;
Laurent Aimar's avatar
Laurent Aimar committed
773

774
            p_scale->fmt_out.video.i_visible_width =
Laurent Aimar's avatar
Laurent Aimar committed
775
                p_region->fmt.i_visible_width * pi_scale_width[ i_scale_idx ] / SCALE_UNIT;
776
            p_scale->fmt_out.video.i_visible_height =
Laurent Aimar's avatar
Laurent Aimar committed
777
                p_region->fmt.i_visible_height * pi_scale_height[ i_scale_idx ] / SCALE_UNIT;
Laurent Aimar's avatar
Laurent Aimar committed
778

779
            p_region->p_cache->fmt = p_scale->fmt_out.video;
Laurent Aimar's avatar
Laurent Aimar committed
780 781
            p_region->p_cache->i_x = p_region->i_x * pi_scale_width[ i_scale_idx ] / SCALE_UNIT;
            p_region->p_cache->i_y = p_region->i_y * pi_scale_height[ i_scale_idx ] / SCALE_UNIT;
Laurent Aimar's avatar
Laurent Aimar committed
782 783 784
            p_region->p_cache->i_align = p_region->i_align;
            p_region->p_cache->i_alpha = p_region->i_alpha;

785 786 787 788 789
            p_pic = NULL;
            if( p_scale->p_module )
                p_pic = p_scale->pf_video_filter( p_scale, &p_region->p_cache->picture );
            else
                msg_Err( p_spu, "scaling failed (module not loaded)" );
790

Laurent Aimar's avatar
Laurent Aimar committed
791 792 793 794 795
            if( p_pic )
            {
                p_region->p_cache->picture = *p_pic;
                free( p_pic );
            }
796 797 798 799 800 801
            else
            {
                p_subpic->pf_destroy_region( VLC_OBJECT(p_spu),
                                             p_region->p_cache );
                p_region->p_cache = NULL;
            }
Laurent Aimar's avatar
Laurent Aimar committed
802 803
        }

804
        /* And use the scaled picture */
Laurent Aimar's avatar
Laurent Aimar committed
805
        if( p_region->p_cache )
806
        {
Laurent Aimar's avatar
Laurent Aimar committed
807
            p_region = p_region->p_cache;
808 809
            fmt_original = p_region->fmt;
        }
Laurent Aimar's avatar
Laurent Aimar committed
810 811 812 813 814
    }

    if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
    {
        i_y_offset = p_fmt->i_height - p_region->fmt.i_height -
Laurent Aimar's avatar
Laurent Aimar committed
815
            (p_subpic->i_y + p_region->i_y) * i_inv_scale_y / SCALE_UNIT;
Laurent Aimar's avatar
Laurent Aimar committed
816 817 818 819 820 821 822 823 824 825
    }
    else if ( !(p_region->i_align & SUBPICTURE_ALIGN_TOP) )
    {
        i_y_offset = p_fmt->i_height / 2 - p_region->fmt.i_height / 2;
    }

    if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
    {
        i_x_offset = p_fmt->i_width - p_region->fmt.i_width -
            (pi_subpic_x[ i_scale_idx ] + p_region->i_x)
Laurent Aimar's avatar
Laurent Aimar committed
826
            * i_inv_scale_x / SCALE_UNIT;
Laurent Aimar's avatar
Laurent Aimar committed
827 828 829 830 831 832 833 834 835 836
    }
    else if ( !(p_region->i_align & SUBPICTURE_ALIGN_LEFT) )
    {
        i_x_offset = p_fmt->i_width / 2 - p_region->fmt.i_width / 2;
    }

    if( p_subpic->b_absolute )
    {
        i_x_offset = (p_region->i_x +
            pi_subpic_x[ i_scale_idx ] *
Laurent Aimar's avatar
Laurent Aimar committed
837 838
                             pi_scale_width[ i_scale_idx ] / SCALE_UNIT)
            * i_inv_scale_x / SCALE_UNIT;
Laurent Aimar's avatar
Laurent Aimar committed
839
        i_y_offset = (p_region->i_y +
Laurent Aimar's avatar
Laurent Aimar committed
840 841
            p_subpic->i_y * pi_scale_height[ i_scale_idx ] / SCALE_UNIT)
            * i_inv_scale_y / SCALE_UNIT;
Laurent Aimar's avatar
Laurent Aimar committed
842 843 844 845 846 847

    }

    i_x_offset = __MAX( i_x_offset, 0 );
    i_y_offset = __MAX( i_y_offset, 0 );

848
    if( p_spu->i_margin != 0 && !b_force_crop )
Laurent Aimar's avatar
Laurent Aimar committed
849 850
    {
        int i_diff = 0;
Laurent Aimar's avatar
Laurent Aimar committed
851
        int i_low = (i_y_offset - p_spu->i_margin) * i_inv_scale_y / SCALE_UNIT;
Laurent Aimar's avatar
Laurent Aimar committed
852 853 854 855 856 857 858
        int i_high = i_low + p_region->fmt.i_height;

        /* crop extra margin to keep within bounds */
        if( i_low < 0 )
            i_diff = i_low;
        if( i_high > (int)p_fmt->i_height )
            i_diff = i_high - p_fmt->i_height;
Laurent Aimar's avatar
Laurent Aimar committed
859
        i_y_offset -= ( p_spu->i_margin * i_inv_scale_y / SCALE_UNIT + i_diff );
Laurent Aimar's avatar
Laurent Aimar committed
860 861
    }

Laurent Aimar's avatar
Laurent Aimar committed
862
    /* Force cropping if requested */
863
    if( b_force_crop )
Laurent Aimar's avatar
Laurent Aimar committed
864
    {
Laurent Aimar's avatar
Laurent Aimar committed
865
        video_format_t *p_fmt = &p_region->fmt;
Laurent Aimar's avatar
Laurent Aimar committed
866 867 868 869 870 871 872 873
        int i_crop_x = p_spu->i_crop_x * pi_scale_width[ i_scale_idx ] / SCALE_UNIT
                            * i_inv_scale_x / SCALE_UNIT;
        int i_crop_y = p_spu->i_crop_y * pi_scale_height[ i_scale_idx ] / SCALE_UNIT
                            * i_inv_scale_y / SCALE_UNIT;
        int i_crop_width = p_spu->i_crop_width * pi_scale_width[ i_scale_idx ] / SCALE_UNIT
                            * i_inv_scale_x / SCALE_UNIT;
        int i_crop_height = p_spu->i_crop_height * pi_scale_height[ i_scale_idx ] / SCALE_UNIT
                            * i_inv_scale_y / SCALE_UNIT;
Laurent Aimar's avatar
Laurent Aimar committed
874 875 876 877 878 879

        /* Find the intersection */
        if( i_crop_x + i_crop_width <= i_x_offset ||
            i_x_offset + (int)p_fmt->i_visible_width < i_crop_x ||
            i_crop_y + i_crop_height <= i_y_offset ||
            i_y_offset + (int)p_fmt->i_visible_height < i_crop_y )
Laurent Aimar's avatar
Laurent Aimar committed
880
        {
Laurent Aimar's avatar
Laurent Aimar committed
881 882
            /* No intersection */
            p_fmt->i_visible_width = p_fmt->i_visible_height = 0;
Laurent Aimar's avatar
Laurent Aimar committed
883 884 885
        }
        else
        {
Laurent Aimar's avatar
Laurent Aimar committed
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
            int i_x, i_y, i_x_end, i_y_end;
            i_x = __MAX( i_crop_x, i_x_offset );
            i_y = __MAX( i_crop_y, i_y_offset );
            i_x_end = __MIN( i_crop_x + i_crop_width,
                           i_x_offset + (int)p_fmt->i_visible_width );
            i_y_end = __MIN( i_crop_y + i_crop_height,
                           i_y_offset + (int)p_fmt->i_visible_height );

            p_fmt->i_x_offset = i_x - i_x_offset;
            p_fmt->i_y_offset = i_y - i_y_offset;
            p_fmt->i_visible_width = i_x_end - i_x;
            p_fmt->i_visible_height = i_y_end - i_y;

            i_x_offset = i_x;
            i_y_offset = i_y;
Laurent Aimar's avatar
Laurent Aimar committed
901
        }
902
        b_restore_format = true;
Laurent Aimar's avatar
Laurent Aimar committed
903 904
    }

Laurent Aimar's avatar
Laurent Aimar committed
905 906 907
    i_x_offset = __MAX( i_x_offset, 0 );
    i_y_offset = __MAX( i_y_offset, 0 );

Laurent Aimar's avatar
Laurent Aimar committed
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923
    /* Compute alpha blend value */
    i_fade_alpha = 255;
    if( p_subpic->b_fade )
    {
        mtime_t i_fade_start = ( p_subpic->i_stop +
                                 p_subpic->i_start ) / 2;
        mtime_t i_now = mdate();
        if( i_now >= i_fade_start && p_subpic->i_stop > i_fade_start )
        {
            i_fade_alpha = 255 * ( p_subpic->i_stop - i_now ) /
                           ( p_subpic->i_stop - i_fade_start );
        }
    }

    /* Update the blender */
    SpuRenderUpdateBlend( p_spu, p_fmt->i_width, p_fmt->i_height, &p_region->fmt );
Laurent Aimar's avatar
Laurent Aimar committed
924 925 926 927

    if( p_spu->p_blend->p_module )
    {
        p_spu->p_blend->pf_video_blend( p_spu->p_blend, p_pic_dst,
928
            &p_region->picture, i_x_offset, i_y_offset,
Laurent Aimar's avatar
Laurent Aimar committed
929 930 931 932 933 934 935 936 937 938
            i_fade_alpha * p_subpic->i_alpha * p_region->i_alpha / 65025 );
    }
    else
    {
        msg_Err( p_spu, "blending %4.4s to %4.4s failed",
                 (char *)&p_spu->p_blend->fmt_out.video.i_chroma,
                 (char *)&p_spu->p_blend->fmt_out.video.i_chroma );
    }

exit:
Laurent Aimar's avatar
Laurent Aimar committed
939 940 941 942 943 944 945 946 947 948 949
    if( b_rerender_text )
    {
        /* Some forms of subtitles need to be re-rendered more than
         * once, eg. karaoke. We therefore restore the region to its
         * pre-rendered state, so the next time through everything is
         * calculated again.
         */
        p_region->picture.pf_release( &p_region->picture );
        memset( &p_region->picture, 0, sizeof( picture_t ) );
        p_region->i_align &= ~SUBPICTURE_RENDERED;
    }
950
    if( b_restore_format )
951
        p_region->fmt = fmt_original;
Laurent Aimar's avatar
Laurent Aimar committed
952
}
953

954
void spu_RenderSubpictures( spu_t *p_spu, video_format_t *p_fmt,
955
                            picture_t *p_pic_dst,
956
                            subpicture_t *p_subpic_list,
957
                            int i_scale_width_orig, int i_scale_height_orig )
Sam Hocevar's avatar
 
Sam Hocevar committed
958
{
959
    const mtime_t i_current_date = mdate();
960 961
    int i_source_video_width;
    int i_source_video_height;
962
    subpicture_t *p_subpic;
963

Gildas Bazin's avatar
 
Gildas Bazin committed
964
    /* Get lock */
965
    vlc_mutex_lock( &p_spu->subpicture_lock );
Gildas Bazin's avatar
 
Gildas Bazin committed
966

967
    if( i_scale_width_orig <= 0 )
Laurent Aimar's avatar
Laurent Aimar committed
968
        i_scale_width_orig = SCALE_UNIT;
969
    if( i_scale_height_orig <= 0 )
Laurent Aimar's avatar
Laurent Aimar committed
970
        i_scale_height_orig = SCALE_UNIT;
971

Laurent Aimar's avatar
Laurent Aimar committed
972 973
    i_source_video_width  = p_fmt->i_width  * SCALE_UNIT / i_scale_width_orig;
    i_source_video_height = p_fmt->i_height * SCALE_UNIT / i_scale_height_orig;
974

Gildas Bazin's avatar
 
Gildas Bazin committed
975
    /* Check i_status again to make sure spudec hasn't destroyed the subpic */
976 977 978
    for( p_subpic = p_subpic_list;
            p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE;
                p_subpic = p_subpic->p_next )
Sam Hocevar's avatar
 
Sam Hocevar committed
979
    {
980 981 982 983 984 985 986
        /* If the source video and subtitles stream agree on the size of
         * the video then disregard all further references to the subtitle
         * stream.
         */
        if( ( i_source_video_height == p_subpic->i_original_picture_height ) &&
            ( i_source_video_width  == p_subpic->i_original_picture_width ) )
        {
987
            /* FIXME this looks wrong */
988 989 990 991
            p_subpic->i_original_picture_height = 0;
            p_subpic->i_original_picture_width = 0;
        }

992 993 994
        /* */
        if( p_subpic->pf_pre_render )
            p_subpic->pf_pre_render( p_fmt, p_spu, p_subpic );
995

996 997
        if( p_subpic->pf_update_regions )
        {
998 999 1000 1001 1002 1003 1004 1005 1006
            /* TODO do not reverse the scaling that was done before calling
             * spu_RenderSubpictures, just pass it along (or do it inside
             * spu_RenderSubpictures) */
            video_format_t fmt_org = *p_fmt;
            fmt_org.i_width =
            fmt_org.i_visible_width = i_source_video_width;
            fmt_org.i_height =
            fmt_org.i_visible_height = i_source_video_height;

1007
            p_subpic->pf_update_regions( &fmt_org, p_spu, p_subpic, i_current_date );
1008