rnd-19981112-1
[rocksndiamonds.git] / src / pcx.c
index 582f4ce52530ac50ac4a7c5292f054d2de403a2d..f2dac1d9cfdb266bc669d54bab7a6a3770b4988b 100644 (file)
--- a/src/pcx.c
+++ b/src/pcx.c
 ***********************************************************/
 
 #include "image.h"
+#include "misc.h"
 
-#define PCX_MAGIC              0x0a    /* first byte in a PCX image file */
-#define PCX_LAST_VERSION       5       /* last acceptable version number */
-#define PCX_ENCODING           1       /* PCX encoding method */
+#define PCX_MAGIC              0x0a    /* first byte in a PCX image file    */
+#define PCX_LAST_VERSION       5       /* last acceptable version number    */
+#define PCX_ENCODING           1       /* PCX encoding method               */
 #define PCX_256COLORS_MAGIC    0x0c    /* first byte of a PCX 256 color map */
-#define PCX_MAXDEPTH           8       /* supports up to 8 bits per pixel */
-#define PCX_MAXCOLORS          256     /* maximum number of colors */
+#define PCX_MAXDEPTH           8       /* supports up to 8 bits per pixel   */
+#define PCX_MAXCOLORS          256     /* maximum number of colors          */
+
 #define PCX_HEADER_SIZE                128
-#define PCX_PALETTE_SIZE       (3 * PCX_MAXCOLORS)
+#define PCX_COLORMAP_SIZE      (3 * PCX_MAXCOLORS)
 
 struct PCX_Header
 {
@@ -42,48 +44,69 @@ struct PCX_Header
   unsigned char filler[58];    /* fill to struct size of 128          */
 };
 
-static boolean PCX_LoadImage();                /* Routine to load PCX bitmap */
-static boolean PCX_LoadColormap();     /* Routine to load PCX colormap */
+static byte *PCX_ReadBitmap(Image *, byte *, byte *);
+static byte *PCX_ReadColormap(Image *, byte *, byte *);
 
 Image *Read_PCX_to_Image(char *filename)
 {
   FILE *file;
-  unsigned char buffer[PCX_HEADER_SIZE];
+  byte *file_buffer;
+  byte *buffer_ptr, *buffer_last;
+  unsigned int file_length;
   struct PCX_Header pcx;
   Image *image;
   int width, height, depth;
+  int i;
 
   if (!(file = fopen(filename, "r")))
     return NULL;
 
-  /* read PCX header */
-  if (fread(buffer, 1, PCX_HEADER_SIZE, file) != PCX_HEADER_SIZE)
+  if (fseek(file, 0, SEEK_END) == -1)
   {
     fclose(file);
     return NULL;
   }
 
-  pcx.signature = buffer[0];
-  pcx.version = buffer[1];
-  pcx.encoding = buffer[2];
-  pcx.bits_per_pixel = buffer[3];
-  pcx.xmin = buffer[4]  + 256 * buffer[5];
-  pcx.ymin = buffer[6]  + 256 * buffer[7];
-  pcx.xmax = buffer[8]  + 256 * buffer[9];
-  pcx.ymax = buffer[10] + 256 * buffer[11];
-  pcx.color_planes = buffer[65];
-  pcx.bytes_per_line = buffer[66] + 256 * buffer[67];
-  pcx.palette_type = buffer[68] + 256 * buffer[69];
-
-  width = pcx.xmax - pcx.xmin + 1;
+  file_length = ftell(file);
+  rewind(file);
+
+  if (file_length < PCX_HEADER_SIZE + PCX_COLORMAP_SIZE)
+  {
+    fclose(file);
+    return NULL;
+  }
+
+  file_buffer = checked_malloc(file_length);
+
+  if (fread(file_buffer, 1, file_length, file) != file_length)
+  {
+    fclose(file);
+    return NULL;
+  }
+
+  fclose(file);
+
+  pcx.signature      = file_buffer[0];
+  pcx.version        = file_buffer[1];
+  pcx.encoding       = file_buffer[2];
+  pcx.bits_per_pixel = file_buffer[3];
+  pcx.xmin           = file_buffer[4]  + 256 * file_buffer[5];
+  pcx.ymin           = file_buffer[6]  + 256 * file_buffer[7];
+  pcx.xmax           = file_buffer[8]  + 256 * file_buffer[9];
+  pcx.ymax           = file_buffer[10] + 256 * file_buffer[11];
+  pcx.color_planes   = file_buffer[65];
+  pcx.bytes_per_line = file_buffer[66] + 256 * file_buffer[67];
+  pcx.palette_type   = file_buffer[68] + 256 * file_buffer[69];
+
+  width  = pcx.xmax - pcx.xmin + 1;
   height = pcx.ymax - pcx.ymin + 1;
-  depth = pcx.bits_per_pixel;
+  depth  = pcx.bits_per_pixel;
 
   if (pcx.signature != PCX_MAGIC || pcx.version > PCX_LAST_VERSION ||
       pcx.encoding != PCX_ENCODING || pcx.color_planes > PCX_MAXDEPTH ||
       width < 0 || height < 0)
   {
-    fclose(file);
+    free(file_buffer);
     return NULL;
   }
 
@@ -100,27 +123,42 @@ Image *Read_PCX_to_Image(char *filename)
   }
 
   /* allocate new image structure */
-  image = newRGBImage(width, height, depth);
+  image = newImage(width, height, depth);
+
+  buffer_ptr  = file_buffer + PCX_HEADER_SIZE;
+  buffer_last = file_buffer + file_length;
 
   /* read compressed bitmap data */
-  if (!PCX_LoadImage(file, image))
+  if ((buffer_ptr = PCX_ReadBitmap(image, buffer_ptr, buffer_last)) == NULL)
   {
-    fclose(file);
-    return image;
+    free(file_buffer);
+    freeImage(image);
+    return NULL;
   }
 
   /* read colormap data */
-  if (!PCX_LoadColormap(file, image))
+  if (!PCX_ReadColormap(image, buffer_ptr, buffer_last))
   {
-    fclose(file);
-    return image;
+    free(file_buffer);
+    freeImage(image);
+    return NULL;
   }
 
-  fclose(file);
-  return(image);
+  free(file_buffer);
+
+  /* determine number of used colormap entries */
+  image->rgb.used = 0;
+  for (i=0; i<PCX_MAXCOLORS; i++)
+    if (image->rgb.color_used[i])
+      image->rgb.used++;
+
+  if (options.verbose)
+    printf("Read_PCX_to_Image: %d colors found\n", image->rgb.used);
+
+  return image;
 }
 
-static boolean PCX_LoadImage(FILE *file, Image *image)
+static byte *PCX_ReadBitmap(Image *image, byte *buffer_ptr, byte *buffer_last)
 {
   /* Run Length Encoding: If the two high bits are set,
    * then the low 6 bits contain a repeat count, and the byte to
@@ -128,60 +166,65 @@ static boolean PCX_LoadImage(FILE *file, Image *image)
    * not set, then this is the byte to write.
    */
 
-  register unsigned char *ptr, *ptr_last;
-  int value, count;
+  unsigned int bytes_per_pixel = (image->depth + 7) / 8;
+  register byte *bitmap_ptr, *bitmap_last;
+  register byte value, count;
 
-  ptr = image->data;
-  ptr_last = ptr + (image->width * image->height * image->pixlen);
+  bitmap_ptr = image->data;
+  bitmap_last = bitmap_ptr + (image->width * image->height * bytes_per_pixel);
 
-  while (ptr < ptr_last)
+  while (bitmap_ptr < bitmap_last && buffer_ptr < buffer_last)
   {
-    if ((value = fgetc(file)) == EOF)
-      return FALSE;
+    value = *buffer_ptr++;
 
-    if ((value & 0xc0) == 0xc0)
+    if ((value & 0xc0) == 0xc0)                /* this is a repeat count byte */
     {
-      count = value & 0x3f;
+      count = value & 0x3f;            /* extract repeat count from byte */
+      value = *buffer_ptr++;           /* next byte is value to repeat */
 
-      if ((value = fgetc(file)) == EOF)
-       return FALSE;
+      for (; count && bitmap_ptr < bitmap_last; count--)
+       *bitmap_ptr++ = value;
 
-      for ( ; count && (ptr < ptr_last); count--)
-       *ptr++ = (unsigned char)value;
-
-      if (count)
-       printf("Repeat count spans end of image!\n");
+      if (count)                       /* repeat count spans end of bitmap */
+       return NULL;
     }
     else
-      *ptr++ = (unsigned char)value;
+      *bitmap_ptr++ = value;
+
+    image->rgb.color_used[value] = TRUE;
   }
 
-  return TRUE;
+  /* check if end of buffer was reached before end of bitmap */
+  if (bitmap_ptr < bitmap_last)
+    return NULL;
+
+  /* return current buffer position for next decoding function */
+  return buffer_ptr;
 }
 
-static boolean PCX_LoadColormap(FILE *file, Image *image)
+static byte *PCX_ReadColormap(Image *image,byte *buffer_ptr, byte *buffer_last)
 {
-  unsigned char buffer[PCX_PALETTE_SIZE];
-  int i, result, magic;
+  int i, magic;
 
   /* read colormap magic byte */
-  if ((magic = fgetc(file)) == EOF)
-    return FALSE;
+  magic = *buffer_ptr++;
 
+  /* check magic colormap header byte */
   if (magic != PCX_256COLORS_MAGIC)
-    return FALSE;
+    return NULL;
 
-  /* read PCX 256 colors colormap */
-  if ((result = fread(buffer, 1, PCX_PALETTE_SIZE, file)) != PCX_PALETTE_SIZE)
-    return FALSE;
+  /* check if enough bytes left for a complete colormap */
+  if (buffer_ptr + PCX_COLORMAP_SIZE > buffer_last)
+    return NULL;
 
+  /* read 256 colors from PCX colormap */
   for (i=0; i<PCX_MAXCOLORS; i++)
   {
-    image->rgb.red[i]   = buffer[i*3 + 0] << 8;
-    image->rgb.green[i] = buffer[i*3 + 1] << 8;
-    image->rgb.blue[i]  = buffer[i*3 + 2] << 8;
+    image->rgb.red[i]   = *buffer_ptr++ << 8;
+    image->rgb.green[i] = *buffer_ptr++ << 8;
+    image->rgb.blue[i]  = *buffer_ptr++ << 8;
   }
-  image->rgb.used = PCX_MAXCOLORS;
 
-  return TRUE;
+  /* return current buffer position for next decoding function */
+  return buffer_ptr;
 }