/*****************************************************************************
- * video_spu.h : DVD subpicture units functions
+ * video_spu.c : DVD subpicture units functions
*****************************************************************************
- * Copyright (C) 1999, 2000 VideoLAN
- *
- * Authors:
+ * Copyright (C) 1999-2001 VideoLAN
+ * $Id: video_spu.c,v 1.22 2001/11/28 15:08:06 massiot Exp $
*
+ * Authors: Samuel Hocevar <sam@zoy.org>
+ *
* 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
#include "defs.h"
#include <stdio.h>
+#include <string.h> /* memcpy(), memset() */
#include "config.h"
#include "common.h"
+#include "intf_msg.h"
#include "threads.h"
-#include "plugins.h"
#include "mtime.h"
+
#include "video.h"
#include "video_output.h"
#include "video_spu.h"
-#include "intf_msg.h"
-
-typedef struct vout_spu_s
-{
- int i_id;
- byte_t *p_data;
-
- /* drawing coordinates inside the spu */
- int i_x;
- int i_y;
- /* target size */
- int i_width;
- int i_height;
-
-} vout_spu_t;
-
-static int NewLine ( vout_spu_t *p_vspu, int *i_id );
-
-/* i = get_nibble(); */
-#define GET_NIBBLE( i ) \
- if( b_aligned ) \
- { \
- i_next = *p_from[i_id]; \
- p_from[ i_id ]++; \
- b_aligned = 0; \
- i = i_next >> 4; \
- } \
- else \
- { \
- b_aligned = 1; \
- i = i_next & 0xf; \
- }
-
-/* i = j + get_nibble(); */
-#define ADD_NIBBLE( i, j ) \
- if( b_aligned ) \
- { \
- i_next = *p_from[i_id]; \
- p_from[ i_id ]++; \
- b_aligned = 0; \
- i = (j) + (i_next >> 4); \
- } \
- else \
- { \
- b_aligned = 1; \
- i = (j) + (i_next & 0xf); \
- }
+/* FIXME: fake palette - the real one has to be sought in the .IFO */
+static int p_palette[4] = { 0x0000, 0xffff, 0x5555, 0x8888 };
/*****************************************************************************
- * vout_RenderSPU: draws an SPU on a picture
+ * vout_RenderRGBSPU: draw an SPU on a picture
*****************************************************************************
- *
+ * This is a fast implementation of the subpicture drawing code. The data
+ * has been preprocessed once in spu_decoder.c, so we don't need to parse the
+ * RLE buffer again and again. Most sanity checks are done in spu_decoder.c
+ * so that this routine can be as fast as possible.
*****************************************************************************/
-void vout_RenderSPU( vout_buffer_t *p_buffer, subpicture_t *p_subpic,
- int i_bytes_per_pixel, int i_bytes_per_line )
+void vout_RenderRGBSPU( picture_t *p_pic, const subpicture_t *p_spu,
+ vout_buffer_t *p_buffer,
+ int i_bytes_per_pixel, int i_bytes_per_line )
{
- int i_code = 0x00;
- int i_next = 0;
- int i_id = 0;
- int i_color;
-
- /* FIXME: we need a way to get this information from the stream */
- #define TARGET_WIDTH 720
- #define TARGET_HEIGHT 576
- int i_x_scale = ( p_buffer->i_pic_width << 6 ) / TARGET_WIDTH;
- int i_y_scale = ( p_buffer->i_pic_height << 6 ) / TARGET_HEIGHT;
-
- /* FIXME: fake palette - the real one has to be sought in the .IFO */
- static int p_palette[4] = { 0x0000, 0x0000, 0x5555, 0xffff };
-
- boolean_t b_aligned = 1;
- byte_t *p_from[2];
- vout_spu_t vspu;
-
- p_from[1] = p_subpic->p_data + p_subpic->type.spu.i_offset[1];
- p_from[0] = p_subpic->p_data + p_subpic->type.spu.i_offset[0];
-
- vspu.i_x = 0;
- vspu.i_y = 0;
- vspu.i_width = TARGET_WIDTH;
- vspu.i_height = TARGET_HEIGHT;
- vspu.p_data = p_buffer->p_data
- /* add the picture coordinates and the SPU coordinates */
- + ( p_buffer->i_pic_x + ((p_subpic->i_x * i_x_scale) >> 6))
- * i_bytes_per_pixel
- + ( p_buffer->i_pic_y + ((p_subpic->i_y * i_y_scale) >> 6))
- * i_bytes_per_line;
-
- while( p_from[0] < (byte_t *)p_subpic->p_data
- + p_subpic->type.spu.i_offset[1] )
+ int i_len, i_color;
+ u16 *p_source = (u16 *)p_spu->p_data;
+
+ int i_xscale = ( p_buffer->i_pic_width << 6 ) / p_pic->i_width;
+ int i_yscale = ( p_buffer->i_pic_height << 6 ) / p_pic->i_height;
+
+ int i_width = p_spu->i_width * i_xscale;
+ int i_height = p_spu->i_height * i_yscale;
+
+ int i_x, i_y, i_ytmp, i_yreal, i_ynext;
+
+ u8 *p_dest = p_buffer->p_data + ( i_width >> 6 ) * i_bytes_per_pixel
+ /* Add the picture coordinates and the SPU coordinates */
+ + ( p_buffer->i_pic_x + ((p_spu->i_x * i_xscale) >> 6))
+ * i_bytes_per_pixel
+ + ( p_buffer->i_pic_y + ((p_spu->i_y * i_yscale) >> 6))
+ * i_bytes_per_line;
+
+ /* Draw until we reach the bottom of the subtitle */
+ i_y = 0;
+
+ while( i_y < i_height )
{
- GET_NIBBLE( i_code );
+ i_ytmp = i_y >> 6;
+ i_y += i_yscale;
- if( i_code >= 0x04 )
+ /* Check whether we need to draw one line or more than one */
+ if( i_ytmp + 1 >= ( i_y >> 6 ) )
{
- found_code:
+ /* Just one line : we precalculate i_y >> 6 */
+ i_yreal = i_bytes_per_line * i_ytmp;
- if( ((i_code >> 2) + vspu.i_x + vspu.i_y * vspu.i_width)
- > vspu.i_height * vspu.i_width )
- {
- intf_DbgMsg ( "video_spu: invalid draw request ! %d %d\n",
- i_code >> 2, vspu.i_height * vspu.i_width
- - ( (i_code >> 2) + vspu.i_x
- + vspu.i_y * vspu.i_width ) );
- return;
- }
- else
+ /* Draw until we reach the end of the line */
+ i_x = i_width;
+
+ while( i_x )
{
- if( (i_color = i_code & 0x3) )
+ /* Get the RLE part */
+ i_color = *p_source & 0x3;
+
+ /* Draw the line */
+ if( i_color )
{
- u8 *p_target = &vspu.p_data[
- i_bytes_per_pixel * ((vspu.i_x * i_x_scale) >> 6)
- + i_bytes_per_line * ((vspu.i_y * i_y_scale) >> 6) ];
-
- memset( p_target, p_palette[i_color],
- ((((i_code - 1) * i_x_scale) >> 8) + 1)
- * i_bytes_per_pixel );
-
- /* if we need some horizontal scaling (unlikely )
- * we only scale up to 2x, someone watching a DVD
- * with more than 2x zoom must be braindead */
- if( i_y_scale >= (1 << 6) )
- {
- p_target += i_bytes_per_line;
+ i_len = i_xscale * ( *p_source++ >> 2 );
- memset( p_target, p_palette[i_color],
- ((((i_code - 1) * i_x_scale) >> 8) + 1)
- * i_bytes_per_pixel );
- }
+ memset( p_dest - i_bytes_per_pixel * ( i_x >> 6 )
+ + i_yreal,
+ p_palette[ i_color ],
+ i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) );
+
+ i_x -= i_len;
+ continue;
}
- vspu.i_x += i_code >> 2;
- }
- if( vspu.i_x >= vspu.i_width )
- {
- /* byte-align the stream */
- b_aligned = 1;
- /* finish the line */
- NewLine( &vspu, &i_id );
+ i_x -= i_xscale * ( *p_source++ >> 2 );
}
- continue;
}
+ else
+ {
+ i_yreal = i_bytes_per_line * i_ytmp;
+ i_ynext = i_bytes_per_line * i_y >> 6;
- ADD_NIBBLE( i_code, (i_code << 4) );
- if( i_code >= 0x10 ) /* 00 11 xx cc */
- goto found_code; /* 00 01 xx cc */
+ /* Draw until we reach the end of the line */
+ i_x = i_width;
- ADD_NIBBLE( i_code, (i_code << 4) );
- if( i_code >= 0x040 ) /* 00 00 11 xx xx cc */
- goto found_code; /* 00 00 01 xx xx cc */
+ while( i_x )
+ {
+ /* Get the RLE part */
+ i_color = *p_source & 0x3;
- ADD_NIBBLE( i_code, (i_code << 4) );
- if( i_code >= 0x0100 ) /* 00 00 00 11 xx xx xx cc */
- goto found_code; /* 00 00 00 01 xx xx xx cc */
+ /* Draw as many lines as needed */
+ if( i_color )
+ {
+ i_len = i_xscale * ( *p_source++ >> 2 );
- /* if the 14 first bits are 0, then it's a newline */
- if( i_code <= 0x0003 )
- {
- if( NewLine( &vspu, &i_id ) < 0 )
- return;
+ for( i_ytmp = i_yreal ;
+ i_ytmp < i_ynext ;
+ i_ytmp += i_bytes_per_line )
+ {
+ memset( p_dest - i_bytes_per_pixel * ( i_x >> 6 )
+ + i_ytmp,
+ p_palette[ i_color ],
+ i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) );
+ }
- if( !b_aligned )
- b_aligned = 1;
- }
- else
- {
- /* we have a boo boo ! */
- intf_DbgMsg( "video_spu: unknown code 0x%x "
- "(dest %x side %x, x=%d, y=%d)\n",
- i_code, p_from[i_id], i_id, vspu.i_x, vspu.i_y );
- if( NewLine( &vspu, &i_id ) < 0 )
- return;
- continue;
+ i_x -= i_len;
+ continue;
+ }
+
+ i_x -= i_xscale * ( *p_source++ >> 2 );
+ }
}
}
}
-static int NewLine( vout_spu_t *p_vspu, int *i_id )
+/*****************************************************************************
+ * vout_RenderYUVSPU: draw an SPU on an YUV overlay
+ *****************************************************************************
+ * This is a fast implementation of the subpicture drawing code. The data
+ * has been preprocessed once in spu_decoder.c, so we don't need to parse the
+ * RLE buffer again and again. Most sanity checks are done in spu_decoder.c
+ * so that this routine can be as fast as possible.
+ *****************************************************************************/
+void vout_RenderYUVSPU( picture_t *p_pic, const subpicture_t *p_spu )
{
- *i_id = 1 - *i_id;
+ int i_len, i_color;
+ u16 *p_source = (u16 *)p_spu->p_data;
+
+ int i_x, i_y;
+
+ u8 *p_dest = p_pic->p_y + p_spu->i_x + p_spu->i_width
+ + p_pic->i_width * ( p_spu->i_y + p_spu->i_height );
- p_vspu->i_x = 0;
- p_vspu->i_y++;
+ /* Draw until we reach the bottom of the subtitle */
+ i_y = p_spu->i_height * p_pic->i_width;
- return( p_vspu->i_width - p_vspu->i_y );
+ while( i_y )
+ {
+ /* Draw until we reach the end of the line */
+ i_x = p_spu->i_width;
+
+ while( i_x )
+ {
+ /* Draw the line if needed */
+ i_color = *p_source & 0x3;
+ if( i_color )
+ {
+ i_len = *p_source++ >> 2;
+ memset( p_dest - i_x - i_y, p_palette[ i_color ], i_len );
+ i_x -= i_len;
+ continue;
+ }
+
+ i_x -= *p_source++ >> 2;
+ }
+
+ i_y -= p_pic->i_width;
+ }
}