]> git.sesse.net Git - vlc/commitdiff
* Additional optimizations to the subtitle decoder
authorSam Hocevar <sam@videolan.org>
Wed, 28 Feb 2001 05:20:02 +0000 (05:20 +0000)
committerSam Hocevar <sam@videolan.org>
Wed, 28 Feb 2001 05:20:02 +0000 (05:20 +0000)
  * The GGI video output should now be faster. Also, right-mouse-clicking
    works in GGI mode as well.

plugins/ggi/vout_ggi.c
src/spu_decoder/spu_decoder.c
src/spu_decoder/spu_decoder.h
src/video_output/video_spu.c

index 39942f964e46eca1b54c8a02cb56e096689175f1..b8419c52bd2e4a5d31d7bed14b204bab4e0bb88a 100644 (file)
@@ -136,6 +136,7 @@ int vout_Create( vout_thread_t *p_vout )
         free( p_vout->p_sys );
         return( 1 );
     }
+
     return( 0 );
 }
 
@@ -146,13 +147,23 @@ int vout_Create( vout_thread_t *p_vout )
  *****************************************************************************/
 int vout_Init( vout_thread_t *p_vout )
 {
+#define p_b p_vout->p_sys->p_buffer
     /* Acquire first buffer */
     if( p_vout->p_sys->b_must_acquire )
     {
-        ggiResourceAcquire( p_vout->p_sys->p_buffer[ p_vout->i_buffer_index ]->resource, GGI_ACTYPE_WRITE );
+        ggiResourceAcquire( p_b[ p_vout->i_buffer_index ]->resource,
+                            GGI_ACTYPE_WRITE );
     }
 
+    /* Listen to the keyboard and the mouse buttons */
+    ggiSetEventMask( p_vout->p_sys->p_display,
+                     emKeyboard | emPtrButtonPress | emPtrButtonRelease );
+
+    /* Set asynchronous display mode -- usually quite faster */
+    ggiAddFlags( p_vout->p_sys->p_display, GGIFLAG_ASYNC );
+
     return( 0 );
+#undef p_b
 }
 
 /*****************************************************************************
@@ -162,11 +173,13 @@ int vout_Init( vout_thread_t *p_vout )
  *****************************************************************************/
 void vout_End( vout_thread_t *p_vout )
 {
+#define p_b p_vout->p_sys->p_buffer
     /* Release buffer */
     if( p_vout->p_sys->b_must_acquire )
     {
-        ggiResourceRelease( p_vout->p_sys->p_buffer[ p_vout->i_buffer_index ]->resource );
+        ggiResourceRelease( p_b[ p_vout->i_buffer_index ]->resource );
     }
+#undef p_b
 }
 
 /*****************************************************************************
@@ -189,21 +202,48 @@ void vout_Destroy( vout_thread_t *p_vout )
  *****************************************************************************/
 int vout_Manage( vout_thread_t *p_vout )
 {
-    int         i_key;                                        /* unicode key */
+    struct timeval tv = { 0, 1000 };                        /* 1 millisecond */
+    gii_event_mask mask;
+    gii_event      event;
+
+    mask = emKeyboard | emPtrButtonPress | emPtrButtonRelease;
 
-    /* For all events in queue */
-    while( ggiKbhit( p_vout->p_sys->p_display ) )
+    ggiEventPoll( p_vout->p_sys->p_display, mask, &tv );
+    
+    while( ggiEventsQueued( p_vout->p_sys->p_display, mask) )
     {
-        i_key = ggiGetc( p_vout->p_sys->p_display );
-        switch( i_key )
-        {
-        case 'q':
-            /* FIXME pass message ! */
-            p_main->p_intf->b_die = 1;
-            break;
+        ggiEventRead( p_vout->p_sys->p_display, &event, mask);
 
-        default:
-            break;
+        switch( event.any.type )
+        {
+            case evKeyRelease:
+
+                switch( event.key.sym )
+                {
+                    case 'q':
+                    case 'Q':
+                    case GIIUC_Escape:
+                        /* FIXME pass message ! */
+                        p_main->p_intf->b_die = 1;
+                        break;
+
+                    default:
+                        break;
+                }
+
+                break;
+
+            case evPtrButtonRelease:
+
+                switch( event.pbutton.button )
+                {
+                    case GII_PBUTTON_RIGHT:
+                        /* FIXME: need locking ! */
+                        p_main->p_intf->b_menu_change = 1;
+                        break;
+                }
+
+            default:
         }
     }
 
@@ -218,23 +258,27 @@ int vout_Manage( vout_thread_t *p_vout )
  *****************************************************************************/
 void vout_Display( vout_thread_t *p_vout )
 {
+#define p_b p_vout->p_sys->p_buffer
     /* Change display frame */
     if( p_vout->p_sys->b_must_acquire )
     {
-        ggiResourceRelease( p_vout->p_sys->p_buffer[ p_vout->i_buffer_index ]->resource );
+        ggiResourceRelease( p_b[ p_vout->i_buffer_index ]->resource );
     }
-    ggiFlush( p_vout->p_sys->p_display ); /* XXX?? */
     ggiSetDisplayFrame( p_vout->p_sys->p_display,
-                        p_vout->p_sys->p_buffer[ p_vout->i_buffer_index ]->frame );
+                        p_b[ p_vout->i_buffer_index ]->frame );
 
     /* Swap buffers and change write frame */
     if( p_vout->p_sys->b_must_acquire )
     {
-        ggiResourceAcquire( p_vout->p_sys->p_buffer[ (p_vout->i_buffer_index + 1) & 1]->resource,
+        ggiResourceAcquire( p_b[ (p_vout->i_buffer_index + 1) & 1]->resource,
                             GGI_ACTYPE_WRITE );
     }
     ggiSetWriteFrame( p_vout->p_sys->p_display,
-                      p_vout->p_sys->p_buffer[ (p_vout->i_buffer_index + 1) & 1]->frame );
+                      p_b[ (p_vout->i_buffer_index + 1) & 1]->frame );
+
+    /* Flush the output so that it actually displays */
+    ggiFlush( p_vout->p_sys->p_display );
+#undef p_b
 }
 
 /* following functions are local */
@@ -247,6 +291,7 @@ void vout_Display( vout_thread_t *p_vout )
  *****************************************************************************/
 static int GGIOpenDisplay( vout_thread_t *p_vout )
 {
+#define p_b p_vout->p_sys->p_buffer
     ggi_mode    mode;                                     /* mode descriptor */
     ggi_color   col_fg;                                  /* foreground color */
     ggi_color   col_bg;                                  /* background color */
@@ -287,8 +332,7 @@ static int GGIOpenDisplay( vout_thread_t *p_vout )
     mode.dpp.y =        GGI_AUTO;
     ggiCheckMode( p_vout->p_sys->p_display, &mode );
 
-    /* Check that returned mode has some minimum properties */
-    /* XXX?? */
+    /* FIXME: Check that returned mode has some minimum properties */
 
     /* Set mode */
     if( ggiSetMode( p_vout->p_sys->p_display, &mode ) )
@@ -305,8 +349,9 @@ static int GGIOpenDisplay( vout_thread_t *p_vout )
     {
         /* Get buffer address */
         p_vout->p_sys->p_buffer[ i_index ] =
-            (ggi_directbuffer *)ggiDBGetBuffer( p_vout->p_sys->p_display, i_index );
-        if( p_vout->p_sys->p_buffer[ i_index ] == NULL )
+            (ggi_directbuffer *)ggiDBGetBuffer( p_vout->p_sys->p_display,
+                                                i_index );
+        if( p_b[ i_index ] == NULL )
         {
             intf_ErrMsg( "vout error: double buffering is not possible" );
             ggiClose( p_vout->p_sys->p_display );
@@ -315,11 +360,11 @@ static int GGIOpenDisplay( vout_thread_t *p_vout )
         }
 
         /* Check buffer properties */
-        if( ! (p_vout->p_sys->p_buffer[ i_index ]->type & GGI_DB_SIMPLE_PLB) ||
-            (p_vout->p_sys->p_buffer[ i_index ]->page_size != 0) ||
-            (p_vout->p_sys->p_buffer[ i_index ]->write == NULL ) ||
-            (p_vout->p_sys->p_buffer[ i_index ]->noaccess != 0) ||
-            (p_vout->p_sys->p_buffer[ i_index ]->align != 0) )
+        if( ! ( p_b[ i_index ]->type & GGI_DB_SIMPLE_PLB )
+           || ( p_b[ i_index ]->page_size != 0 )
+           || ( p_b[ i_index ]->write == NULL )
+           || ( p_b[ i_index ]->noaccess != 0 )
+           || ( p_b[ i_index ]->align != 0 ) )
         {
             intf_ErrMsg( "vout error: incorrect video memory type" );
             ggiClose( p_vout->p_sys->p_display );
@@ -328,17 +373,16 @@ static int GGIOpenDisplay( vout_thread_t *p_vout )
         }
 
         /* Check if buffer needs to be acquired before write */
-        if( ggiResourceMustAcquire( p_vout->p_sys->p_buffer[ i_index ]->resource ) )
+        if( ggiResourceMustAcquire( p_b[ i_index ]->resource ) )
         {
             p_vout->p_sys->b_must_acquire = 1;
         }
     }
-#ifdef DEBUG
+
     if( p_vout->p_sys->b_must_acquire )
     {
         intf_DbgMsg("buffers must be acquired");
     }
-#endif
 
     /* Set graphic context colors */
     col_fg.r = col_fg.g = col_fg.b = -1;
@@ -367,18 +411,20 @@ static int GGIOpenDisplay( vout_thread_t *p_vout )
     /* Set thread information */
     p_vout->i_width =           mode.visible.x;
     p_vout->i_height =          mode.visible.y;
-    p_vout->i_bytes_per_line =  p_vout->p_sys->p_buffer[ 0 ]->buffer.plb.stride;
-    p_vout->i_screen_depth =    p_vout->p_sys->p_buffer[ 0 ]->buffer.plb.pixelformat->depth;
-    p_vout->i_bytes_per_pixel = p_vout->p_sys->p_buffer[ 0 ]->buffer.plb.pixelformat->size / 8;
-    p_vout->i_red_mask =        p_vout->p_sys->p_buffer[ 0 ]->buffer.plb.pixelformat->red_mask;
-    p_vout->i_green_mask =      p_vout->p_sys->p_buffer[ 0 ]->buffer.plb.pixelformat->green_mask;
-    p_vout->i_blue_mask =       p_vout->p_sys->p_buffer[ 0 ]->buffer.plb.pixelformat->blue_mask;
-    /* FIXME: palette in 8bpp ?? */
+    p_vout->i_bytes_per_line =  p_b[ 0 ]->buffer.plb.stride;
+    p_vout->i_screen_depth =    p_b[ 0 ]->buffer.plb.pixelformat->depth;
+    p_vout->i_bytes_per_pixel = p_b[ 0 ]->buffer.plb.pixelformat->size / 8;
+    p_vout->i_red_mask =        p_b[ 0 ]->buffer.plb.pixelformat->red_mask;
+    p_vout->i_green_mask =      p_b[ 0 ]->buffer.plb.pixelformat->green_mask;
+    p_vout->i_blue_mask =       p_b[ 0 ]->buffer.plb.pixelformat->blue_mask;
+
+    /* FIXME: set palette in 8bpp */
 
     /* Set and initialize buffers */
-    vout_SetBuffers( p_vout, p_vout->p_sys->p_buffer[ 0 ]->write, p_vout->p_sys->p_buffer[ 1 ]->write );
+    vout_SetBuffers( p_vout, p_b[ 0 ]->write, p_b[ 1 ]->write );
 
     return( 0 );
+#undef p_b
 }
 
 /*****************************************************************************
index a5e9ed2701637f684c34a4a94fbc5b58e429e134..e76186b22837f2fccc350b06b9eaf7e9a42d795b 100644 (file)
@@ -213,7 +213,7 @@ static int SyncPacket( spudec_thread_t *p_spudec )
     p_spudec->i_spu_size = GetBits( &p_spudec->bit_stream, 16 );
 
     /* The RLE stuff size (remove 4 because we just read 32 bits) */
-    p_spudec->i_rle_size = GetBits( &p_spudec->bit_stream, 16 ) - 4;
+    p_spudec->i_rle_size = ShowBits( &p_spudec->bit_stream, 16 ) - 4;
 
     /* If the values we got are a bit strange, skip packet */
     if( p_spudec->i_rle_size >= p_spudec->i_spu_size )
@@ -221,6 +221,8 @@ static int SyncPacket( spudec_thread_t *p_spudec )
         return( 1 );
     }
 
+    RemoveBits( &p_spudec->bit_stream, 16 );
+
     return( 0 );
 }
 
@@ -233,7 +235,7 @@ static int SyncPacket( spudec_thread_t *p_spudec )
 static void ParsePacket( spudec_thread_t *p_spudec )
 {
     subpicture_t * p_spu;
-    u8           * p_source;
+    u8           * p_src;
 
     /* We cannot display a subpicture with no date */
     if( DECODER_FIFO_START(*p_spudec->p_fifo)->i_pts == 0 )
@@ -246,8 +248,8 @@ static void ParsePacket( spudec_thread_t *p_spudec )
                                    p_spudec->i_rle_size * 4 );
     /* Rationale for the "p_spudec->i_rle_size * 4": we are going to
      * expand the RLE stuff so that we won't need to read nibbles later
-     * on. This will speed things up a lot. Plus, we won't need to do
-     * this stupid interlacing stuff. */
+     * on. This will speed things up a lot. Plus, we'll only need to do
+     * this stupid interlacing stuff once. */
 
     if( p_spu == NULL )
     {
@@ -259,17 +261,17 @@ static void ParsePacket( spudec_thread_t *p_spudec )
                     = DECODER_FIFO_START(*p_spudec->p_fifo)->i_pts;
 
     /* Allocate the temporary buffer we will parse */
-    p_source = malloc( p_spudec->i_rle_size );
+    p_src = malloc( p_spudec->i_rle_size );
 
-    if( p_source == NULL )
+    if( p_src == NULL )
     {
-        intf_ErrMsg( "spudec error: could not allocate p_source" );
+        intf_ErrMsg( "spudec error: could not allocate p_src" );
         vout_DestroySubPicture( p_spudec->p_vout, p_spu );
         return;
     }
 
     /* Get RLE data */
-    GetChunk( &p_spudec->bit_stream, p_source, p_spudec->i_rle_size );
+    GetChunk( &p_spudec->bit_stream, p_src, p_spudec->i_rle_size );
 
 #if 0
     /* Dump the subtitle info */
@@ -280,29 +282,29 @@ static void ParsePacket( spudec_thread_t *p_spudec )
     if( ParseControlSequences( p_spudec, p_spu ) )
     {
         /* There was a parse error, delete the subpicture */
-        free( p_source );
+        free( p_src );
         vout_DestroySubPicture( p_spudec->p_vout, p_spu );
         return;
     }
 
-    intf_WarnMsg( 1, "spudec: got a valid %ix%i subtitle at (%i,%i), "
-                     "RLE offsets: 0x%x 0x%x",
-                  p_spu->i_width, p_spu->i_height, p_spu->i_x, p_spu->i_y,
-                  p_spu->type.spu.i_offset[0], p_spu->type.spu.i_offset[1] );
-
-    if( ParseRLE( p_source, p_spu ) )
+    if( ParseRLE( p_src, p_spu ) )
     {
         /* There was a parse error, delete the subpicture */
-        free( p_source );
+        free( p_src );
         vout_DestroySubPicture( p_spudec->p_vout, p_spu );
         return;
     }
 
+    intf_WarnMsg( 1, "spudec: got a valid %ix%i subtitle at (%i,%i), "
+                     "RLE offsets: 0x%x 0x%x",
+                  p_spu->i_width, p_spu->i_height, p_spu->i_x, p_spu->i_y,
+                  p_spu->type.spu.i_offset[0], p_spu->type.spu.i_offset[1] );
+
     /* SPU is finished - we can tell the video output to display it */
     vout_DisplaySubPicture( p_spudec->p_vout, p_spu );
 
     /* Clean up */
-    free( p_source );
+    free( p_src );
 }
 
 /*****************************************************************************
@@ -355,14 +357,14 @@ static int ParseControlSequences( spudec_thread_t *p_spudec,
                 case SPU_CMD_START_DISPLAY:
  
                     /* 01 (start displaying) */
-                    p_spu->begin_date += ( i_date * 12000 );
+                    p_spu->begin_date += ( i_date * 11000 );
  
                     break;
  
                 case SPU_CMD_STOP_DISPLAY:
  
                     /* 02 (stop displaying) */
-                    p_spu->end_date += ( i_date * 12000 );
+                    p_spu->end_date += ( i_date * 11000 );
  
                     break;
  
@@ -484,111 +486,91 @@ static int ParseControlSequences( spudec_thread_t *p_spudec,
  * convenient structure for later decoding. For more information on the
  * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
  *****************************************************************************/
-static int ParseRLE( u8 *p_source, subpicture_t * p_spu )
+static int ParseRLE( u8 *p_src, subpicture_t * p_spu )
 {
-    int i_code;
-    int i_id = 0;
+    unsigned int i_code;
+    unsigned int i_id = 0;
 
-    int i_width = p_spu->i_width;
-    int i_height = p_spu->i_height;
-    int i_x = 0, i_y = 0;
+    unsigned int i_width = p_spu->i_width;
+    unsigned int i_height = p_spu->i_height;
+    unsigned int i_x, i_y;
 
     u16 *p_dest = (u16 *)p_spu->p_data;
-    int pi_index[2];
 
-    pi_index[0] = p_spu->type.spu.i_offset[0] << 1;
-    pi_index[1] = p_spu->type.spu.i_offset[1] << 1;
+    /* The subtitles are interlaced, we need two offsets */
+    unsigned int  pi_table[2];
+    unsigned int *pi_offset;
+    pi_table[0] = p_spu->type.spu.i_offset[0] << 1;
+    pi_table[1] = p_spu->type.spu.i_offset[1] << 1;
 
-    while( i_y < i_height )
+    for( i_y = 0 ; i_y < i_height ; i_y++ )
     {
-        i_code = GetNibble( p_source, pi_index + i_id );
+        pi_offset = pi_table + i_id;
 
-        if( i_code >= 0x04 )
+        for( i_x = 0 ; i_x < i_width ; i_x += i_code >> 2 )
         {
-            found_code:
+            i_code = AddNibble( 0, p_src, pi_offset );
 
-            if( ((i_code >> 2) + i_x + i_y * i_width) > i_height * i_width )
+            if( i_code < 0x04 )
             {
-                intf_ErrMsg( "spudec error: out of bounds, %i at (%i,%i) is "
-                             "out of %ix%i",
-                             i_code >> 2, i_x, i_y, i_width, i_height);
-                return( 1 );
-            }
-            else
-            {
-                /* Store the code */
-                *p_dest++ = i_code;
+                i_code = AddNibble( i_code, p_src, pi_offset );
 
-                i_x += i_code >> 2;
+                if( i_code < 0x10 )
+                {
+                    i_code = AddNibble( i_code, p_src, pi_offset );
+
+                    if( i_code < 0x040 )
+                    {
+                        i_code = AddNibble( i_code, p_src, pi_offset );
+
+                        if( i_code < 0x0100 )
+                        {
+                            /* If the 14 first bits are set to 0, then it's a
+                             * new line. We emulate it. */
+                            if( i_code < 0x0004 )
+                            {
+                                i_code |= ( i_width - i_x ) << 2;
+                            }
+                            else
+                            {
+                                /* We have a boo boo ! */
+                                intf_ErrMsg( "spudec error: unknown code %.4x",
+                                             i_code );
+                                return( 1 );
+                            }
+                        }
+                    }
+                }
             }
 
-            if( i_x > i_width )
+            if( ( (i_code >> 2) + i_x + i_y * i_width ) > i_height * i_width )
             {
-                intf_ErrMsg( "spudec error: i_x overflowed, %i > %i",
-                             i_x, i_width );
+                intf_ErrMsg( "spudec error: out of bounds, %i at (%i,%i) is "
+                             "out of %ix%i",
+                             i_code >> 2, i_x, i_y, i_width, i_height);
                 return( 1 );
             }
 
-            if( i_x == i_width )
-            {
-                /* byte-align the stream */
-                if( pi_index[i_id] & 0x1 )
-                {
-                    pi_index[i_id]++;
-                }
-
-                i_id = ~i_id & 0x1;
-
-                i_y++;
-                i_x = 0;
-
-                if( i_y > i_height )
-                {
-                    intf_ErrMsg( "spudec error: i_y overflowed at EOL, "
-                                 "%i > %i", i_y, i_height );
-                    return( 1 );
-                }
-            }
-
-            continue;
+            /* We got a valid code, store it */
+            *p_dest++ = i_code;
         }
 
-        i_code = ( i_code << 4 ) + GetNibble( p_source, pi_index + i_id );
-
-        /* 00 11 xx cc */
-        if( i_code >= 0x10 )
+        /* Check that we didn't go too far */
+        if( i_x > i_width )
         {
-            /* 00 01 xx cc */
-            goto found_code;
-        }
-
-        i_code = ( i_code << 4 ) + GetNibble( p_source, pi_index + i_id );
-
-        /* 00 00 11 xx xx cc */
-        if( i_code >= 0x040 )
-        {
-            goto found_code;   /* 00 00 01 xx xx cc */
+            intf_ErrMsg( "spudec error: i_x overflowed, %i > %i",
+                         i_x, i_width );
+            return( 1 );
         }
 
-        i_code = ( i_code << 4 ) + GetNibble( p_source, pi_index + i_id );
-
-        if( i_code >= 0x0100 ) /* 00 00 00 11 xx xx xx cc */
+        /* Byte-align the stream */
+        if( *pi_offset & 0x1 )
         {
-            goto found_code;   /* 00 00 00 01 xx xx xx cc */
+            (*pi_offset)++;
         }
 
-        if( i_code & ~0x0003 )
-        {
-            /* We have a boo boo ! */
-            intf_ErrMsg( "spudec error: unknown code 0x%.4x", i_code );
-            return( 1 );
-        }
-        else
-        {
-            /* If the 14 first bits are 0, then it's a new line */
-            i_code |= ( i_width - i_x ) << 2;
-            goto found_code;
-        }
+        /* Swap fields */
+        i_id = ~i_id & 0x1;
     }
 
     /* FIXME: we shouldn't need these padding bytes */
index 47ce700a27b7d356814eebdb022e662a6dbb8f2a..966f09754f01ea46a2a2be7eac92111709a3d241 100644 (file)
@@ -65,17 +65,18 @@ typedef struct spudec_thread_s
 #define SPU_CMD_END                 0xff
 
 /*****************************************************************************
- * GetNibble: read a nibble from a source packet.
+ * AddNibble: read a nibble from a source packet and add it to our integer.
  *****************************************************************************/
-static __inline__ u8 GetNibble( u8 *p_source, int *pi_index )
+static __inline__ unsigned int AddNibble( unsigned int i_code,
+                                          u8 *p_src, int *pi_index )
 {
     if( *pi_index & 0x1 )
     {
-        return( p_source[(*pi_index)++ >> 1] & 0xf );
+        return( i_code << 4 | ( p_src[(*pi_index)++ >> 1] & 0xf ) );
     }
     else
     {
-        return( p_source[(*pi_index)++ >> 1] >> 4 );
+        return( i_code << 4 | p_src[(*pi_index)++ >> 1] >> 4 );
     }
 }
 
index 42ec9b586ce5458d554095079eee47b2a7b0ac99..618834710847ba6e5b8661e460d946b847d24a5f 100644 (file)
@@ -4,7 +4,6 @@
  * Copyright (C) 1999, 2000 VideoLAN
  *
  * Authors: Samuel Hocevar <sam@zoy.org>
- *          Henri Fallon <henri@via.ecp.fr>
  * 
  * 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
@@ -61,7 +60,7 @@ void vout_RenderSPU( vout_buffer_t *p_buffer, subpicture_t *p_spu,
     int i_width  = p_spu->i_width  * i_xscale;
     int i_height = p_spu->i_height * i_yscale;
 
-    int i_x = 0, i_y = 0;
+    int i_x = 0, i_y = 0, i_ytmp, i_yreal, i_ynext;
 
     u8 *p_dest = p_buffer->p_data
                   /* Add the picture coordinates and the SPU coordinates */
@@ -71,36 +70,60 @@ void vout_RenderSPU( vout_buffer_t *p_buffer, subpicture_t *p_spu,
                        * i_bytes_per_line;
 
     /* Draw until we reach the bottom of the subtitle */
-    while( i_y < i_height )
+    for( i_y = 0 ; i_y < i_height ; /* i_y incremented below */ )
     {
-        /* Get RLE information */
-        i_len = i_xscale * ( *p_source >> 2 );
-        i_color = *p_source++ & 0x3;
+        i_ytmp = i_y >> 6;
+        i_y += i_yscale;
 
-        /* Draw the line */
-        if( i_color )
+        /* Check whether we need to draw one line or more than one */
+        if( i_ytmp + 1 >= ( i_y >> 6 ) )
         {
-            memset( p_dest + i_bytes_per_pixel * ( i_x >> 6 )
-                           + i_bytes_per_line * ( i_y >> 6 ),
-                    p_palette[ i_color ],
-                    i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) );
+            /* Just one line : we precalculate i_y >> 6 */
+            i_yreal = i_bytes_per_line * i_ytmp;
 
-            /* Duplicate line if needed */
-            if( i_yscale > 1 << 6 )
+            /* Draw until we reach the end of the line */
+            for( i_x = 0 ; i_x < i_width ; i_x += i_len )
             {
-                memset( p_dest + i_bytes_per_pixel * ( i_x >> 6 )
-                               + i_bytes_per_line * ( ( i_y >> 6 ) + 1 ),
-                        p_palette[ i_color ],
-                        i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) );
+                /* Get RLE information */
+                i_len = i_xscale * ( *p_source >> 2 );
+                i_color = *p_source++ & 0x3;
+
+                /* Draw the line */
+                if( i_color )
+                {
+                    memset( p_dest + i_bytes_per_pixel * ( i_x >> 6 )
+                                   + i_yreal,
+                            p_palette[ i_color ],
+                            i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) );
+                }
             }
         }
-
-        /* Check for end of line */
-        i_x += i_len;
-        if( i_x >= i_width )
+       else
         {
-            i_y += i_yscale;
-            i_x = 0;
+            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 )
+            {
+                /* Get RLE information */
+                i_len = i_xscale * ( *p_source >> 2 );
+                i_color = *p_source++ & 0x3;
+
+                /* Draw as many lines as needed */
+                if( i_color )
+                {
+                    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 ) );
+                    }
+                }
+            }
         }
     }
 }