Logo Search packages:      
Sourcecode: aegis version File versions

open.c

/*
 *    aegis - project change supervisor
 *    Copyright (C) 1998, 1999, 2002 Peter Miller;
 *    All rights reserved.
 *
 *    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.
 *
 * MANIFEST: functions to open GIF files
 */

#include <ac/stdio.h>
#include <ac/string.h>

#include <error.h>
#include <gif.h>
#include <gif/private.h>
#include <mem.h>
#include <sub.h>
#include <trace.h>

/*
 * This symbol is defined if you want the code to tolerate
 * some really stupid deviations from the stated GIF standard.
 */
#define TOLERATE_FOOLS


typedef struct table_ty table_ty;
struct table_ty
{
    int               emit;
    int               chain;
};

enum state_ty
{
    state_normal,
    state_before_clear,
    state_after_clear
};
typedef enum state_ty state_ty;


static void
format_error(char *fn)
{
    sub_context_ty  *scp;

    scp = sub_context_new();
    sub_var_set_charstar(scp, "File_Name", fn);
    fatal_intl(scp, i18n("read $filename: format error"));
    /* NOTREACHED */
}


static int
gif_getc(FILE *fp, char *fn)
{
    int               c;

    c = getc(fp);
    if (c == EOF)
    {
      if (ferror(fp))
      {
          sub_context_ty  *scp;

          scp = sub_context_new();
          sub_errno_set(scp);
          sub_var_set_charstar(scp, "File_Name", fn);
          fatal_intl(scp, i18n("read $filename: $errno"));
          /* NOTREACHED */
      }
      format_error(fn);
    }
    return c;
}


static void
read_image(FILE *fp, char *fn, int row_bytes, unsigned char *data, int width,
    int height, int interlaced)
{
    int               c;
    int               unpack_buffer;
    int               unpack_buffer_len;
    int               unpack_runlen;

    int               clear_cmd;
    int               end_cmd;

    int               code_prev = 0; /* last chain */
    int               code_cur; /* current chain */
    int               code_size;
    int               code_size_minimum;
    int               code_mask;
    int               pixel_prev = 0; /* last pixel of last chain */
    int               pixel_cur; /* last pixel of current chain (eventually) */
    int               phase;
    int               x;
    int         y;
    unsigned char   *dp;

    table_ty          table[1 << MAX_BITS];
    int               table_length =  0;
    int               table_pos_minimum;
    int               table_pos =       0;

    state_ty          state;

    unsigned char   lifo[1 << MAX_BITS];
    unsigned char   *lifo_ptr;

    c = gif_getc(fp, fn);
    trace(("code_size = %d;", c));
    if (c < 2 || c > 8)
    {
      /*
       * bad code size
       */
      format_error(fn);
    }
    code_size = c;
    clear_cmd = (1 << code_size);
    end_cmd = clear_cmd + 1;
    table_pos_minimum = clear_cmd + 2;

    code_size++;
    code_size_minimum = code_size;
    code_mask = (1 << code_size) - 1;

    x = 0;
    y = 0;
    phase = 0;
    dp = data;
    unpack_buffer = 0;
    unpack_buffer_len = 0;
    unpack_runlen = 0;
    state = state_before_clear;
    lifo_ptr = lifo;
    for (;;)
    {
      /*
       * read in the next code
       */
      while (unpack_buffer_len < code_size)
      {
          if (!unpack_runlen)
          {
            unpack_runlen = gif_getc(fp, fn);
            if (!unpack_runlen)
            {
                /*
                 * zero length packet too early
                 */
                format_error(fn);
            }
          }
          c = gif_getc(fp, fn);
          --unpack_runlen;
          unpack_buffer |= c << unpack_buffer_len;
          unpack_buffer_len += 8;
      }
      code_cur = unpack_buffer & code_mask;
      trace(("code_cur = 0x%.*X", (code_size+3)/4, code_cur));
      unpack_buffer >>= code_size;
      unpack_buffer_len -= code_size;

      /*
       * see if we are finished
       */
      if (code_cur == end_cmd)
          break;

      /*
       * see if it is a clear code
       * reset everything
       */
      if (code_cur == clear_cmd)
      {
          code_size = code_size_minimum;
          table_length = (1 << code_size);
          code_mask = table_length - 1;
          trace(("code_size = %d;", code_size));
          trace(("table_length = %d;", table_length));
          trace(("code_mask = 0x%04X;", code_mask));
          table_pos = table_pos_minimum;
          trace(("table_pos = 0x%02X;", table_pos));
          state = state_after_clear;
          continue;
      }
      switch ((int)state)
      {
      case state_before_clear:
          /*
           * clear code missing
           */
          trace(("clear code missing\n"));
          format_error(fn);
          break;

      case state_after_clear:
          /*
           * immediately after clear
           * we must also initialize the "last" info
           * Also, the data after a clear MUST be raw.
           */
          if (code_cur > code_mask)
          {
            /*
             * code after clear must be raw pixel
             */
            trace(("code after clear must be raw pixel\n"));
            format_error(fn);
          }
          pixel_prev = code_cur;
          trace(("pixel_prev = 0x%03X;", pixel_prev));
          break;
      }

      /*
       * initially, pixel_cur is the table index
       */
      pixel_cur = code_cur;

      /*
       * if it is the next element in the table,
       * then repeat the last pixel and the last chain.
       */
      if (pixel_cur > table_pos)
      {
          /*
           * code off end of table
           */
          trace(("code off end of table\n"));
          format_error(fn);
      }
      if (pixel_cur >= table_pos)
      {
          pixel_cur = code_prev;
          *lifo_ptr++ = pixel_prev;
      }

      /*
       * follow the chain through the table
       */
      while (pixel_cur >= clear_cmd)
      {
          *lifo_ptr++ = table[pixel_cur].emit;
          pixel_cur = table[pixel_cur].chain;
      }
      pixel_prev = pixel_cur;
      trace(("pixel_prev = 0x%03X;", pixel_prev));
      *lifo_ptr++ = pixel_cur;

      /*
       * emit the sequence, in reverse
       */
      while (lifo_ptr > lifo)
      {
          if (y >= height)
          {
            /*
             * too many pixels!
             */
            trace(("too many pixels\n"));
            format_error(fn);
          }
          *dp++ = *--lifo_ptr;
          trace(("pixel[%d][%d] = 0x%02X;", y, x, dp[-1]));
          if (++x >= width)
          {
            x = 0;
            if (interlaced)
            {
                switch (phase)
                {
                case 0:
                  y += 8;
                  if (y >= height)
                  {
                      phase++;
                      y = 4;
                  }
                  break;

                case 1:
                  y += 8;
                  if (y >= height)
                  {
                      phase++;
                      y = 2;
                  }
                  break;

                case 2:
                  y += 4;
                  if (y >= height)
                  {
                      phase++;
                      y = 1;
                  }
                  break;

                case 3:
                  y += 2;
                  if (y >= height)
                      y = height;
                  break;
                }
            }
            else
                ++y;
            dp = data + y * row_bytes;
          }
      }

      /*
       * add the next element to the table
       */
      if (state == state_normal)
      {
          if (table_pos >= (1 << MAX_BITS))
          {
            /*
             * tries to write code off end of table
             */
            trace(("tries to write code off end of table"));
            format_error(fn);
          }
          trace(("table_pos = 0x%02X;", table_pos));
          table[table_pos].chain = code_prev;
          trace(("table[0x%03X].chain = 0x%03X;", table_pos, code_prev));
          table[table_pos].emit = pixel_cur;
          trace(("table[0x%03X].emit = 0x%03X;", table_pos, pixel_cur));
          table_pos++;
      }
      code_prev = code_cur;
      trace(("code_prev = 0x%03X;", code_prev));
      state = state_normal;

      /*
       * move the table position on
       */
      if (table_pos >= table_length)
      {
          if (code_size < MAX_BITS)
          {
            code_size++;
            table_length <<= 1;
            code_mask = table_length - 1;
            trace(("code_size = %d;", code_size));
            trace(("table_length = %d;", table_length));
            trace(("code_mask = 0x%04X;", code_mask));
          }
          else
            state = state_before_clear;
      }
    }

    /*
     * check that we were given all of the lines
     *
     * Many implementations don't write the last line
     * (probably anencephalic programmers),
     * and some assume that absent lines mean pixel number 0.
     */
#ifndef TOLERATE_FOOLS
    if (y != height)
    {
      trace(("too few pixels (y = %d, height, x = %d, width = %d)\n",
          y, height, x, width));
      format_error(fn);
    }
#endif

    /*
     * check that all of last packet was used
     *
     * Many imlementations always write 255 byte packets,
     * even when the last packet is only partially full.
     */
#ifndef TOLERATE_FOOLS
    if (unpack_runlen)
    {
      trace(("last packet not all used"));
      format_error(fn);
    }
#endif

    /*
     * Check for zero-length packet at end of image.
     *
     * Many implementations leave it out.
     */
    c = gif_getc(fp, fn);
    if (c)
    {
#ifdef TOLERATE_FOOLS
      ungetc(c, fp);
#else
      trace(("image 0 terminator missing"));
      format_error(fn);
#endif
    }
}


static int
get_le_short(FILE *fp, char *fn)
{
    int               c1;
    int               c2;

    /*
     * must use temp vars
     * to guarantee order of evaluation
     * (see ANSI C std, discussion of sequence points)
     */
    c1 = gif_getc(fp, fn);
    c2 = gif_getc(fp, fn);
    return (c1 + (c2 << 8));
}


gif_ty *
gif_open(char *fn, int mode)
{
    gif_ty      *result;
    int               j;
    FILE        *fp;
    unsigned char   *cp;
    int               c;
    int               bits_per_pixel;
    int               read_colormap;

    trace(("gif_open(fn = \"%s\", mode = %d)", fn, mode));

    /*
     * initialize things
     */
    result = mem_alloc(sizeof(gif_ty));
    for (j = 0; j < 256; ++j)
    {
      result->colormap[j][0] = j;
      result->colormap[j][1] = j;
      result->colormap[j][2] = j;
    }
    result->image_flat = 0;
    result->image = 0;
    result->mode = mode;
    result->mime = 0;

    /*
     * open the file
     */
    if (fn)
    {
      result->fn = mem_copy_string(fn);
      fp = fopen(fn, "rb");
      if (!fp)
      {
          sub_context_ty  *scp;

          scp = sub_context_new();
          sub_errno_set(scp);
          sub_var_set_charstar(scp, "File_Name", fn);
          fatal_intl(scp, i18n("open $filename: $errno"));
          /* NOTREACHED */
      }
    }
    else
    {
      result->fn = 0;
      fn = "standard input";
      fp = stdin;
    }

    /*
     * make sure the magic number is there
     */
    for (cp = gif_magic_number; *cp; ++cp)
    {
      c = gif_getc(fp, fn);
      if (c != *cp)
          format_error(fn);
    }

    /*
     * get the size
     */
    result->width = get_le_short(fp, fn);
    if (!result->width || result->width > SIZE_MAX)
      format_error(fn);
    result->height = get_le_short(fp, fn);
    if (!result->height || result->height > SIZE_MAX)
      format_error(fn);

    /*
     * allocate the image
     */
    result->image_flat =
      mem_alloc((size_t)result->width * (size_t)result->height);
    result->image = mem_alloc((size_t)result->height * sizeof(unsigned char *));
    for (j = 0; j < result->height; ++j)
      result->image[j] = result->image_flat + j * result->width;

    /*
     * get encoding info
     */
    c = gif_getc(fp, fn);
    bits_per_pixel = 1 + (c & 7);
    if (c & 0x80)
      read_colormap = 1 << bits_per_pixel;
    else
      read_colormap = 0;

    /*
     * get background color
     */
    c = gif_getc(fp, fn);
    memset
    (
      result->image_flat,
      c,
      (size_t)result->width * (size_t)result->height
    );

    /*
     * this byte should be zero
     */
    c = gif_getc(fp, fn);
    if (c != 0)
      format_error(fn);

    /*
     * read the colormap
     */
    if (read_colormap)
    {
      for (j = 0; j < read_colormap; ++j)
          for (c = 0; c < 3; ++c)
            result->colormap[j][c] = gif_getc(fp, fn);
      }

    /*
     * read the image
     */
    for (;;)
    {
      c = gif_getc(fp, fn);
      switch (c)
      {
      default:
          format_error(fn);
          break;

      case ';':
          if (fp != stdin)
            fclose(fp);
          return result;

      case '!':
          /*
           * extension block - ignore
           */
          c = gif_getc(fp, fn);
          while (c > 0)
          {
            gif_getc(fp, fn);
            --c;
          }
          break;

      case ',':
          {
            int         image_left;
            int         image_top;
            int         image_width;
            int         image_height;
            int         flags;
            int         interlaced;

            /*
             * image specifier
             */
            image_left = get_le_short(fp, fn);
            if (image_left >= result->width)
                format_error(fn);
            image_top = get_le_short(fp, fn);
            if (image_top >= result->height)
                format_error(fn);
            image_width = get_le_short(fp, fn);
            if (image_left + image_width > result->width)
                format_error(fn);
            image_height = get_le_short(fp, fn);
            if (image_top + image_height > result->height)
                format_error(fn);
            flags = gif_getc(fp, fn);
            if (flags & 0x80)
            {
                /*
                 * uses local color map,
                 * which we can't cope with
                 */
                format_error(fn);
            }
            interlaced = (flags & 0x40) != 0;

            read_image
            (
                fp,
                fn,
                result->width,
                result->image_flat + image_left + image_top * result->width,
                image_width,
                image_height,
                interlaced
            );
          }
          break;
      }
    }
}

Generated by  Doxygen 1.6.0   Back to index