changeset 4687:2973e997c4a5

some obscure bug fixes to the FLI decoder, with many thanks to Roberto Togni
author melanson
date Wed, 13 Feb 2002 04:06:55 +0000
parents b49325176f0d
children 68582b4ce881
files dec_video.c fli.c
diffstat 2 files changed, 97 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/dec_video.c	Wed Feb 13 03:14:45 2002 +0000
+++ b/dec_video.c	Wed Feb 13 04:06:55 2002 +0000
@@ -118,13 +118,16 @@
   unsigned char *palette_map,
   int bytes_per_pixel);
 
-void Decode_Fli(
+void *init_fli_decoder(int width, int height);
+
+void decode_fli_frame(
   unsigned char *encoded,
   int encoded_size,
   unsigned char *decoded,
   int width,
   int height,
-  int bytes_per_pixel);
+  int bytes_per_pixel,
+  void *context);
 
 void qt_decode_rle(
   unsigned char *encoded,
@@ -712,8 +715,9 @@
      }
    }
    break;
+ case VFM_FLI:
+   sh_video->context = init_fli_decoder(sh_video->disp_w, sh_video->disp_h);
  case VFM_MSVIDC:
- case VFM_FLI:
  case VFM_QTRLE:
  case VFM_DUCKTM1:
 #ifdef HAVE_PNG
@@ -1117,10 +1121,11 @@
     blit_frame = 3;
     break;
   case VFM_FLI:
-    Decode_Fli(
+    decode_fli_frame(
         start, in_size, sh_video->our_out_buffer,
         sh_video->disp_w, sh_video->disp_h,
-        ((out_fmt&255)+7)/8);
+        ((out_fmt&255)+7)/8,
+        sh_video->context);
     blit_frame = 3;
     break;
   case VFM_NUV:
--- a/fli.c	Wed Feb 13 03:14:45 2002 +0000
+++ b/fli.c	Wed Feb 13 04:06:55 2002 +0000
@@ -4,11 +4,19 @@
     (C) 2001 Mike Melanson
     
     32bpp support (c) alex
+
+    Additional code and bug fixes by Roberto Togni
+
+    For information on the FLI format, as well as various traps to
+    avoid while programming one, visit:
+      http://www.pcisys.net/~melanson/codecs/
 */
 
+#include <stdio.h>
+#include <stdlib.h>
 #include "config.h"
 #include "bswap.h"
-#include <stdio.h>
+#include "mp_msg.h"
 
 #define LE_16(x) (le2me_16(*(unsigned short *)(x)))
 #define LE_32(x) (le2me_32(*(unsigned int *)(x)))
@@ -24,15 +32,24 @@
 
 // 256 RGB entries; 25% of these bytes will be unused, but it's faster
 // to index 4-byte entries
-static unsigned char palette[256 * 4];
+#define PALETTE_SIZE 1024
+static unsigned char palette[PALETTE_SIZE];
 
-void Decode_Fli(
+void *init_fli_decoder(int width, int height)
+{
+  memset(palette, 0, PALETTE_SIZE);
+
+  return malloc(width * height * sizeof (unsigned int));
+}
+
+void decode_fli_frame(
   unsigned char *encoded,
   int encoded_size,
   unsigned char *decoded,
   int width,
   int height,
-  int bytes_per_pixel)
+  int bytes_per_pixel,
+  void *context)
 {
   int stream_ptr = 0;
   int pixel_ptr;
@@ -56,9 +73,14 @@
   int starting_line;
   signed short line_packets;
   int y_ptr;
-  int line_inc;
+  int line_inc = width * bytes_per_pixel;
   signed char byte_run;
-
+  int pixel_skip;
+  int update_whole_frame = 0; // Palette change flag
+  unsigned int *fli_ghost_image = (unsigned int *)context;
+  int ghost_pixel_ptr;
+  int ghost_y_ptr;
+	
   frame_size = LE_32(&encoded[stream_ptr]);
   stream_ptr += 6;  // skip the magic number
   num_chunks = LE_16(&encoded[stream_ptr]);
@@ -89,6 +111,9 @@
       {
         // first byte is how many colors to skip
         palette_ptr1 += (encoded[stream_ptr++] * 4);
+        // wrap around, for good measure
+        if (palette_ptr1 >= PALETTE_SIZE)
+          palette_ptr1 = 0;
         // next byte indicates how many entries to change
         color_changes = encoded[stream_ptr++];
         // if there are 0 color changes, there are actually 256
@@ -107,11 +132,12 @@
       // so account for a pad byte
       if (stream_ptr & 0x01)
         stream_ptr++;
+      /* Palette has changed, must update frame */
+      update_whole_frame = 1;
       break;
 
     case FLI_DELTA:
-      line_inc = width * bytes_per_pixel;
-      y_ptr = 0;
+      y_ptr = ghost_y_ptr = 0;
       compressed_lines = LE_16(&encoded[stream_ptr]);
       stream_ptr += 2;
       while (compressed_lines > 0)
@@ -122,14 +148,18 @@
         {
           line_packets = -line_packets;
           y_ptr += (line_packets * line_inc);
+          ghost_y_ptr += (line_packets * width);
         }
         else
         {
           pixel_ptr = y_ptr;
+          ghost_pixel_ptr = ghost_y_ptr;
           for (i = 0; i < line_packets; i++)
           {
             // account for the skip bytes
-            pixel_ptr += encoded[stream_ptr++] * bytes_per_pixel;
+            pixel_skip = encoded[stream_ptr++];
+            pixel_ptr += pixel_skip * bytes_per_pixel;
+            ghost_pixel_ptr += pixel_skip;
             byte_run = encoded[stream_ptr++];
             if (byte_run < 0)
             {
@@ -138,12 +168,14 @@
               palette_ptr2 = encoded[stream_ptr++] * 4;
               for (j = 0; j < byte_run; j++)
               {
+                fli_ghost_image[ghost_pixel_ptr++] = palette_ptr1;
                 decoded[pixel_ptr++] = palette[palette_ptr1 + 0];
                 decoded[pixel_ptr++] = palette[palette_ptr1 + 1];
                 decoded[pixel_ptr++] = palette[palette_ptr1 + 2];
 		if (bytes_per_pixel == 4) /* 32bpp */
 		    pixel_ptr++;
 
+                fli_ghost_image[ghost_pixel_ptr++] = palette_ptr2;
                 decoded[pixel_ptr++] = palette[palette_ptr2 + 0];
                 decoded[pixel_ptr++] = palette[palette_ptr2 + 1];
                 decoded[pixel_ptr++] = palette[palette_ptr2 + 2];
@@ -156,6 +188,7 @@
               for (j = 0; j < byte_run * 2; j++)
               {
                 palette_ptr1 = encoded[stream_ptr++] * 4;
+                fli_ghost_image[ghost_pixel_ptr++] = palette_ptr1;
                 decoded[pixel_ptr++] = palette[palette_ptr1 + 0];
                 decoded[pixel_ptr++] = palette[palette_ptr1 + 1];
                 decoded[pixel_ptr++] = palette[palette_ptr1 + 2];
@@ -165,6 +198,7 @@
             }
           }
           y_ptr += line_inc;
+          ghost_y_ptr += width;
           compressed_lines--;
         }
       }
@@ -172,29 +206,33 @@
 
     case FLI_LC:
       // line compressed
-      line_inc = width * bytes_per_pixel;
       starting_line = LE_16(&encoded[stream_ptr]);
       stream_ptr += 2;
       y_ptr = starting_line * line_inc;
+      ghost_y_ptr = starting_line * width;
 
       compressed_lines = LE_16(&encoded[stream_ptr]);
       stream_ptr += 2;
       while (compressed_lines > 0)
       {
         pixel_ptr = y_ptr;
+        ghost_pixel_ptr = ghost_y_ptr;
         line_packets = encoded[stream_ptr++];
         if (line_packets > 0)
         {
           for (i = 0; i < line_packets; i++)
           {
             // account for the skip bytes
-            pixel_ptr += encoded[stream_ptr++] * bytes_per_pixel;
+            pixel_skip = encoded[stream_ptr++];
+            pixel_ptr += pixel_skip * bytes_per_pixel;
+            ghost_pixel_ptr += pixel_skip;
             byte_run = encoded[stream_ptr++];
             if (byte_run > 0)
             {
               for (j = 0; j < byte_run; j++)
               {
                 palette_ptr1 = encoded[stream_ptr++] * 4;
+                fli_ghost_image[ghost_pixel_ptr++] = palette_ptr1;
                 decoded[pixel_ptr++] = palette[palette_ptr1 + 0];
                 decoded[pixel_ptr++] = palette[palette_ptr1 + 1];
                 decoded[pixel_ptr++] = palette[palette_ptr1 + 2];
@@ -208,6 +246,7 @@
               palette_ptr1 = encoded[stream_ptr++] * 4;
               for (j = 0; j < byte_run; j++)
               {
+                fli_ghost_image[ghost_pixel_ptr++] = palette_ptr1;
                 decoded[pixel_ptr++] = palette[palette_ptr1 + 0];
                 decoded[pixel_ptr++] = palette[palette_ptr1 + 1];
                 decoded[pixel_ptr++] = palette[palette_ptr1 + 2];
@@ -219,29 +258,26 @@
         }
 
         y_ptr += line_inc;
+        ghost_y_ptr += width;
         compressed_lines--;
       }
       break;
 
     case FLI_BLACK:
-      // set the whole frame to color 0 (which is usually black)
-      for (pixel_ptr = 0; pixel_ptr < (width * height * bytes_per_pixel); pixel_ptr++)
-      {
-        decoded[pixel_ptr++] = palette[0];
-        decoded[pixel_ptr++] = palette[1];
-        decoded[pixel_ptr++] = palette[2];
-	if (bytes_per_pixel == 4) /* 32bpp */
-	    pixel_ptr++;
-      }
+      // set the whole frame to color 0 (which is usually black) by
+      // clearing the ghost image and trigger a full frame update
+      memset(fli_ghost_image, 0, width * height * sizeof(unsigned int));
+      update_whole_frame = 1;
       break;
 
     case FLI_BRUN:
       // byte run compression
-      line_inc = width * bytes_per_pixel;
       y_ptr = 0;
+      ghost_y_ptr = 0;
       for (lines = 0; lines < height; lines++)
       {
         pixel_ptr = y_ptr;
+        ghost_pixel_ptr = ghost_y_ptr;
         line_packets = encoded[stream_ptr++];
         for (i = 0; i < line_packets; i++)
         {
@@ -251,6 +287,7 @@
             palette_ptr1 = encoded[stream_ptr++] * 4;
             for (j = 0; j < byte_run; j++)
             {
+              fli_ghost_image[ghost_pixel_ptr++] = palette_ptr1;
               decoded[pixel_ptr++] = palette[palette_ptr1 + 0];
               decoded[pixel_ptr++] = palette[palette_ptr1 + 1];
               decoded[pixel_ptr++] = palette[palette_ptr1 + 2];
@@ -264,6 +301,7 @@
             for (j = 0; j < byte_run; j++)
             {
               palette_ptr1 = encoded[stream_ptr++] * 4;
+              fli_ghost_image[ghost_pixel_ptr++] = palette_ptr1;
               decoded[pixel_ptr++] = palette[palette_ptr1 + 0];
               decoded[pixel_ptr++] = palette[palette_ptr1 + 1];
               decoded[pixel_ptr++] = palette[palette_ptr1 + 2];
@@ -274,20 +312,16 @@
         }
 
         y_ptr += line_inc;
+        ghost_y_ptr += width;
       }
       break;
 
     case FLI_COPY:
-      // copy the chunk (uncompressed frame)
-      for (pixel_ptr = 0; pixel_ptr < chunk_size - 6; pixel_ptr++)
-      {
-        palette_ptr1 = encoded[stream_ptr++] * 4;
-        decoded[pixel_ptr++] = palette[palette_ptr1 + 0];
-        decoded[pixel_ptr++] = palette[palette_ptr1 + 1];
-        decoded[pixel_ptr++] = palette[palette_ptr1 + 2];
-        if (bytes_per_pixel == 4) /* 32bpp */
-          pixel_ptr++;
-      }
+      // copy the chunk (uncompressed frame) to the ghost image and
+      // schedule the whole frame to be updated
+      memcpy(fli_ghost_image, &encoded[stream_ptr], chunk_size - 6);
+      stream_ptr += chunk_size - 6;
+      update_whole_frame = 1;
       break;
 
     case FLI_MINI:
@@ -303,4 +337,26 @@
     frame_size -= chunk_size;
     num_chunks--;
   }
+
+  if (update_whole_frame)
+  {
+    pixel_ptr = 0;
+    while (pixel_ptr < (width * height * bytes_per_pixel))
+    {
+      palette_ptr1 = fli_ghost_image[pixel_ptr/bytes_per_pixel];
+      decoded[pixel_ptr++] = palette[palette_ptr1 + 0];
+      decoded[pixel_ptr++] = palette[palette_ptr1 + 1];
+      decoded[pixel_ptr++] = palette[palette_ptr1 + 2];
+      if (bytes_per_pixel == 4) /* 32bpp */
+        pixel_ptr++;
+    }
+  }
+
+  // by the end of the chunk, the stream ptr should equal the frame 
+  // size (minus 1, possible); if it doesn't, issue a warning
+  if ((stream_ptr != encoded_size) && (stream_ptr != encoded_size - 1))
+    mp_msg(MSGT_DECVIDEO, MSGL_WARN,
+      "  warning: processed FLI chunk where encoded size = %d\n" \
+      "  and final chunk ptr = and final chunk ptr = %d\n",
+      encoded_size, stream_ptr);
 }