1 /*****************************************************************************
2 * vout_subpictures.c : subpicture management functions
3 *****************************************************************************
4 * Copyright (C) 2000-2004 VideoLAN
7 * Authors: Vincent Seguin <seguin@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
9 * Gildas Bazin <gbazin@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
29 #include <stdlib.h> /* free() */
30 #include <stdio.h> /* sprintf() */
31 #include <string.h> /* strerror() */
35 #include "vlc_block.h"
36 #include "vlc_video.h"
37 #include "video_output.h"
38 #include "vlc_filter.h"
41 static void UpdateSPU ( vout_thread_t *, vlc_object_t * );
42 static int CropCallback ( vlc_object_t *, char const *,
43 vlc_value_t, vlc_value_t, void * );
45 static subpicture_t *spu_new_buffer( filter_t * );
46 static void spu_del_buffer( filter_t *, subpicture_t * );
49 * Initialise the subpicture decoder unit
51 * \param p_vout the vout in which to create the subpicture unit
53 void vout_InitSPU( vout_thread_t *p_vout )
57 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
59 p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
60 p_vout->p_subpicture[i_index].i_type = EMPTY_SUBPICTURE;
63 p_vout->p_blend = NULL;
64 p_vout->p_text = NULL;
66 p_vout->i_crop_x = p_vout->i_crop_y =
67 p_vout->i_crop_width = p_vout->i_crop_height = 0;
68 p_vout->b_force_alpha = VLC_FALSE;
69 p_vout->b_force_crop = VLC_FALSE;
73 * Destroy the subpicture decoder unit
75 * \param p_vout the vout in which to destroy the subpicture unit
77 void vout_DestroySPU( vout_thread_t *p_vout )
81 /* Destroy all remaining subpictures */
82 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
84 if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
86 vout_DestroySubPicture( p_vout,
87 &p_vout->p_subpicture[i_index] );
93 if( p_vout->p_blend->p_module )
94 module_Unneed( p_vout->p_blend, p_vout->p_blend->p_module );
96 vlc_object_detach( p_vout->p_blend );
97 vlc_object_destroy( p_vout->p_blend );
102 if( p_vout->p_text->p_module )
103 module_Unneed( p_vout->p_text, p_vout->p_text->p_module );
105 vlc_object_detach( p_vout->p_text );
106 vlc_object_destroy( p_vout->p_text );
109 vout_AttachSPU( p_vout, VLC_OBJECT(p_vout), VLC_FALSE );
113 * Attach/Detach the SPU from any input
115 * \param p_vout the vout in which to destroy the subpicture unit
116 * \param b_attach to select attach or detach
118 void vout_AttachSPU( vout_thread_t *p_vout, vlc_object_t *p_this,
119 vlc_bool_t b_attach )
121 vlc_object_t *p_input;
123 p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
124 if( !p_input ) return;
128 UpdateSPU( p_vout, VLC_OBJECT(p_input) );
129 var_AddCallback( p_input, "highlight", CropCallback, p_vout );
130 vlc_object_release( p_input );
134 /* Delete callback */
135 var_DelCallback( p_input, "highlight", CropCallback, p_vout );
136 vlc_object_release( p_input );
142 * Create a subpicture region
144 * \param p_this vlc_object_t
145 * \param p_fmt the format that this subpicture region should have
147 subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this,
148 video_format_t *p_fmt )
150 subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
151 memset( p_region, 0, sizeof(subpicture_region_t) );
152 p_region->p_next = 0;
153 p_region->fmt = *p_fmt;
154 p_region->psz_text = 0;
156 if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
157 p_fmt->p_palette = p_region->fmt.p_palette =
158 malloc( sizeof(video_palette_t) );
159 else p_fmt->p_palette = p_region->fmt.p_palette = NULL;
161 p_region->picture.p_data_orig = 0;
163 if( p_fmt->i_chroma == VLC_FOURCC('T','E','X','T') ) return p_region;
165 vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma,
166 p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
168 if( !p_region->picture.i_planes )
171 free( p_fmt->p_palette );
179 * Destroy a subpicture region
181 * \param p_this vlc_object_t
182 * \param p_region the subpicture region to destroy
184 void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region )
186 if( !p_region ) return;
187 if( p_region->picture.p_data_orig ) free( p_region->picture.p_data_orig );
188 if( p_region->fmt.p_palette ) free( p_region->fmt.p_palette );
189 if( p_region->psz_text ) free( p_region->psz_text );
194 * Display a subpicture unit
196 * Remove the reservation flag of a subpicture, which will cause it to be
198 * \param p_vout the video output this subpicture should be displayed on
199 * \param p_subpic the subpicture to display
201 void vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
205 /* Check if status is valid */
206 if( p_subpic->i_status != RESERVED_SUBPICTURE )
208 msg_Err( p_vout, "subpicture %p has invalid status #%d",
209 p_subpic, p_subpic->i_status );
212 /* If the user requested an SPU margin, we force the position after
213 * having checked that it was a valid value. */
214 i_margin = config_GetInt( p_vout, "spumargin" );
218 if( p_subpic->i_height + (unsigned int)i_margin
219 <= p_vout->output.i_height )
221 p_subpic->i_y = p_vout->output.i_height
222 - i_margin - p_subpic->i_height;
226 /* Remove reservation flag */
227 p_subpic->i_status = READY_SUBPICTURE;
231 * Allocate a subpicture in the video output heap.
233 * This function create a reserved subpicture in the video output heap.
234 * A null pointer is returned if the function fails. This method provides an
235 * already allocated zone of memory in the spu data fields. It needs locking
236 * since several pictures can be created by several producers threads.
237 * \param p_vout the vout in which to create the subpicture
238 * \param i_channel the channel this subpicture should belong to
239 * \param i_type the type of the subpicture
240 * \return NULL on error, a reserved subpicture otherwise
242 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_channel,
245 int i_subpic; /* subpicture index */
246 subpicture_t * p_subpic = NULL; /* first free subpicture */
248 /* Clear the default channel before writing into it */
249 if( i_channel == DEFAULT_CHAN )
251 vout_ClearOSDChannel( p_vout, DEFAULT_CHAN );
255 vlc_mutex_lock( &p_vout->subpicture_lock );
258 * Look for an empty place
261 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
263 if( p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
265 /* Subpicture is empty and ready for allocation */
266 p_subpic = &p_vout->p_subpicture[i_subpic];
267 p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
272 /* If no free subpicture could be found */
273 if( p_subpic == NULL )
275 msg_Err( p_vout, "subpicture heap is full" );
276 vlc_mutex_unlock( &p_vout->subpicture_lock );
280 /* Copy subpicture information, set some default values */
281 p_subpic->i_channel = i_channel;
282 p_subpic->i_type = i_type;
283 p_subpic->i_status = RESERVED_SUBPICTURE;
285 p_subpic->i_start = 0;
286 p_subpic->i_stop = 0;
287 p_subpic->b_ephemer = VLC_FALSE;
291 p_subpic->i_width = 0;
292 p_subpic->i_height = 0;
293 p_subpic->b_absolute= VLC_TRUE;
294 p_subpic->i_flags = 0;
295 p_subpic->pf_render = 0;
296 p_subpic->pf_destroy= 0;
299 /* Remain last subpicture displayed in DEFAULT_CHAN */
300 if( i_channel == DEFAULT_CHAN )
302 p_vout->p_default_channel = p_subpic;
305 vlc_mutex_unlock( &p_vout->subpicture_lock );
307 p_subpic->pf_create_region = __spu_CreateRegion;
308 p_subpic->pf_destroy_region = __spu_DestroyRegion;
314 * Remove a subpicture from the heap
316 * This function frees a previously reserved subpicture.
317 * It is meant to be used when the construction of a picture aborted.
318 * This function does not need locking since reserved subpictures are ignored
319 * by the output thread.
321 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
324 vlc_mutex_lock( &p_vout->subpicture_lock );
326 /* There can be race conditions so we need to check the status */
327 if( p_subpic->i_status == FREE_SUBPICTURE )
329 vlc_mutex_unlock( &p_vout->subpicture_lock );
333 /* Check if status is valid */
334 if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
335 && ( p_subpic->i_status != READY_SUBPICTURE ) )
337 msg_Err( p_vout, "subpicture %p has invalid status %d",
338 p_subpic, p_subpic->i_status );
341 while( p_subpic->p_region )
343 subpicture_region_t *p_region = p_subpic->p_region;
344 p_subpic->p_region = p_region->p_next;
345 spu_DestroyRegion( p_vout, p_region );
348 if( p_subpic->pf_destroy )
350 p_subpic->pf_destroy( p_subpic );
353 p_subpic->i_status = FREE_SUBPICTURE;
355 vlc_mutex_unlock( &p_vout->subpicture_lock );
358 /*****************************************************************************
359 * vout_RenderSubPictures: render a subpicture list
360 *****************************************************************************
361 * This function renders all sub picture units in the list.
362 *****************************************************************************/
363 void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic_dst,
364 picture_t *p_pic_src, subpicture_t *p_subpic )
367 vlc_mutex_lock( &p_vout->subpicture_lock );
369 /* Check i_status again to make sure spudec hasn't destroyed the subpic */
370 while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE )
372 subpicture_region_t *p_region = p_subpic->p_region;
374 /* Load the blending module */
375 if( !p_vout->p_blend && p_region )
377 p_vout->p_blend = vlc_object_create( p_vout, sizeof(filter_t) );
378 vlc_object_attach( p_vout->p_blend, p_vout );
379 p_vout->p_blend->fmt_out.video.i_x_offset =
380 p_vout->p_blend->fmt_out.video.i_y_offset = 0;
381 p_vout->p_blend->fmt_out.video.i_aspect =
382 p_vout->render.i_aspect;
383 p_vout->p_blend->fmt_out.video.i_chroma =
384 p_vout->output.i_chroma;
386 p_vout->p_blend->fmt_in.video.i_chroma = VLC_FOURCC('Y','U','V','P');
388 p_vout->p_blend->p_module =
389 module_Need( p_vout->p_blend, "video blending", 0, 0 );
392 /* Load the text rendering module */
393 if( !p_vout->p_text && p_region )
395 p_vout->p_text = vlc_object_create( p_vout, sizeof(filter_t) );
396 vlc_object_attach( p_vout->p_text, p_vout );
398 p_vout->p_text->fmt_out.video.i_width =
399 p_vout->p_text->fmt_out.video.i_visible_width =
400 p_vout->output.i_width;
401 p_vout->p_text->fmt_out.video.i_height =
402 p_vout->p_text->fmt_out.video.i_visible_height =
403 p_vout->output.i_height;
405 p_vout->p_text->pf_spu_buffer_new = spu_new_buffer;
406 p_vout->p_text->pf_spu_buffer_del = spu_del_buffer;
408 p_vout->p_text->p_module =
409 module_Need( p_vout->p_text, "text renderer", 0, 0 );
412 if( p_subpic->pf_render )
414 p_subpic->pf_render( p_vout, p_pic_dst, p_subpic );
416 else while( p_region && p_vout->p_blend &&
417 p_vout->p_blend->pf_video_blend )
419 int i_x_offset = p_region->i_x + p_subpic->i_x;
420 int i_y_offset = p_region->i_y + p_subpic->i_y;
422 if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
424 if( p_vout->p_text && p_vout->p_text->p_module &&
425 p_vout->p_text->pf_render_string )
427 /* TODO: do it in a less hacky way
428 * (modify text renderer API) */
430 subpicture_region_t tmp_region;
431 block_t *p_new_block =
432 block_New( p_vout, strlen(p_region->psz_text) + 1 );
436 memcpy( p_new_block->p_buffer, p_region->psz_text,
437 p_new_block->i_buffer );
438 p_new_block->i_pts = p_new_block->i_dts =
440 p_new_block->i_length =
441 p_subpic->i_start - p_subpic->i_stop;
442 p_spu = p_vout->p_text->pf_render_string(
443 p_vout->p_text, p_new_block );
447 tmp_region = *p_region;
448 *p_region = *p_spu->p_region;
449 p_region->p_next = tmp_region.p_next;
450 *p_spu->p_region = tmp_region;
451 p_vout->p_text->pf_spu_buffer_del( p_vout->p_text,
458 if( p_subpic->i_flags & OSD_ALIGN_BOTTOM )
460 i_y_offset = p_vout->output.i_height - p_region->fmt.i_height -
463 else if ( !(p_subpic->i_flags & OSD_ALIGN_TOP) )
465 i_y_offset = p_vout->output.i_height / 2 -
466 p_region->fmt.i_height / 2;
469 if( p_subpic->i_flags & OSD_ALIGN_RIGHT )
471 i_x_offset = p_vout->output.i_width - p_region->fmt.i_width -
474 else if ( !(p_subpic->i_flags & OSD_ALIGN_LEFT) )
476 i_x_offset = p_vout->output.i_width / 2 -
477 p_region->fmt.i_width / 2;
480 if( p_subpic->b_absolute )
482 i_x_offset = p_region->i_x + p_subpic->i_x;
483 i_y_offset = p_region->i_y + p_subpic->i_y;
486 p_vout->p_blend->fmt_in.video = p_region->fmt;
488 /* Force cropping if requested */
489 if( p_vout->b_force_crop )
491 video_format_t *p_fmt = &p_vout->p_blend->fmt_in.video;
493 /* Find the intersection */
494 if( p_vout->i_crop_x + p_vout->i_crop_width <= i_x_offset ||
495 i_x_offset + (int)p_fmt->i_visible_width <
497 p_vout->i_crop_y + p_vout->i_crop_height <= i_y_offset ||
498 i_y_offset + (int)p_fmt->i_visible_height <
501 /* No intersection */
502 p_fmt->i_visible_width = p_fmt->i_visible_height = 0;
506 int i_x, i_y, i_x_end, i_y_end;
507 i_x = __MAX( p_vout->i_crop_x, i_x_offset );
508 i_y = __MAX( p_vout->i_crop_y, i_y_offset );
509 i_x_end = __MIN( p_vout->i_crop_x + p_vout->i_crop_width,
510 i_x_offset + (int)p_fmt->i_visible_width );
511 i_y_end = __MIN( p_vout->i_crop_y + p_vout->i_crop_height,
512 i_y_offset + (int)p_fmt->i_visible_height );
514 p_fmt->i_x_offset = i_x - i_x_offset;
515 p_fmt->i_y_offset = i_y - i_y_offset;
516 p_fmt->i_visible_width = i_x_end - i_x;
517 p_fmt->i_visible_height = i_y_end - i_y;
524 /* Force palette if requested */
525 if( p_vout->b_force_alpha && VLC_FOURCC('Y','U','V','P') ==
526 p_vout->p_blend->fmt_in.video.i_chroma )
528 p_vout->p_blend->fmt_in.video.p_palette->palette[0][3] =
530 p_vout->p_blend->fmt_in.video.p_palette->palette[1][3] =
532 p_vout->p_blend->fmt_in.video.p_palette->palette[2][3] =
534 p_vout->p_blend->fmt_in.video.p_palette->palette[3][3] =
538 /* Update the output picture size */
539 p_vout->p_blend->fmt_out.video.i_width =
540 p_vout->p_blend->fmt_out.video.i_visible_width =
541 p_vout->output.i_width;
542 p_vout->p_blend->fmt_out.video.i_height =
543 p_vout->p_blend->fmt_out.video.i_visible_height =
544 p_vout->output.i_height;
546 p_vout->p_blend->pf_video_blend( p_vout->p_blend, p_pic_dst,
547 p_pic_src, &p_region->picture, i_x_offset, i_y_offset );
549 p_region = p_region->p_next;
552 p_subpic = p_subpic->p_next;
555 vlc_mutex_unlock( &p_vout->subpicture_lock );
558 /*****************************************************************************
559 * vout_SortSubPictures: find the subpictures to display
560 *****************************************************************************
561 * This function parses all subpictures and decides which ones need to be
562 * displayed. This operation does not need lock, since only READY_SUBPICTURE
563 * are handled. If no picture has been selected, display_date will depend on
565 * We also check for ephemer DVD subpictures (subpictures that have
566 * to be removed if a newer one is available), which makes it a lot
567 * more difficult to guess if a subpicture has to be rendered or not.
568 *****************************************************************************/
569 subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
570 mtime_t display_date )
573 subpicture_t *p_subpic = NULL;
574 subpicture_t *p_ephemer = NULL;
575 mtime_t ephemer_date = 0;
577 /* We get an easily parsable chained list of subpictures which
578 * ends with NULL since p_subpic was initialized to NULL. */
579 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
581 if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
583 /* If it is a DVD subpicture, check its date */
584 if( p_vout->p_subpicture[i_index].i_type == MEMORY_SUBPICTURE )
586 if( !p_vout->p_subpicture[i_index].b_ephemer
587 && display_date > p_vout->p_subpicture[i_index].i_stop )
589 /* Too late, destroy the subpic */
590 vout_DestroySubPicture( p_vout,
591 &p_vout->p_subpicture[i_index] );
596 && display_date < p_vout->p_subpicture[i_index].i_start )
598 /* Too early, come back next monday */
602 /* If this is an ephemer subpic, see if it's the
603 * youngest we have */
604 if( p_vout->p_subpicture[i_index].b_ephemer )
606 if( p_ephemer == NULL )
608 p_ephemer = &p_vout->p_subpicture[i_index];
612 if( p_vout->p_subpicture[i_index].i_start
613 < p_ephemer->i_start )
615 /* Link the previous ephemer subpicture and
616 * replace it with the current one */
617 p_ephemer->p_next = p_subpic;
618 p_subpic = p_ephemer;
619 p_ephemer = &p_vout->p_subpicture[i_index];
621 /* If it's the 2nd youngest subpicture,
622 * register its date */
624 || ephemer_date > p_subpic->i_start )
626 ephemer_date = p_subpic->i_start;
633 p_vout->p_subpicture[i_index].p_next = p_subpic;
634 p_subpic = &p_vout->p_subpicture[i_index];
636 /* If it's the 2nd youngest subpicture, register its date */
637 if( !ephemer_date || ephemer_date > p_subpic->i_start )
639 ephemer_date = p_subpic->i_start;
642 /* If it's not a DVD subpicture, just register it */
645 p_vout->p_subpicture[i_index].p_next = p_subpic;
646 p_subpic = &p_vout->p_subpicture[i_index];
651 /* If we found an ephemer subpicture, check if it has to be
653 if( p_ephemer != NULL )
655 if( p_ephemer->i_start <= ephemer_date )
657 /* Ephemer subpicture has lived too long */
658 vout_DestroySubPicture( p_vout, p_ephemer );
662 /* Ephemer subpicture can still live a bit */
663 p_ephemer->p_next = p_subpic;
671 /*****************************************************************************
672 * vout_RegisterOSDChannel: register an OSD channel
673 *****************************************************************************
674 * This function affects an ID to an OSD channel
675 *****************************************************************************/
676 int vout_RegisterOSDChannel( vout_thread_t *p_vout )
678 msg_Dbg( p_vout, "Registering OSD channel, ID: %i",
679 p_vout->i_channel_count + 1 );
680 return ++p_vout->i_channel_count;
683 /*****************************************************************************
684 * vout_ClearOSDChannel: clear an OSD channel
685 *****************************************************************************
686 * This function destroys the subpictures which belong to the OSD channel
687 * corresponding to i_channel_id.
688 *****************************************************************************/
689 void vout_ClearOSDChannel( vout_thread_t *p_vout, int i_channel )
691 int i_subpic; /* subpicture index */
692 subpicture_t * p_subpic = NULL; /* first free subpicture */
694 if( i_channel == DEFAULT_CHAN )
696 if( p_vout->p_default_channel != NULL )
698 vout_DestroySubPicture( p_vout, p_vout->p_default_channel );
700 p_vout->p_default_channel = NULL;
704 vlc_mutex_lock( &p_vout->subpicture_lock );
706 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
708 p_subpic = &p_vout->p_subpicture[i_subpic];
709 if( p_subpic->i_status == FREE_SUBPICTURE
710 || ( p_subpic->i_status != RESERVED_SUBPICTURE
711 && p_subpic->i_status != READY_SUBPICTURE ) )
716 if( p_subpic->i_channel == i_channel )
718 while( p_subpic->p_region )
720 subpicture_region_t *p_region = p_subpic->p_region;
721 p_subpic->p_region = p_region->p_next;
722 spu_DestroyRegion( p_vout, p_region );
725 if( p_subpic->pf_destroy )
727 p_subpic->pf_destroy( p_subpic );
729 p_subpic->i_status = FREE_SUBPICTURE;
733 vlc_mutex_unlock( &p_vout->subpicture_lock );
736 /*****************************************************************************
737 * UpdateSPU: update subpicture settings
738 *****************************************************************************
739 * This function is called from CropCallback and at initialization time, to
740 * retrieve crop information from the input.
741 *****************************************************************************/
742 static void UpdateSPU( vout_thread_t *p_vout, vlc_object_t *p_object )
746 p_vout->b_force_alpha = VLC_FALSE;
747 p_vout->b_force_crop = VLC_FALSE;
749 if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return;
751 p_vout->b_force_crop = VLC_TRUE;
752 var_Get( p_object, "x-start", &val );
753 p_vout->i_crop_x = val.i_int;
754 var_Get( p_object, "y-start", &val );
755 p_vout->i_crop_y = val.i_int;
756 var_Get( p_object, "x-end", &val );
757 p_vout->i_crop_width = val.i_int - p_vout->i_crop_x;
758 var_Get( p_object, "y-end", &val );
759 p_vout->i_crop_height = val.i_int - p_vout->i_crop_y;
762 if( var_Get( p_object, "color", &val ) == VLC_SUCCESS )
765 for( i = 0; i < 4; i++ )
767 p_vout->pi_color[i] = ((uint8_t *)val.p_address)[i];
772 if( var_Get( p_object, "contrast", &val ) == VLC_SUCCESS )
775 for( i = 0; i < 4; i++ )
777 p_vout->pi_alpha[i] = ((uint8_t *)val.p_address)[i];
778 p_vout->pi_alpha[i] = p_vout->pi_alpha[i] == 0xf ?
779 0xff : p_vout->pi_alpha[i] << 4;
781 p_vout->b_force_alpha = VLC_TRUE;
784 msg_Dbg( p_vout, "crop: %i,%i,%i,%i, alpha: %i",
785 p_vout->i_crop_x, p_vout->i_crop_y,
786 p_vout->i_crop_width, p_vout->i_crop_height,
787 p_vout->b_force_alpha );
790 /*****************************************************************************
791 * CropCallback: called when the highlight properties are changed
792 *****************************************************************************
793 * This callback is called from the input thread when we need cropping
794 *****************************************************************************/
795 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
796 vlc_value_t oldval, vlc_value_t newval, void *p_data )
798 UpdateSPU( (vout_thread_t *)p_data, p_object );
802 /*****************************************************************************
803 * Buffers allocation callbacks for the filters
804 *****************************************************************************/
805 static subpicture_t *spu_new_buffer( filter_t *p_filter )
807 subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t));
808 memset( p_subpic, 0, sizeof(subpicture_t) );
809 p_subpic->b_absolute = VLC_TRUE;
811 p_subpic->pf_create_region = __spu_CreateRegion;
812 p_subpic->pf_destroy_region = __spu_DestroyRegion;
817 static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
819 while( p_subpic->p_region )
821 subpicture_region_t *p_region = p_subpic->p_region;
822 p_subpic->p_region = p_region->p_next;
823 p_subpic->pf_destroy_region( VLC_OBJECT(p_filter), p_region );