]> git.sesse.net Git - vlc/blobdiff - src/video_output/video_spu.c
-Fixed a bug in area management added in my last commit
[vlc] / src / video_output / video_spu.c
index bc9aa9c9b71e996f1da16cca3f2cf88869fb9ec3..52cb742d09f445dcb3391040dacbf9f01e1067de 100644 (file)
@@ -1,11 +1,10 @@
 /*****************************************************************************
- * video_spu.h : DVD subpicture units functions
+ * video_spu.c : DVD subpicture units functions
  *****************************************************************************
  * Copyright (C) 1999, 2000 VideoLAN
+ * $Id: video_spu.c,v 1.20 2001/04/06 09:15:48 sam Exp $
  *
- * Authors:
- * Samuel "Sam" Hocevar <sam@via.ecp.fr>
- * Henri Fallon <henri@via.ecp.fr>
+ * 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
@@ -28,6 +27,7 @@
 #include "defs.h"
 
 #include <stdio.h>
+#include <string.h>                                    /* memcpy(), memset() */
 
 #include "config.h"
 #include "common.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_RenderSPU: 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,
+void vout_RenderSPU( vout_buffer_t *p_buffer, subpicture_t *p_spu,
                      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;
+    int  i_len, i_color;
+    u16 *p_source = (u16 *)p_spu->p_data;
 
-    /* FIXME: fake palette - the real one has to be sought in the .IFO */
-    static int p_palette[4] = { 0x0000, 0xffff, 0x5555, 0x8888 };
+    /* FIXME: we need a way to get 720 and 576 from the stream */
+    int i_xscale = ( p_buffer->i_pic_width << 6 ) / 720;
+    int i_yscale = ( p_buffer->i_pic_height << 6 ) / 576;
 
-    boolean_t b_aligned = 1;
-    byte_t *p_from[2];
-    vout_spu_t vspu;
+    int i_width  = p_spu->i_width  * i_xscale;
+    int i_height = p_spu->i_height * i_yscale;
 
-    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];
+    int i_x = 0, i_y = 0, i_ytmp, i_yreal, i_ynext;
 
-    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;
+    u8 *p_dest = p_buffer->p_data
+                  /* 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;
 
-    /* Do we need scaling ? 
-     * This is mostly dupliucate code except a few lines.
-     * This test was put out of the loop to avoid testing it
-     * each time.
-     */
-    if ( i_y_scale >= (1 << 6) )
+    /* Draw until we reach the bottom of the subtitle */
+    for( i_y = 0 ; i_y < i_height ; /* i_y incremented below */ )
     {
-        while( p_from[0] < (byte_t *)p_subpic->p_data
-                             + p_subpic->type.spu.i_offset[1] )
+        i_ytmp = i_y >> 6;
+        i_y += i_yscale;
+
+        /* Check whether we need to draw one line or more than one */
+        if( i_ytmp + 1 >= ( i_y >> 6 ) )
         {
-            GET_NIBBLE( i_code );
-    
-            if( i_code >= 0x04 )
+            /* Just one line : we precalculate i_y >> 6 */
+            i_yreal = i_bytes_per_line * i_ytmp;
+
+            /* Draw until we reach the end of the line */
+            for( i_x = 0 ; i_x < i_width ; i_x += i_len )
             {
-                found_code_with_scale:
-    
-                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",
-                                  i_code >> 2, vspu.i_height * vspu.i_width
-                                   - ( (i_code >> 2) + vspu.i_x
-                                       + vspu.i_y * vspu.i_width ) );
-                    return;
-                }
-                else
-                {
-                    if( (i_color = i_code & 0x3) )
-                    {
-                        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 );
-    
-                        /* here we need some horizontal scaling (unlikely )
-                         * we only scale up to 2x, someone watching a DVD
-                         * with more than 2x zoom must be braindead */
-                            p_target += i_bytes_per_line;
-    
-                            memset( p_target, p_palette[i_color],
-                                    ((((i_code - 1) * i_x_scale) >> 8) + 1)
-                                    * i_bytes_per_pixel );
-                    }
-                    vspu.i_x += i_code >> 2;
-                }
-    
-                if( vspu.i_x >= vspu.i_width )
+                /* Get RLE information */
+                i_len = i_xscale * ( *p_source >> 2 );
+                i_color = *p_source++ & 0x3;
+
+                /* Draw the line */
+                if( i_color )
                 {
-                    /* byte-align the stream */
-                    b_aligned = 1;
-                    /* finish the line */
-                    NewLine( &vspu, &i_id );
+                    memset( p_dest + i_bytes_per_pixel * ( i_x >> 6 )
+                                   + i_yreal,
+                            p_palette[ i_color ],
+                            i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) );
                 }
-                continue;
-            }
-    
-            ADD_NIBBLE( i_code, (i_code << 4) );
-            if( i_code >= 0x10 )   /* 00 11 xx cc */
-                goto found_code_with_scale;   /* 00 01 xx cc */
-    
-            ADD_NIBBLE( i_code, (i_code << 4) );
-            if( i_code >= 0x040 )  /* 00 00 11 xx xx cc */
-                goto found_code_with_scale;   /* 00 00 01 xx xx cc */
-    
-            ADD_NIBBLE( i_code, (i_code << 4) );
-            if( i_code >= 0x0100 ) /* 00 00 00 11 xx xx xx cc */
-                goto found_code_with_scale;   /* 00 00 00 01 xx xx xx cc */
-    
-            /* if the 14 first bits are 0, then it's a newline */
-            if( i_code <= 0x0003 )
-            {
-                if( NewLine( &vspu, &i_id ) < 0 )
-                    return;
-    
-                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)",
-                             i_code, p_from[i_id], i_id, vspu.i_x, vspu.i_y );
-                if( NewLine( &vspu, &i_id ) < 0 )
-                    return;
-                continue;
             }
         }
-    }
-    else
-    {
-        while( p_from[0] < (byte_t *)p_subpic->p_data
-                             + p_subpic->type.spu.i_offset[1] )
+        else
         {
-            GET_NIBBLE( i_code );
-    
-            if( i_code >= 0x04 )
+            i_yreal = i_bytes_per_line * i_ytmp;
+            i_ynext = i_bytes_per_line * i_y >> 6;
+
+            /* Draw until we reach the end of the line */
+            for( i_x = 0 ; i_x < i_width ; i_x += i_len )
             {
-                found_code:
-    
-                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",
-                                  i_code >> 2, vspu.i_height * vspu.i_width
-                                   - ( (i_code >> 2) + vspu.i_x
-                                       + vspu.i_y * vspu.i_width ) );
-                    return;
-                }
-                else
+                /* Get RLE information */
+                i_len = i_xscale * ( *p_source >> 2 );
+                i_color = *p_source++ & 0x3;
+
+                /* Draw as many lines as needed */
+                if( i_color )
                 {
-                    if( (i_color = i_code & 0x3) )
+                    for( i_ytmp = i_yreal ;
+                         i_ytmp < i_ynext ;
+                         i_ytmp += i_bytes_per_line )
                     {
-                        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 );
+                        memset( p_dest + i_bytes_per_pixel * ( i_x >> 6 )
+                                       + i_ytmp,
+                                p_palette[ i_color ],
+                                i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) );
                     }
-                    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 );
                 }
-                continue;
-            }
-    
-            ADD_NIBBLE( i_code, (i_code << 4) );
-            if( i_code >= 0x10 )   /* 00 11 xx cc */
-                goto found_code;   /* 00 01 xx cc */
-    
-            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 */
-    
-            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 */
-    
-            /* if the 14 first bits are 0, then it's a newline */
-            if( i_code <= 0x0003 )
-            {
-                if( NewLine( &vspu, &i_id ) < 0 )
-                    return;
-    
-                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)",
-                             i_code, p_from[i_id], i_id, vspu.i_x, vspu.i_y );
-                if( NewLine( &vspu, &i_id ) < 0 )
-                    return;
-                continue;
             }
         }
     }
 }
 
-static int NewLine( vout_spu_t *p_vspu, int *i_id )
-{
-    *i_id = 1 - *i_id;
-
-    p_vspu->i_x = 0;
-    p_vspu->i_y++;
-
-    return( p_vspu->i_width - p_vspu->i_y );
-
-}
-