Decoding GIF file in C? [Source code here] 
Author Message
 Decoding GIF file in C? [Source code here]


Quote:

>Hi there,

>    I am desperately looking for a C code that could decode a GIF file
>        in DOS.  Anybody out there who knows how to do it?  I will really
>    appreciate any help.

The following code is based on a LZW decoder originally coded by Steven A.
Bennett. All I did is write the code for parsing the GIF format, passing
the approptiate data to the LZW decoder and writing the code for displaying
the data on the screen. Both 87a and 89a formats are accepted.

Video BIOS must support VESA functions - the code selects a video mode
whith a resolution just high enough to display the given GIF file. Max
resolution is 1280x1024, pictures larger than this will be partially
displayed. Interlaced GIFs are supported. Program was only tested on my
video card (Cirrus 5428 VESA Local Bus). It was not intended for public
release but since I've already written it and the guy i desperate...

Compiles with Borland C++ v3.1

----------------------- chop here --------------------------

/* Various error codes used by decoder
 * and my own routines...   It's okay
 * for you to define whatever you want,
 * as long as it's negative...  It will be
 * returned intact up the various subroutine
 * levels...
 */
#define READ_ERROR      -1
#define WRITE_ERROR     -2
#define OPEN_ERROR      -3
#define CREATE_ERROR    -4
#define OUT_OF_MEMORY   -5
#define BAD_CODE_SIZE   -6

#define ISIZE           2048            // image line size
#define BSIZE           256             // buffer size
#define PSIZE           768             // pallette size
#define NULL            0L
#define MAX_CODES       4095

#include <dos.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Static variables */
short curr_size;                     /* The current code size */
short clear;                         /* Value for a clear code */
short ending;                        /* Value for a ending code */
short newcodes;                      /* First available code */
short top_slot;                      /* Highest code for current size */
short slot;                          /* Last read code */

/* The following static variables are used
 * for seperating out codes
 */
short navail_bytes = 0;              /* # bytes left in block */
short nbits_left = 0;                /* # bits left in current byte */
unsigned char b1;                           /* Current byte */
unsigned char byte_buff[257];               /* Current block */
unsigned char *pbytes;                      /* Pointer to next byte in block */

/* The reason we have these separated like this instead of using
 * a structure like the original Wilhite code did, is because this
 * stuff generally produces significantly faster code when compiled...
 * This code is full of similar speedups...  (For a good book on writing
 * C for speed or for space optimisation, see Efficient C by Tom Plum,
 * published by Plum-Hall Associates...)
 */
unsigned char stack[MAX_CODES + 1];            /* Stack for storing pixels */
unsigned char suffix[MAX_CODES + 1];           /* Suffix table */
unsigned short prefix[MAX_CODES + 1];           /* Prefix linked list */

long code_mask[13] = {
     0x0000,
     0x0001, 0x0003, 0x0007, 0x000F,
     0x001F, 0x003F, 0x007F, 0x00FF,
     0x01FF, 0x03FF, 0x07FF, 0x0FFF
     };

// incremented each time an out of range code is read by the decoder
int bad_code_count;

unsigned char far *buffer=NULL;         // file buffer
unsigned char far *grgb=NULL;           // global rgb table
unsigned char far *lrgb=NULL;           // local rgb table
unsigned char far *pixels=NULL;         // line of pixels to be displayed
unsigned char far *vid=NULL;            // ptr to start of graphics window
unsigned char background=0;             // background color index
unsigned long offset=0;                 // offset into graphics window
unsigned long piclen;                   // total screen size in pixels
unsigned int  realx;                    // real picture width
unsigned int  realy;                    // real picture height
unsigned int  xsize=0;                  // graphic width
unsigned int  ysize=0;                  // graphic height
unsigned int  win=0;                    // window number
unsigned int  granularity=0;            // granularity of video window
unsigned int  group=0;                  // picture group (interlaced or not)
FILE *fp;                               // file pointer

/* int get_byte()
 *   - This function is expected to return the next byte from
 *     the GIF file or a negative number defined in ERRS.H
 */
int get_byte(){
  unsigned char c;

  if(fscanf(fp, "%c", &c)==1)
    return((int)c);
  else
    return(READ_ERROR);

Quote:
}

/* int out_line(unsigned char pixels[], int linelen)
 *   - This function takes a full line of pixels (one byte per pixel) and
 * displays them (or does whatever your program wants with them...).  It
 * should return zero, or negative if an error or some other event occurs
 * which would require aborting the decode process...  Note that the length
 * passed will almost always be equal to the line length passed to the
 * decoder function, with the sole exception occurring when an ending code
 * occurs in an odd place in the GIF file...  In any case, linelen will be
 * equal to the number of pixels passed...
 */
int out_line(unsigned char *pixels, int linelen){
  unsigned long segment;

  segment=offset&(unsigned long)(granularity-1);

  // put pixels on screen
  memmove(vid+segment, pixels, linelen);
  memset(vid+segment+linelen, background, xsize-linelen);
  switch (group) {
    case 0:
      offset+=xsize;
      break;
    case 1:
      offset+=xsize*8;
      if(offset>=piclen){
        group++;
        offset=xsize*4;
      }
      break;
    case 2:
      offset+=xsize*8;
      if(offset>=piclen){
        group++;
        offset=xsize*2;
      }
      break;
    case 3:
      offset+=xsize*4;
      if(offset>=piclen){
        group++;
        offset=xsize;
      }
      break;
    case 4:
      offset+=xsize*2;
      break;
    default:
      break;
  }

  // if we've run over a window granularity border, move window position
  if((offset>>12)!=win){
    win=offset>>12;
    asm pusha
    asm mov ax, 0x4f05
    asm mov bx, 0x0000
    asm mov dx, win
    asm int 0x10
    asm popa
  }
  return(1);

Quote:
}

/* This function initializes the decoder for reading a new image.
 */
static short init_exp(short size)
   {
   curr_size = size + 1;
   top_slot = 1 << curr_size;
   clear = 1 << size;
   ending = clear + 1;
   slot = newcodes = ending + 1;
   navail_bytes = nbits_left = 0;
   return(0);
   }

/* get_next_code()
 * - gets the next code from the GIF file.  Returns the code, or else
 * a negative number in case of file errors...
 */
static short get_next_code()
   {
   short i, x;
   unsigned long ret;

   if (nbits_left == 0)
      {
      if (navail_bytes <= 0)
         {

         /* Out of bytes in current block, so read next block
          */
         pbytes = byte_buff;
         if ((navail_bytes = get_byte()) < 0)
            return(navail_bytes);
         else if (navail_bytes)
            {
            for (i = 0; i < navail_bytes; ++i)
               {
               if ((x = get_byte()) < 0)
                  return(x);
               byte_buff[i] = x;
               }
            }
         }
      b1 = *pbytes++;
      nbits_left = 8;
      --navail_bytes;
      }

   ret = b1 >> (8 - nbits_left);
   while (curr_size > nbits_left)
      {
      if (navail_bytes <= 0)
         {

         /* Out of bytes in current block, so read next block
          */
         pbytes = byte_buff;
         if ((navail_bytes = get_byte()) < 0)
            return(navail_bytes);
         else if (navail_bytes)
            {
            for (i = 0; i < navail_bytes; ++i)
               {
               if ((x = get_byte()) < 0)
                  return(x);
               byte_buff[i] = x;
               }
            }
         }
      b1 = *pbytes++;
      ret |= b1 << nbits_left;
      nbits_left += 8;
      --navail_bytes;
      }
   nbits_left -= curr_size;
   ret &= code_mask[curr_size];
   return((short)(ret));
   }

/* DECODE.C - An LZW decoder for GIF
 *
 * Copyright (C) 1987, by Steven A. Bennett
 *
 * Permission is given by the author to freely redistribute and include
 * this code in any program as long as this credit is given where due.
 *
 * In accordance with the above, I want to credit Steve Wilhite who wrote
 * the code which this is heavily inspired by...
 *
 * GIF and 'Graphics Interchange Format' are trademarks (tm) of
 * Compuserve, Incorporated, an H&R Block Company.
 *
 * Release Notes: This file contains a decoder routine for GIF images
 * which is similar, structurally, to the original routine by Steve Wilhite.
 * It is, however, somewhat noticably faster in most cases.
 *
 * short decode(linewidth)
 *    short linewidth;               * Pixels per line of image *
 *
 * - This function decodes an LZW image, according to the method used
 * in the GIF spec.  Every *linewidth* "characters" (ie. pixels) decoded
 * will generate a call to out_line(), which is a user specific function
 * to display a line of pixels.  The function gets it's codes from
 * get_next_code() which is responsible for reading blocks of data and
 * seperating them into the proper size codes.  Finally, get_byte() is
 * the global routine to read the next byte from the GIF file.
 *
 * It is generally a good idea to have linewidth correspond to the actual
 * width of a line (as specified in the Image header) to make your own
 * code a bit simpler, but it isn't absolutely necessary.
 *
 * Returns: 0 if successful, else negative.  (See ERRS.H)
 *
 */

short decode(short linewidth)
   {
   register unsigned char *sp, *bufptr;
   unsigned char *buf;
   register short code, fc, oc, bufcnt;
   short c, size, ret;

   /* Initialize for decoding a new image...
    */
   if ((size = get_byte()) < 0)
      return(size);
   if (size < 2 || 9 < size)
      return(BAD_CODE_SIZE);
   init_exp(size);

   /* Initialize in case they forgot to put in a clear code.
    * (This shouldn't happen, but we'll try and decode it anyway...)
    */
   oc = fc = 0;

   /* Allocate space for the decode buffer
    */
   if ((buf = (unsigned char *)malloc(linewidth + 1)) == NULL)
      return(OUT_OF_MEMORY);

   /* Set up the stack pointer and decode buffer pointer
    */
   sp = stack;
   bufptr = buf;
   bufcnt = linewidth;

   /* This is the main loop.  For each code we get we pass through the
    * linked list of prefix codes, pushing the corresponding "character" for
    * each code onto the stack.  When the list reaches a single "character"
    * we push that on the stack too, and then start unstacking each
    * character for output in the correct order.  Special handling is
    * included for the clear code, and the whole thing ends when we get
    * an ending code.
    */
   while ((c = get_next_code()) != ending)
      {

      /* If we had a file error, return without completing the decode
       */
      if (c < 0)
         {
         free(buf);
         return(0);
         }

      /* If the code is a clear code, reinitialize all necessary items.
       */
      if (c == clear)
         {
         curr_size = size + 1;
         slot = newcodes;
         top_slot = 1 << curr_size;

         /* Continue reading codes until we get a non-clear code
          * (Another unlikely, but possible case...)
          */
         while ((c = get_next_code()) == clear)
            ;

         /* If we get an ending code immediately after a clear code
          * (Yet another unlikely case), then break out of the loop.
          */
         if (c == ending)
            break;

         /* Finally, if the code is beyond the range of already set codes,
          * (This one had better NOT happen...  I have no idea what will
          * result from this, but I doubt it will look good...) then set it
          * to color zero.
          */
         if (c >= slot)
            c = 0;

         oc = fc = c;

         /* And let us not forget to put the char into the buffer... And
          * if, on the off chance, we were exactly one pixel from the end
          * of the line, we have to send the buffer to the out_line()
          * routine...
          */
         *bufptr++ = c;
         if (--bufcnt == 0)
            {
            if ((ret = out_line(buf, linewidth)) < 0)
               {
               free(buf);
               return(ret);
               }
            bufptr = buf;
            bufcnt = linewidth;
            }
         }
      else
         {

         /* In this case, it's not a clear code or an ending code, so
          * it must be a code code...  So we can now decode the code into
          * a stack of character codes. (Clear as mud, right?)
          */
         code = c;

         /* Here we go again with one of those off chances...  If, on the
          * off chance, the code we got is beyond the range of those already
          * set up (Another thing which had better NOT happen...) we trick
          * the decoder into thinking it actually got the last code read.
          * (Hmmn... I'm not sure why this works...  But it does...)
          */
         if (code >= slot)
            {
            if (code > slot)
               ++bad_code_count;
            code = oc;
            *sp++ = fc;
            }

         /* Here we scan back along the linked list of prefixes, pushing
          * helpless characters (ie. suffixes) onto the stack as we do so.
          */
         while (code >= newcodes)
            {
            *sp++ = suffix[code];
            code = prefix[code];
            }

         /* Push the last character on the stack, and set up the new
          * prefix and suffix, and if the required slot number is greater
          * than that allowed by the current bit size, increase the bit
          * size.  (NOTE - If we are all full, we *don't* save the new
          * suffix and prefix...  I'm not certain if this is correct...
          * it might be more proper to overwrite the last code...
          */
         *sp++ = code;
         if (slot < top_slot)
            {
            suffix[slot] = fc = code;
            prefix[slot++] = oc;
            oc = c;
            }
         if (slot >= top_slot)
            if (curr_size < 12)
               {
               top_slot <<= 1;
               ++curr_size;
               }

         /* Now that we've pushed the decoded string (in reverse order)
          * onto the stack, lets pop it off and put it into our decode
          * buffer...  And when the decode buffer is full, write another
          * line...
          */
         while (sp > stack)
            {
            *bufptr++ = *(--sp);
            if (--bufcnt == 0)
               {
               if ((ret = out_line(buf, linewidth)) < 0)
                  {
                  free(buf);
                  return(ret);
                  }
               bufptr = buf;
               bufcnt = linewidth;
               }
            }
         }
      }
   ret = 0;
   if (bufcnt != linewidth)
      ret = out_line(buf, (linewidth - bufcnt));
   free(buf);
   return(ret);
   }

// this function is called just before program exits
void cleanup(void){
  // free any allocated resources
  if(fp!=NULL)
    fclose(fp);
  if(buffer!=NULL)
    free(buffer);
  if(grgb!=NULL)
    free(grgb);
  if(lrgb!=NULL)
    free(lrgb);
  if(pixels!=NULL)
    free(pixels);

Quote:
}

void main(int argc, char **argv){
  unsigned int gctsize=0, lctsize=0, mode;
  int i;

  // set bad codes encountered to zero
  bad_code_count=0;

  // set function to be called on exit
  if(atexit(cleanup))
    exit(0);

  // exit if no file given
  if (argc<2){
    printf("Usage: %s gif_file\n", argv[0]);
    exit(0);
  }

  // try to open file
  if((fp=fopen(argv[1], "rb"))==NULL){
    printf("Failed to open file.\n");
    exit(0);
  }

  // allocate buffers
  buffer=(unsigned char *)malloc(BSIZE);
  pixels=(unsigned char *)malloc(ISIZE);
  grgb  =(unsigned char *)malloc(PSIZE);
  lrgb  =(unsigned char *)malloc(PSIZE);
  if((grgb==NULL) || (lrgb==NULL) || (pixels==NULL) || (buffer==NULL)){
    printf("Not enough memory.\n");
    exit(0);
  }

  fread(buffer, 1, 6, fp);
  // test file for valid GIF signature
  if(memcmp(buffer, "GIF", 3)!=0){
    printf("Not a GIF file.\n");
    exit(0);
  }
  // test file for version number
  if((memcmp(buffer+3, "87a", 3)!=0) && (memcmp(buffer+3, "89a", 3)!=0)){
    printf("Unsuported GIF version. Hit a key to decode anyway.\n");
    getch();
  }

  // read logical screen descriptor
  fread(buffer, 1, 7, fp);

  // test for global color table presence
  if(*(buffer+4)&0x80){
    // compute global color table size
    gctsize=1<<((*(buffer+4)&0x07) + 1);
    // read global color table into buffer
    fread(grgb, 1, gctsize*3, fp);
    // adjust colors to crappy 6-bit PC-DAC values
    for(i=0; i<gctsize*3; i++)
      *(grgb+i)>>=2;
  }
  // get background color index
  background=*(buffer+5);

  // scan file for data blocks
  while((i=get_byte())>0) {
    // in end of GIF marker encountered then exit
    if(i==0x3b)
      exit(0);
    // test for extentions
    if(i==0x21){
      if( (i=get_byte()) < 0 )
        exit(0);
      // if graphic color extention present or
      // coment extention present or
      // plain text extention present or
      // application extention present then skip it
      if( (i==0x01) || (i==0xf9) || (i==0xfe) || (i==0xff)){
        while((i=get_byte())!=0){
          if(fread(buffer, 1, i, fp)!=i)
            exit(0);
        }
      }
    }

    // test for presence of image descriptor
    if(i==0x2c){
      // get image descriptor
      fread(buffer, 1, 9, fp);
      // interlaced flag is set or cleared accordingly
      if(*(buffer+8)&0x40)
        group=1;
      realx=xsize=*(buffer+4) | (*(buffer+5)<<8);
      realy=ysize=*(buffer+6) | (*(buffer+7)<<8);
      // test for presence of local color table
      if(*(buffer+8)&0x80){
        // compute local color table size
        lctsize=1<<((*(buffer+8)&0x07) + 1);
        // read local color table into buffer
        fread(lrgb, 1, lctsize*3, fp);
        // adjust colors to crappy 6-bit PC-DAC values
        for(i=0; i<gctsize*3; i++)
          *(lrgb+i)>>=2;
      }

      // choose a video mode that will just fit the image
      if((xsize<=640) && (ysize<=480)){
        mode=0x101;             //  VESA 640 x  480 x 256
        xsize=640;
        ysize=480;
      } else if ((xsize<=800) && (ysize<=600)){
        mode=0x103;             //  VESA 800 x  600 x 256
        xsize=800;
        ysize=600;
      } else if ((xsize<=1024) && (ysize<=768)){
        mode=0x105;             // VESA 1024 x  768 x 256
        xsize=1024;
        ysize=768;
      } else {
        mode=0x107;             // VESA 1280 x 1024 x 256
        xsize=1280;
        ysize=1024;
      }

      piclen=(unsigned long)xsize*(unsigned long)ysize;
      // get video mode info through VESA call
      asm pusha
      asm mov ax, 0x4f01
      asm mov cx, mode
      asm les di, buffer
      asm int 0x10
      asm mov i, ax
      asm popa

      if(i!=0x004f){
        printf("VESA video functions not available.\n");
        exit(0);
      }
      // if mode not supported, or not color, or not graphics then exit
      if((*buffer&0x19)!=0x19){
        printf("Required graphics mode is not available.\n");
        exit(0);
      }

      // if window does not exist or is not writable exit
      if((*(buffer+2)&0x05)!=0x05) {
        printf("Cannot access video RAM.\n");
        exit(0);
      }

      // calculate window granularity
      granularity=(*(buffer+4) | (*(buffer+5)<<8))<<10;
      // calculate pointer to video RAM start
      vid=(unsigned char *)MK_FP((*(buffer+8) | (*(buffer+9)<<8)), 0);

      // set VESA video mode
      asm pusha
      asm mov ax, 0x4f02
      asm mov bx, mode
      asm int 0x10
      asm popa

      // set color table if present
      if(lctsize){
        // scope of local color table is local so once used it's gone
        lctsize=0;
        asm pusha
        asm mov ax, 0x1012
        asm mov bx, 0x0000
        asm mov cx, 0x0100
        asm les dx, lrgb
        asm int 0x10
        asm popa
      } else if(gctsize){
        // if no local color table then set global color table if present
        asm pusha
        asm mov ax, 0x1012
        asm mov bx, 0x0000
        asm mov cx, 0x0100
        asm les dx, grgb
        asm int 0x10
        asm pop es
        asm popa
      }

      // decode and display graphic
      decode(realx);
      // wait for key press
      getch();

      // set default text mode
      asm mov ax, 0x0003
      asm int 0x10
    }
  }

  // exit to dos
  exit(0);

Quote:
}

-------------- chop here -------------------

--
+-----------------------------+-----------------------------------------------+
| Alex Ivopol                 |  If you  love  something set it free.  If it  |

+-----------------------------+-----------------------------------------------+



Fri, 17 Jan 1997 15:29:37 GMT  
 
 [ 1 post ] 

 Relevant Pages 

1. source code of gif encoding and decoding?

2. Need GIF decoding algorithm Source

3. C Source for GIF Decode/Encode?

4. Need source code for reading and display gif/bmp files

5. Decoding GIF file in C?

6. Newbie: separate big .cs file into small .cs files

7. Compile CS source code using ICodeCompiler

8. cs source code

9. Source Code convert .BMP to .GIF ?

10. I want source code of GIF viewer program in C (any version)

11. loading GIF (source code requested)

12. Needed: GIF/TIFF/etc reader source code

 

 
Powered by phpBB® Forum Software