]> git.sesse.net Git - vlc/commitdiff
Check in a stable copy for reference against future changes. Should
authorRocky Bernstein <rocky@videolan.org>
Fri, 26 Dec 2003 01:39:35 +0000 (01:39 +0000)
committerRocky Bernstein <rocky@videolan.org>
Fri, 26 Dec 2003 01:39:35 +0000 (01:39 +0000)
not affect or be seen in upcoming release.

modules/codec/ogt/parse.c [new file with mode: 0644]
modules/codec/ogt/render.c [new file with mode: 0644]

diff --git a/modules/codec/ogt/parse.c b/modules/codec/ogt/parse.c
new file mode 100644 (file)
index 0000000..9e3e3b3
--- /dev/null
@@ -0,0 +1,471 @@
+/*****************************************************************************
+ * parse.c: Philips OGT (SVCD subtitle) packet parser
+ *****************************************************************************
+ * Copyright (C) 2003 VideoLAN
+ * $Id: parse.c,v 1.1 2003/12/26 01:39:23 rocky Exp $
+ *
+ * Authors: Rocky Bernstein 
+ *   based on code from: 
+ *       Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
+ *       Sam Hocevar <sam@zoy.org>
+ *       Laurent Aimar <fenrir@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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <vlc/vlc.h>
+#include <vlc/vout.h>
+#include <vlc/decoder.h>
+
+#include "ogt.h"
+
+/* An image color is a two-bit palette entry: 0..3 */ 
+typedef uint8_t ogt_color_t;
+
+/*****************************************************************************
+ * Local prototypes.
+ *****************************************************************************/
+static int  ParseImage         ( decoder_t *, subpicture_t * );
+
+static void DestroySPU       ( subpicture_t * );
+
+static void UpdateSPU        ( subpicture_t *, vlc_object_t * );
+static int  CropCallback     ( vlc_object_t *, char const *,
+                               vlc_value_t, vlc_value_t, void * );
+
+/*
+  The format is roughly as follows (everything is big-endian):
+   size     description
+   -------------------------------------------
+   byte     subtitle channel (0..7) in bits 0-3 
+   byte     subtitle packet number of this subtitle image 0-N,
+            if the subtitle packet is complete, the top bit of the byte is 1.
+   u_int16  subtitle image number
+   u_int16  length in bytes of the rest
+   byte     option flags, unknown meaning except bit 3 (0x08) indicates
+           presence of the duration field
+   byte     unknown 
+   u_int32  duration in 1/90000ths of a second (optional), start time
+           is as indicated by the PTS in the PES header
+   u_int32  xpos
+   u_int32  ypos
+   u_int32  width (must be even)
+   u_int32  height (must be even)
+   byte[16] palette, 4 palette entries, each contains values for
+           Y, U, V and transparency, 0 standing for transparent
+   byte     command,
+           cmd>>6==1 indicates shift
+           (cmd>>4)&3 is direction from, (0=top,1=left,2=right,3=bottom)
+   u_int32  shift duration in 1/90000ths of a second
+   u_int16  offset of odd-numbered scanlines - subtitle images are 
+            given in interlace order
+   byte[]   limited RLE image data in interlace order (0,2,4... 1,3,5) with
+            2-bits per palette number
+*/
+
+/* FIXME: do we really need p_buffer and p? 
+   Can't all of thes _offset's and _lengths's get removed? 
+*/
+void E_(ParseHeader)( decoder_t *p_dec, uint8_t *p_buffer, block_t *p_block )
+{
+  decoder_sys_t *p_sys = p_dec->p_sys;
+  u_int8_t *p = p_buffer;
+  int i;
+  
+  p_sys->i_pts    = p_block->i_pts;
+  p_sys->i_spu_size = GETINT16(p);
+  p_sys->i_options  = *p++;
+  p_sys->i_options2 = *p++;
+  
+  if ( p_sys->i_options & 0x08 ) {
+    p_sys->i_duration = GETINT32(p);
+  } else {
+    /* 0 means display until next subtitle comes in. */
+    p_sys->i_duration = 0;
+  }
+  p_sys->i_x_start= GETINT16(p);
+  p_sys->i_y_start= GETINT16(p);
+  p_sys->i_width  = GETINT16(p);
+  p_sys->i_height = GETINT16(p);
+  
+  for (i=0; i<4; i++) {
+    p_sys->pi_palette[i].y = *p++;
+    p_sys->pi_palette[i].u = *p++;
+    p_sys->pi_palette[i].v = *p++;
+    /* We have just 4-bit resolution for alpha, but the value for SVCD
+     * has 8 bits so we scale down the values to the acceptable range */
+       p_sys->pi_palette[i].t = (*p++) >> 4;
+  }
+  p_sys->i_cmd = *p++;
+      /* We do not really know this, FIXME */
+  if ( p_sys->i_cmd ) {
+    p_sys->i_cmd_arg = GETINT32(p);
+  }
+
+  /* Actually, this is measured against a different origin, so we have to
+     adjust it */
+  p_sys->second_field_offset = GETINT16(p);
+  p_sys->comp_image_offset = p - p_buffer;
+  p_sys->comp_image_length = p_sys->i_spu_size - p_sys->comp_image_offset;
+  p_sys->metadata_length   = p_sys->comp_image_offset;
+
+  if (p_sys && p_sys->i_debug & DECODE_DBG_PACKET) {
+    msg_Dbg( p_dec, "x-start: %d, y-start: %d, width: %d, height %d, "
+            "spu size: %d, duration: %u (d:%d p:%d)",
+            p_sys->i_x_start, p_sys->i_y_start, 
+            p_sys->i_width, p_sys->i_height, 
+            p_sys->i_spu_size, p_sys->i_duration,
+            p_sys->comp_image_length, p_sys->comp_image_offset);
+    
+    for (i=0; i<4; i++) {
+      msg_Dbg( p_dec, "palette[%d]= T: %2x, Y: %2x, u: %2x, v: %2x", i,
+              p_sys->pi_palette[i].t, p_sys->pi_palette[i].y, 
+              p_sys->pi_palette[i].u, p_sys->pi_palette[i].v );
+    }
+  }
+}
+
+
+/*****************************************************************************
+ * ParsePacket: parse an SPU packet and send it to the video output
+ *****************************************************************************
+ * This function parses the SPU packet and, if valid, sends it to the
+ * video output.
+ *****************************************************************************/
+void 
+E_(ParsePacket)( decoder_t *p_dec)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    subpicture_t  *p_spu;
+
+    dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
+    
+    /* Allocate the subpicture internal data. */
+    p_spu = vout_CreateSubPicture( p_sys->p_vout, MEMORY_SUBPICTURE );
+    if( p_spu == NULL )
+    {
+        return;
+    }
+
+    /* In ParseImage we expand the run-length encoded color 0's; also
+       we expand pixels and remove the color palette. This should
+       facilitate scaling and antialiasing and speed up rendering.
+    */
+    p_spu->p_sys = malloc( sizeof( subpicture_sys_t ) 
+                          + PIXEL_SIZE * (p_sys->i_width * p_sys->i_height) );
+
+    /* Fill the p_spu structure */
+    vlc_mutex_init( p_dec, &p_spu->p_sys->lock );
+
+    p_spu->pf_render = E_(RenderSPU);
+    p_spu->pf_destroy = DestroySPU;
+    p_spu->p_sys->p_data = (uint8_t*)p_spu->p_sys + sizeof( subpicture_sys_t );
+    p_spu->p_sys->b_palette = VLC_FALSE;
+
+    p_spu->p_sys->i_x_end        = p_sys->i_x_start + p_sys->i_width - 1;
+    p_spu->p_sys->i_y_end        = p_sys->i_y_start + p_sys->i_height - 1;
+
+    p_spu->i_x        = p_sys->i_x_start / 2;
+    p_spu->i_y        = p_sys->i_y_start;
+    p_spu->i_width    = p_sys->i_width;
+    p_spu->i_height   = p_sys->i_height;
+
+    p_spu->i_start    = p_sys->i_pts;
+    p_spu->i_stop     = p_sys->i_pts + (p_sys->i_duration * 10);
+    
+    p_spu->p_sys->b_crop = VLC_FALSE;
+
+    /* Get display time now. If we do it later, we may miss the PTS. */
+    p_spu->p_sys->i_pts = p_sys->i_pts;
+
+    /* Attach to our input thread */
+    p_spu->p_sys->p_input = vlc_object_find( p_dec,
+                                             VLC_OBJECT_INPUT, FIND_PARENT );
+
+    /* We try to display it */
+    if( ParseImage( p_dec, p_spu ) )
+    {
+        /* There was a parse error, delete the subpicture */
+        vout_DestroySubPicture( p_sys->p_vout, p_spu );
+        return;
+    }
+
+    /* SPU is finished - we can ask the video output to display it */
+    vout_DisplaySubPicture( p_sys->p_vout, p_spu );
+
+}
+
+/* Advance pointer to image pointer, update internal i_remaining counter
+   and check that we haven't goine too far  in the image data. */
+#define advance_color_pointer_byte                                     \
+  p++;                                                                 \
+  i_remaining=4;                                                       \
+  if (p >= maxp) {                                                     \
+    msg_Warn( p_dec,                                                   \
+             "broken subtitle - tried to access beyond end "           \
+             "in image extraction");                                   \
+    return VLC_EGENERIC;                                               \
+  }                                                                    \
+
+#define advance_color_pointer                                          \
+  i_remaining--;                                                       \
+  if ( i_remaining == 0 ) {                                            \
+    advance_color_pointer_byte;                                                \
+  }                                                                    
+
+/* Get the next field - either a palette index or a RLE count for
+   color 0.  To do this we use byte image pointer p, and i_remaining
+   which indicates where we are in the byte.
+*/
+static inline ogt_color_t 
+ExtractField(uint8_t *p, unsigned int i_remaining) 
+{
+  return ( ( *p >> 2*(i_remaining-1) ) & 0x3 );
+}
+
+#ifdef FINISHED
+/* Scales down (reduces size) of p_dest in the x direction as 
+   determined through aspect ratio x_scale by y_scale. Scaling
+   is done in place. i_width, is updated to new ratio.
+
+   The aspect ratio is assumed to be between 1 and 2.
+*/
+static void
+ScaleX( uint8_t *p_dest, /*in out */ u_int16_t *i_width, u_int16_t i_height,
+       unsigned int scale_x, unsigned int scale_y )
+{
+  int i_row, i_col;
+  uint8_t *p1 = p_dest;
+  uint8_t *p2 = p_dest + PIXEL_SIZE;
+  
+  unsigned int used=0;  /* Number of bytes used up in p1. */
+
+  for ( i_row=0; i_row < i_height - 1; i_row++ ) {
+    for ( i_col=0; i_col <= (*i_width)-2; i_col++ ) {
+      unsigned int i;
+      unsigned int w1= scale_x - used;
+      unsigned int w2= scale_y - w1;
+      used = w2;
+      for (i = 0; i < PIXEL_SIZE; i++ ) {
+       *p1 = ( (*p1 * w1) + (*p2 * w2) ) / scale_y;
+       p1++; p2++;
+      }
+      if (scale_x == used) {
+       p1 = p2;
+       p2 += PIXEL_SIZE;
+       used = 0;
+      }
+    }
+  }
+  /* *i_width = ((*i_width) * scale_y)  / scale_x; */
+}
+#endif
+
+/*****************************************************************************
+ * ParseImage: parse the image part of the subtitle
+ *****************************************************************************
+ This part parses the subtitle graphical data and stores it in a more
+ convenient structure for later rendering. 
+
+ The image is encoded using two bits per pixel that select a palette
+ entry except that value 0 starts a limited run-length encoding for
+ color 0.  When 0 is seen, the next two bits encode one less than the
+ number of pixels, so we can encode run lengths from 1 to 4. These get
+ filled with the color in palette entry 0.
+
+ The encoding of each line is padded to a whole number of bytes.  The
+ first field is padded to an even byte length and the complete subtitle
+ is padded to a 4-byte multiple that always include one zero byte at
+ the end.
+
+ However we'll transform this so that that the RLE is expanded and
+ interlacing will also be removed. On output each pixel entry will by 
+ an 8-bit alpha, y, u, and v entry.
+
+ *****************************************************************************/
+static int 
+ParseImage( decoder_t *p_dec, subpicture_t * p_spu )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    unsigned int i_field;  /* The subtitles are interlaced, are we on an
+                             even or odd scanline?  */
+
+    unsigned int i_row;    /* scanline row number */
+    unsigned int i_column; /* scanline column number */
+
+    unsigned int i_width  = p_sys->i_width;
+    unsigned int i_height = p_sys->i_height;
+
+    uint8_t *p_dest = (uint8_t *)p_spu->p_sys->p_data;
+
+    uint8_t i_remaining;           /* number of 2-bit pixels remaining 
+                                     in byte of *p */
+    uint8_t i_pending_zero = 0;    /* number of pixels to fill with 
+                                     color zero 0..4 */
+    ogt_color_t i_color;           /* current pixel color: 0..3 */
+    uint8_t *p = p_sys->subtitle_data  + p_sys->comp_image_offset;
+    uint8_t *maxp = p + p_sys->comp_image_length;
+
+    dbg_print( (DECODE_DBG_CALL) , "width x height: %dx%d ",
+              i_width, i_height);
+
+    if (p_sys && p_sys->i_debug & DECODE_DBG_IMAGE)    
+      printf("\n");
+
+    for ( i_field=0; i_field < 2; i_field++ ) {
+      i_remaining = 4;
+      for ( i_row=i_field; i_row < i_height; i_row += 2 ) {
+       for ( i_column=0; i_column<i_width; i_column++ ) {
+
+         if ( i_pending_zero ) {
+           i_pending_zero--;
+           i_color = 0;
+         } else {
+           i_color = ExtractField( p, i_remaining);
+           advance_color_pointer;
+           if ( i_color == 0 ) {
+             i_pending_zero = ExtractField( p, i_remaining );
+             advance_color_pointer;
+             /* Fall through with i_color == 0 to output the first cell */
+           }
+         }
+
+         /* Color is 0-3. */
+         p_dest[i_row*i_width+i_column] = i_color;
+
+         if (p_sys && p_sys->i_debug & DECODE_DBG_IMAGE)       
+           printf("%1d", i_color);
+
+       }
+
+       if (p_sys && p_sys->i_debug & DECODE_DBG_IMAGE) 
+         printf("\n");
+
+       if ( i_remaining != 4 ) {
+         /* Lines are padded to complete bytes, ignore padding */
+         advance_color_pointer_byte;
+       }
+      }
+      p = p_sys->subtitle_data + p_sys->comp_image_offset 
+       + p_sys->second_field_offset;
+    }
+
+    /* Dump out image not interlaced... */
+    if (p_sys && p_sys->i_debug & DECODE_DBG_IMAGE) {
+      uint8_t *p = p_dest;
+      printf("-------------------------------------\n++");
+      for ( i_row=0; i_row < i_height; i_row ++ ) {
+       for ( i_column=0; i_column<i_width; i_column++ ) {
+         printf("%1d", *p++ & 0x03);
+       }
+       printf("\n++");
+      }
+      printf("\n-------------------------------------\n");
+    }
+
+    /* Remove color palette by expanding pixel entries to contain the
+       palette values. We work from the free space at the end to the
+       beginning so we can expand inline.
+    */
+    {
+      int n = (i_height * i_width) - 1;
+      uint8_t    *p_from = p_dest;
+      ogt_yuvt_t *p_to   = (ogt_yuvt_t *) p_dest;
+      
+      for ( ; n >= 0 ; n-- ) {
+       p_to[n] = p_sys->pi_palette[p_from[n]];
+      }
+    }
+
+#ifdef FINISHED
+    /* The video is automatically scaled. However subtitle bitmaps
+       assume a 1:1 aspect ratio. So we need to scale to compensate for
+       or undo the effects of video output scaling. 
+    */
+    /* FIXME do the right scaling depending on vout. It may not be 4:3 */
+    ScaleX( p_dest, &(p_sys->i_width), i_height, 3, 4 );
+#endif
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * DestroySPU: subpicture destructor
+ *****************************************************************************/
+static void DestroySPU( subpicture_t *p_spu )
+{
+    if( p_spu->p_sys->p_input )
+    {
+        /* Detach from our input thread */
+        var_DelCallback( p_spu->p_sys->p_input, "highlight",
+                         CropCallback, p_spu );
+        vlc_object_release( p_spu->p_sys->p_input );
+    }
+
+    vlc_mutex_destroy( &p_spu->p_sys->lock );
+    free( p_spu->p_sys );
+}
+
+/*****************************************************************************
+ * UpdateSPU: update subpicture settings
+ *****************************************************************************
+ * This function is called from CropCallback and at initialization time, to
+ * retrieve crop information from the input.
+ *****************************************************************************/
+static void UpdateSPU( subpicture_t *p_spu, vlc_object_t *p_object )
+{
+    vlc_value_t val;
+
+    if( var_Get( p_object, "highlight", &val ) )
+    {
+        return;
+    }
+
+    p_spu->p_sys->b_crop = val.b_bool;
+    if( !p_spu->p_sys->b_crop )
+    {
+        return;
+    }
+
+    var_Get( p_object, "x-start", &val );
+    p_spu->p_sys->i_x_start = val.i_int;
+    var_Get( p_object, "y-start", &val );
+    p_spu->p_sys->i_y_start = val.i_int;
+    var_Get( p_object, "x-end", &val );
+    p_spu->p_sys->i_x_end = val.i_int;
+    var_Get( p_object, "y-end", &val );
+    p_spu->p_sys->i_y_end = val.i_int;
+
+}
+
+/*****************************************************************************
+ * CropCallback: called when the highlight properties are changed
+ *****************************************************************************
+ * This callback is called from the input thread when we need cropping
+ *****************************************************************************/
+static int CropCallback( vlc_object_t *p_object, char const *psz_var,
+                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    UpdateSPU( (subpicture_t *)p_data, p_object );
+
+    return VLC_SUCCESS;
+}
+
diff --git a/modules/codec/ogt/render.c b/modules/codec/ogt/render.c
new file mode 100644 (file)
index 0000000..bfae81e
--- /dev/null
@@ -0,0 +1,180 @@
+/*****************************************************************************
+ * render.c : Philips OGT (SVCD Subtitle) renderer
+ *****************************************************************************
+ * Copyright (C) 2003 VideoLAN
+ * $Id: render.c,v 1.1 2003/12/26 01:39:35 rocky Exp $
+ *
+ * Author: Rocky Bernstein 
+ *   based on code from: 
+ *          Sam Hocevar <sam@zoy.org>
+ *          Rudolf Cornelissen <rag.cornelissen@inter.nl.net>
+ *          Roine Gustafsson <roine@popstar.com>
+ *
+ * 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
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <vlc/vlc.h>
+#include <vlc/vout.h>
+#include <vlc/decoder.h>
+
+#include "ogt.h"
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static void RenderI420( vout_thread_t *, picture_t *, const subpicture_t *,
+                        vlc_bool_t );
+
+/*****************************************************************************
+ * RenderSPU: draw an SPU on a picture
+ *****************************************************************************
+  This is a fast implementation of the subpicture drawing code. The
+  data has been preprocessed. Each byte has a run-length 1 in the upper
+  nibble and a color in the lower nibble. The interleaving of rows has
+  been done. Most sanity checks are already done so that this
+  routine can be as fast as possible.
+
+ *****************************************************************************/
+void E_(RenderSPU)( vout_thread_t *p_vout, picture_t *p_pic,
+                    const subpicture_t *p_spu )
+{
+
+#ifndef FINISHED
+    printf("+++%x\n", p_vout->output.i_chroma);
+#endif
+  
+    switch( p_vout->output.i_chroma )
+    {
+        /* I420 target, no scaling */
+        case VLC_FOURCC('I','4','2','0'):
+        case VLC_FOURCC('I','Y','U','V'):
+        case VLC_FOURCC('Y','V','1','2'):
+            RenderI420( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
+            break;
+
+        /* RV16 target, scaling */
+        case VLC_FOURCC('R','V','1','6'):
+            msg_Err( p_vout, "RV16 not implimented yet" );
+           break;
+
+        /* RV32 target, scaling */
+        case VLC_FOURCC('R','V','2','4'):
+        case VLC_FOURCC('R','V','3','2'):
+            msg_Err( p_vout, "RV24/VF32 not implimented yet" );
+           break;
+
+        /* NVidia overlay, no scaling */
+        case VLC_FOURCC('Y','U','Y','2'):
+            msg_Err( p_vout, "YUV2 not implimented yet" );
+           break;
+
+        default:
+            msg_Err( p_vout, "unknown chroma, can't render SPU" );
+            break;
+    }
+}
+
+/* Following functions are local */
+
+static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
+                        const subpicture_t *p_spu, vlc_bool_t b_crop )
+{
+  /* Common variables */
+  uint8_t *p_dest;
+  uint8_t *p_destptr;
+  ogt_yuvt_t *p_source;
+  
+  int i_x, i_y;
+  uint16_t i_colprecomp, i_destalpha;
+  
+  /* Crop-specific */
+  int i_x_start, i_y_start, i_x_end, i_y_end;
+  /* int i=0; */
+  
+  p_dest = p_pic->Y_PIXELS + p_spu->i_x + p_spu->i_width
+    + p_pic->Y_PITCH * ( p_spu->i_y + p_spu->i_height );
+  
+  i_x_start = p_spu->i_width - p_spu->p_sys->i_x_end;
+  i_y_start = p_pic->Y_PITCH * (p_spu->i_height - p_spu->p_sys->i_y_end );
+  i_x_end   = p_spu->i_width - p_spu->p_sys->i_x_start;
+  i_y_end   = p_pic->Y_PITCH * (p_spu->i_height - p_spu->p_sys->i_y_start );
+  
+  p_source = (ogt_yuvt_t *)p_spu->p_sys->p_data;
+
+  /* printf("+++spu width: %d, height %d\n", p_spu->i_width, 
+     p_spu->i_height); */
+  
+  /* Draw until we reach the bottom of the subtitle */
+  for( i_y = p_spu->i_height * p_pic->Y_PITCH ;
+       i_y ;
+       i_y -= p_pic->Y_PITCH )
+    {
+      /* printf("+++begin line: %d,\n", i++); */
+      /* Draw until we reach the end of the line */
+      for( i_x = p_spu->i_width ; i_x ; i_x--, p_source++ )
+        {
+         
+         if( b_crop
+             && ( i_x < i_x_start || i_x > i_x_end
+                  || i_y < i_y_start || i_y > i_y_end ) )
+            {
+             continue;
+            }
+
+         /* printf( "t: %x, y: %x, u: %x, v: %x\n", 
+            p_source->t, p_source->y, p_source->u, p_source->v ); */
+         
+         switch( p_source->t )
+            {
+           case 0x00: 
+             /* Completely transparent. Don't change underlying pixel */
+             break;
+             
+           case 0x0f:
+             /* Completely opaque. Completely overwrite underlying
+                pixel with subtitle pixel. */
+
+             p_destptr = p_dest - i_x - i_y;
+
+             i_colprecomp = (uint16_t) ( p_source->y * 15 );
+             *p_destptr = i_colprecomp >> 4;
+
+             break;
+             
+           default:
+             /* Blend in underlying pixel subtitle pixel. */
+
+             /* To be able to divide by 16 (>>4) we add 1 to the alpha.
+              * This means Alpha 0 won't be completely transparent, but
+              * that's handled in a special case above anyway. */
+
+             p_destptr = p_dest - i_x - i_y;
+
+             i_colprecomp = (uint16_t) ( (p_source->y 
+                                          * (uint16_t)(p_source->t + 1) ) );
+             i_destalpha = 15 - p_source->t;
+
+             *p_destptr = ( i_colprecomp +
+                              (uint16_t)*p_destptr * i_destalpha ) >> 4;
+             break;
+            }
+        }
+    }
+}
+