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;
71 vout_AttachSPU( p_vout, VLC_TRUE );
75 * Destroy the subpicture decoder unit
77 * \param p_vout the vout in which to destroy the subpicture unit
79 void vout_DestroySPU( vout_thread_t *p_vout )
83 /* Destroy all remaining subpictures */
84 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
86 if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
88 vout_DestroySubPicture( p_vout,
89 &p_vout->p_subpicture[i_index] );
95 if( p_vout->p_blend->p_module )
96 module_Unneed( p_vout->p_blend, p_vout->p_blend->p_module );
98 vlc_object_detach( p_vout->p_blend );
99 vlc_object_destroy( p_vout->p_blend );
104 if( p_vout->p_text->p_module )
105 module_Unneed( p_vout->p_text, p_vout->p_text->p_module );
107 vlc_object_detach( p_vout->p_text );
108 vlc_object_destroy( p_vout->p_text );
111 vout_AttachSPU( p_vout, VLC_FALSE );
115 * Attach/Detach the SPU from any input
117 * \param p_vout the vout in which to destroy the subpicture unit
118 * \param b_attach to select attach or detach
120 void vout_AttachSPU( vout_thread_t *p_vout, vlc_bool_t b_attach )
122 vlc_object_t *p_input;
124 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
125 if( !p_input ) return;
129 UpdateSPU( p_vout, VLC_OBJECT(p_input) );
130 var_AddCallback( p_input, "highlight", CropCallback, p_vout );
131 vlc_object_release( p_input );
135 /* Delete callback */
136 var_DelCallback( p_input, "highlight", CropCallback, p_vout );
137 vlc_object_release( p_input );
143 * Create a subpicture region
145 * \param p_this vlc_object_t
146 * \param p_fmt the format that this subpicture region should have
148 subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this,
149 video_format_t *p_fmt )
151 subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
152 memset( p_region, 0, sizeof(subpicture_region_t) );
153 p_region->p_next = 0;
154 p_region->fmt = *p_fmt;
155 p_region->psz_text = 0;
157 if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
158 p_fmt->p_palette = p_region->fmt.p_palette =
159 malloc( sizeof(video_palette_t) );
160 else p_fmt->p_palette = p_region->fmt.p_palette = NULL;
162 p_region->picture.p_data_orig = 0;
164 if( p_fmt->i_chroma == VLC_FOURCC('T','E','X','T') ) return p_region;
166 vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma,
167 p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
169 if( !p_region->picture.i_planes )
172 free( p_fmt->p_palette );
180 * Destroy a subpicture region
182 * \param p_this vlc_object_t
183 * \param p_region the subpicture region to destroy
185 void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region )
187 if( !p_region ) return;
188 if( p_region->picture.p_data_orig ) free( p_region->picture.p_data_orig );
189 if( p_region->fmt.p_palette ) free( p_region->fmt.p_palette );
190 if( p_region->psz_text ) free( p_region->psz_text );
195 * Display a subpicture unit
197 * Remove the reservation flag of a subpicture, which will cause it to be
199 * \param p_vout the video output this subpicture should be displayed on
200 * \param p_subpic the subpicture to display
202 void vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
206 /* Check if status is valid */
207 if( p_subpic->i_status != RESERVED_SUBPICTURE )
209 msg_Err( p_vout, "subpicture %p has invalid status #%d",
210 p_subpic, p_subpic->i_status );
213 /* If the user requested an SPU margin, we force the position after
214 * having checked that it was a valid value. */
215 i_margin = config_GetInt( p_vout, "spumargin" );
219 if( p_subpic->i_height + (unsigned int)i_margin
220 <= p_vout->output.i_height )
222 p_subpic->i_y = p_vout->output.i_height
223 - i_margin - p_subpic->i_height;
227 /* Remove reservation flag */
228 p_subpic->i_status = READY_SUBPICTURE;
232 * Allocate a subpicture in the video output heap.
234 * This function create a reserved subpicture in the video output heap.
235 * A null pointer is returned if the function fails. This method provides an
236 * already allocated zone of memory in the spu data fields. It needs locking
237 * since several pictures can be created by several producers threads.
238 * \param p_vout the vout in which to create the subpicture
239 * \param i_channel the channel this subpicture should belong to
240 * \param i_type the type of the subpicture
241 * \return NULL on error, a reserved subpicture otherwise
243 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_channel,
246 int i_subpic; /* subpicture index */
247 subpicture_t * p_subpic = NULL; /* first free subpicture */
249 /* Clear the default channel before writing into it */
250 if( i_channel == DEFAULT_CHAN )
252 vout_ClearOSDChannel( p_vout, DEFAULT_CHAN );
256 vlc_mutex_lock( &p_vout->subpicture_lock );
259 * Look for an empty place
262 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
264 if( p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
266 /* Subpicture is empty and ready for allocation */
267 p_subpic = &p_vout->p_subpicture[i_subpic];
268 p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
273 /* If no free subpicture could be found */
274 if( p_subpic == NULL )
276 msg_Err( p_vout, "subpicture heap is full" );
277 vlc_mutex_unlock( &p_vout->subpicture_lock );
281 /* Copy subpicture information, set some default values */
282 p_subpic->i_channel = i_channel;
283 p_subpic->i_type = i_type;
284 p_subpic->i_status = RESERVED_SUBPICTURE;
286 p_subpic->i_start = 0;
287 p_subpic->i_stop = 0;
288 p_subpic->b_ephemer = VLC_FALSE;
292 p_subpic->i_width = 0;
293 p_subpic->i_height = 0;
294 p_subpic->b_absolute= VLC_TRUE;
295 p_subpic->i_flags = 0;
296 p_subpic->pf_render = 0;
297 p_subpic->pf_destroy= 0;
300 /* Remain last subpicture displayed in DEFAULT_CHAN */
301 if( i_channel == DEFAULT_CHAN )
303 p_vout->p_default_channel = p_subpic;
306 vlc_mutex_unlock( &p_vout->subpicture_lock );
308 p_subpic->pf_create_region = __spu_CreateRegion;
309 p_subpic->pf_destroy_region = __spu_DestroyRegion;
315 * Remove a subpicture from the heap
317 * This function frees a previously reserved subpicture.
318 * It is meant to be used when the construction of a picture aborted.
319 * This function does not need locking since reserved subpictures are ignored
320 * by the output thread.
322 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
325 vlc_mutex_lock( &p_vout->subpicture_lock );
327 /* There can be race conditions so we need to check the status */
328 if( p_subpic->i_status == FREE_SUBPICTURE )
330 vlc_mutex_unlock( &p_vout->subpicture_lock );
334 /* Check if status is valid */
335 if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
336 && ( p_subpic->i_status != READY_SUBPICTURE ) )
338 msg_Err( p_vout, "subpicture %p has invalid status %d",
339 p_subpic, p_subpic->i_status );
342 while( p_subpic->p_region )
344 subpicture_region_t *p_region = p_subpic->p_region;
345 p_subpic->p_region = p_region->p_next;
346 spu_DestroyRegion( p_vout, p_region );
349 if( p_subpic->pf_destroy )
351 p_subpic->pf_destroy( p_subpic );
354 p_subpic->i_status = FREE_SUBPICTURE;
356 vlc_mutex_unlock( &p_vout->subpicture_lock );
359 /*****************************************************************************
360 * vout_RenderSubPictures: render a subpicture list
361 *****************************************************************************
362 * This function renders all sub picture units in the list.
363 *****************************************************************************/
364 void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic_dst,
365 picture_t *p_pic_src, subpicture_t *p_subpic )
368 vlc_mutex_lock( &p_vout->subpicture_lock );
370 /* Check i_status again to make sure spudec hasn't destroyed the subpic */
371 while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE )
373 subpicture_region_t *p_region = p_subpic->p_region;
375 /* Load the blending module */
376 if( !p_vout->p_blend && p_region )
378 p_vout->p_blend = vlc_object_create( p_vout, sizeof(filter_t) );
379 vlc_object_attach( p_vout->p_blend, p_vout );
380 p_vout->p_blend->fmt_out.video.i_x_offset =
381 p_vout->p_blend->fmt_out.video.i_y_offset = 0;
382 p_vout->p_blend->fmt_out.video.i_aspect =
383 p_vout->render.i_aspect;
384 p_vout->p_blend->fmt_out.video.i_chroma =
385 p_vout->output.i_chroma;
387 p_vout->p_blend->fmt_in.video.i_chroma = VLC_FOURCC('Y','U','V','P');
389 p_vout->p_blend->p_module =
390 module_Need( p_vout->p_blend, "video blending", 0, 0 );
393 /* Load the text rendering module */
394 if( !p_vout->p_text && p_region )
396 p_vout->p_text = vlc_object_create( p_vout, sizeof(filter_t) );
397 vlc_object_attach( p_vout->p_text, p_vout );
399 p_vout->p_text->fmt_out.video.i_width =
400 p_vout->p_text->fmt_out.video.i_visible_width =
401 p_vout->output.i_width;
402 p_vout->p_text->fmt_out.video.i_height =
403 p_vout->p_text->fmt_out.video.i_visible_height =
404 p_vout->output.i_height;
406 p_vout->p_text->pf_spu_buffer_new = spu_new_buffer;
407 p_vout->p_text->pf_spu_buffer_del = spu_del_buffer;
409 p_vout->p_text->p_module =
410 module_Need( p_vout->p_text, "text renderer", 0, 0 );
413 if( p_subpic->pf_render )
415 p_subpic->pf_render( p_vout, p_pic_dst, p_subpic );
417 else while( p_region && p_vout->p_blend &&
418 p_vout->p_blend->pf_video_blend )
420 int i_x_offset = p_region->i_x + p_subpic->i_x;
421 int i_y_offset = p_region->i_y + p_subpic->i_y;
423 if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
425 if( p_vout->p_text && p_vout->p_text->p_module &&
426 p_vout->p_text->pf_render_string )
428 /* TODO: do it in a less hacky way
429 * (modify text renderer API) */
431 subpicture_region_t tmp_region;
432 block_t *p_new_block =
433 block_New( p_vout, strlen(p_region->psz_text) + 1 );
437 memcpy( p_new_block->p_buffer, p_region->psz_text,
438 p_new_block->i_buffer );
439 p_new_block->i_pts = p_new_block->i_dts =
441 p_new_block->i_length =
442 p_subpic->i_start - p_subpic->i_stop;
443 p_spu = p_vout->p_text->pf_render_string(
444 p_vout->p_text, p_new_block );
448 tmp_region = *p_region;
449 *p_region = *p_spu->p_region;
450 p_region->p_next = tmp_region.p_next;
451 *p_spu->p_region = tmp_region;
452 p_vout->p_text->pf_spu_buffer_del( p_vout->p_text,
459 if( p_subpic->i_flags & OSD_ALIGN_BOTTOM )
461 i_y_offset = p_vout->output.i_height - p_region->fmt.i_height -
464 else if ( !(p_subpic->i_flags & OSD_ALIGN_TOP) )
466 i_y_offset = p_vout->output.i_height / 2 -
467 p_region->fmt.i_height / 2;
470 if( p_subpic->i_flags & OSD_ALIGN_RIGHT )
472 i_x_offset = p_vout->output.i_width - p_region->fmt.i_width -
475 else if ( !(p_subpic->i_flags & OSD_ALIGN_LEFT) )
477 i_x_offset = p_vout->output.i_width / 2 -
478 p_region->fmt.i_width / 2;
481 if( p_subpic->b_absolute )
483 i_x_offset = p_region->i_x + p_subpic->i_x;
484 i_y_offset = p_region->i_y + p_subpic->i_y;
487 p_vout->p_blend->fmt_in.video = p_region->fmt;
489 /* Force cropping if requested */
490 if( p_vout->b_force_crop )
492 video_format_t *p_fmt = &p_vout->p_blend->fmt_in.video;
494 /* Find the intersection */
495 if( p_vout->i_crop_x + p_vout->i_crop_width <= i_x_offset ||
496 i_x_offset + (int)p_fmt->i_visible_width <
498 p_vout->i_crop_y + p_vout->i_crop_height <= i_y_offset ||
499 i_y_offset + (int)p_fmt->i_visible_height <
502 /* No intersection */
503 p_fmt->i_visible_width = p_fmt->i_visible_height = 0;
507 int i_x, i_y, i_x_end, i_y_end;
508 i_x = __MAX( p_vout->i_crop_x, i_x_offset );
509 i_y = __MAX( p_vout->i_crop_y, i_y_offset );
510 i_x_end = __MIN( p_vout->i_crop_x + p_vout->i_crop_width,
511 i_x_offset + (int)p_fmt->i_visible_width );
512 i_y_end = __MIN( p_vout->i_crop_y + p_vout->i_crop_height,
513 i_y_offset + (int)p_fmt->i_visible_height );
515 p_fmt->i_x_offset = i_x - i_x_offset;
516 p_fmt->i_y_offset = i_y - i_y_offset;
517 p_fmt->i_visible_width = i_x_end - i_x;
518 p_fmt->i_visible_height = i_y_end - i_y;
525 /* Force palette if requested */
526 if( p_vout->b_force_alpha && VLC_FOURCC('Y','U','V','P') ==
527 p_vout->p_blend->fmt_in.video.i_chroma )
529 p_vout->p_blend->fmt_in.video.p_palette->palette[0][3] =
531 p_vout->p_blend->fmt_in.video.p_palette->palette[1][3] =
533 p_vout->p_blend->fmt_in.video.p_palette->palette[2][3] =
535 p_vout->p_blend->fmt_in.video.p_palette->palette[3][3] =
539 /* Update the output picture size */
540 p_vout->p_blend->fmt_out.video.i_width =
541 p_vout->p_blend->fmt_out.video.i_visible_width =
542 p_vout->output.i_width;
543 p_vout->p_blend->fmt_out.video.i_height =
544 p_vout->p_blend->fmt_out.video.i_visible_height =
545 p_vout->output.i_height;
547 p_vout->p_blend->pf_video_blend( p_vout->p_blend, p_pic_dst,
548 p_pic_src, &p_region->picture, i_x_offset, i_y_offset );
550 p_region = p_region->p_next;
553 p_subpic = p_subpic->p_next;
556 vlc_mutex_unlock( &p_vout->subpicture_lock );
559 /*****************************************************************************
560 * vout_SortSubPictures: find the subpictures to display
561 *****************************************************************************
562 * This function parses all subpictures and decides which ones need to be
563 * displayed. This operation does not need lock, since only READY_SUBPICTURE
564 * are handled. If no picture has been selected, display_date will depend on
566 * We also check for ephemer DVD subpictures (subpictures that have
567 * to be removed if a newer one is available), which makes it a lot
568 * more difficult to guess if a subpicture has to be rendered or not.
569 *****************************************************************************/
570 subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
571 mtime_t display_date )
574 subpicture_t *p_subpic = NULL;
575 subpicture_t *p_ephemer = NULL;
576 mtime_t ephemer_date = 0;
578 /* We get an easily parsable chained list of subpictures which
579 * ends with NULL since p_subpic was initialized to NULL. */
580 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
582 if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
584 /* If it is a DVD subpicture, check its date */
585 if( p_vout->p_subpicture[i_index].i_type == MEMORY_SUBPICTURE )
587 if( !p_vout->p_subpicture[i_index].b_ephemer
588 && display_date > p_vout->p_subpicture[i_index].i_stop )
590 /* Too late, destroy the subpic */
591 vout_DestroySubPicture( p_vout,
592 &p_vout->p_subpicture[i_index] );
597 && display_date < p_vout->p_subpicture[i_index].i_start )
599 /* Too early, come back next monday */
603 /* If this is an ephemer subpic, see if it's the
604 * youngest we have */
605 if( p_vout->p_subpicture[i_index].b_ephemer )
607 if( p_ephemer == NULL )
609 p_ephemer = &p_vout->p_subpicture[i_index];
613 if( p_vout->p_subpicture[i_index].i_start
614 < p_ephemer->i_start )
616 /* Link the previous ephemer subpicture and
617 * replace it with the current one */
618 p_ephemer->p_next = p_subpic;
619 p_subpic = p_ephemer;
620 p_ephemer = &p_vout->p_subpicture[i_index];
622 /* If it's the 2nd youngest subpicture,
623 * register its date */
625 || ephemer_date > p_subpic->i_start )
627 ephemer_date = p_subpic->i_start;
634 p_vout->p_subpicture[i_index].p_next = p_subpic;
635 p_subpic = &p_vout->p_subpicture[i_index];
637 /* If it's the 2nd youngest subpicture, register its date */
638 if( !ephemer_date || ephemer_date > p_subpic->i_start )
640 ephemer_date = p_subpic->i_start;
643 /* If it's not a DVD subpicture, just register it */
646 p_vout->p_subpicture[i_index].p_next = p_subpic;
647 p_subpic = &p_vout->p_subpicture[i_index];
652 /* If we found an ephemer subpicture, check if it has to be
654 if( p_ephemer != NULL )
656 if( p_ephemer->i_start <= ephemer_date )
658 /* Ephemer subpicture has lived too long */
659 vout_DestroySubPicture( p_vout, p_ephemer );
663 /* Ephemer subpicture can still live a bit */
664 p_ephemer->p_next = p_subpic;
672 /*****************************************************************************
673 * vout_RegisterOSDChannel: register an OSD channel
674 *****************************************************************************
675 * This function affects an ID to an OSD channel
676 *****************************************************************************/
677 int vout_RegisterOSDChannel( vout_thread_t *p_vout )
679 msg_Dbg( p_vout, "Registering OSD channel, ID: %i",
680 p_vout->i_channel_count + 1 );
681 return ++p_vout->i_channel_count;
684 /*****************************************************************************
685 * vout_ClearOSDChannel: clear an OSD channel
686 *****************************************************************************
687 * This function destroys the subpictures which belong to the OSD channel
688 * corresponding to i_channel_id.
689 *****************************************************************************/
690 void vout_ClearOSDChannel( vout_thread_t *p_vout, int i_channel )
692 int i_subpic; /* subpicture index */
693 subpicture_t * p_subpic = NULL; /* first free subpicture */
695 if( i_channel == DEFAULT_CHAN )
697 if( p_vout->p_default_channel != NULL )
699 vout_DestroySubPicture( p_vout, p_vout->p_default_channel );
701 p_vout->p_default_channel = NULL;
705 vlc_mutex_lock( &p_vout->subpicture_lock );
707 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
709 p_subpic = &p_vout->p_subpicture[i_subpic];
710 if( p_subpic->i_status == FREE_SUBPICTURE
711 || ( p_subpic->i_status != RESERVED_SUBPICTURE
712 && p_subpic->i_status != READY_SUBPICTURE ) )
717 if( p_subpic->i_channel == i_channel )
719 while( p_subpic->p_region )
721 subpicture_region_t *p_region = p_subpic->p_region;
722 p_subpic->p_region = p_region->p_next;
723 spu_DestroyRegion( p_vout, p_region );
726 if( p_subpic->pf_destroy )
728 p_subpic->pf_destroy( p_subpic );
730 p_subpic->i_status = FREE_SUBPICTURE;
734 vlc_mutex_unlock( &p_vout->subpicture_lock );
737 /*****************************************************************************
738 * UpdateSPU: update subpicture settings
739 *****************************************************************************
740 * This function is called from CropCallback and at initialization time, to
741 * retrieve crop information from the input.
742 *****************************************************************************/
743 static void UpdateSPU( vout_thread_t *p_vout, vlc_object_t *p_object )
747 p_vout->b_force_alpha = VLC_FALSE;
748 p_vout->b_force_crop = VLC_FALSE;
750 if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return;
752 p_vout->b_force_crop = VLC_TRUE;
753 var_Get( p_object, "x-start", &val );
754 p_vout->i_crop_x = val.i_int;
755 var_Get( p_object, "y-start", &val );
756 p_vout->i_crop_y = val.i_int;
757 var_Get( p_object, "x-end", &val );
758 p_vout->i_crop_width = val.i_int - p_vout->i_crop_x;
759 var_Get( p_object, "y-end", &val );
760 p_vout->i_crop_height = val.i_int - p_vout->i_crop_y;
763 if( var_Get( p_object, "color", &val ) == VLC_SUCCESS )
766 for( i = 0; i < 4; i++ )
768 p_vout->pi_color[i] = ((uint8_t *)val.p_address)[i];
773 if( var_Get( p_object, "contrast", &val ) == VLC_SUCCESS )
776 for( i = 0; i < 4; i++ )
778 p_vout->pi_alpha[i] = ((uint8_t *)val.p_address)[i];
779 p_vout->pi_alpha[i] = p_vout->pi_alpha[i] == 0xf ?
780 0xff : p_vout->pi_alpha[i] << 4;
782 p_vout->b_force_alpha = VLC_TRUE;
785 msg_Dbg( p_vout, "crop: %i,%i,%i,%i, alpha: %i",
786 p_vout->i_crop_x, p_vout->i_crop_y,
787 p_vout->i_crop_width, p_vout->i_crop_height,
788 p_vout->b_force_alpha );
791 /*****************************************************************************
792 * CropCallback: called when the highlight properties are changed
793 *****************************************************************************
794 * This callback is called from the input thread when we need cropping
795 *****************************************************************************/
796 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
797 vlc_value_t oldval, vlc_value_t newval, void *p_data )
799 UpdateSPU( (vout_thread_t *)p_data, p_object );
803 /*****************************************************************************
804 * Buffers allocation callbacks for the filters
805 *****************************************************************************/
806 static subpicture_t *spu_new_buffer( filter_t *p_filter )
808 subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t));
809 memset( p_subpic, 0, sizeof(subpicture_t) );
810 p_subpic->b_absolute = VLC_TRUE;
812 p_subpic->pf_create_region = __spu_CreateRegion;
813 p_subpic->pf_destroy_region = __spu_DestroyRegion;
818 static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
820 while( p_subpic->p_region )
822 subpicture_region_t *p_region = p_subpic->p_region;
823 p_subpic->p_region = p_region->p_next;
824 p_subpic->pf_destroy_region( VLC_OBJECT(p_filter), p_region );