// FILE: gif.h

#ifndef _gif_
#define _gif_

//#define _gif_info_

typedef struct {
  char ID[6]                            __attribute__ ((packed));
  short Width                           __attribute__ ((packed));
  short Height                          __attribute__ ((packed));
  char Info                             __attribute__ ((packed));
  char BackGroundColor                  __attribute__ ((packed));
  char Res                              __attribute__ ((packed));
} GIF_Screen_Descriptor;

typedef struct {
  char Red                              __attribute__ ((packed));
  char Green                            __attribute__ ((packed));
  char Blue                             __attribute__ ((packed));
} GIF_Color;

typedef struct {
  short LeftOffset                      __attribute__ ((packed));
  short TopOffset                       __attribute__ ((packed));
  short Width                           __attribute__ ((packed));
  short Height                          __attribute__ ((packed));
  char Info                             __attribute__ ((packed));
} GIF_Image_Descriptor;

typedef struct {
  short prev                            __attribute__ ((packed));
  char data                             __attribute__ ((packed));
} GIF_HT_Item;

extern int GIF_error;

extern char *Load_GIF (char *name, int *l, int *h, char *pal,
                         int *color_cnt, char *bg_color);
extern int Save_GIF (char *name, char *pic, char *pal, int l, int h,
                 int color_cnt, char bg_color);

#endif

// FILE: gif.c

#include <stdlib.h>
#include <stdio.h>
#include <mem.h>
#include <string.h>
#include "gif.h"

GIF_HT_Item GIF_HashTable[4096];
int GIF_cClear, GIF_cEOI, GIF_cMax;
int GIF_cur_code_size;
int GIF_cMask[] = {0,1,3,7,0xF,0x1F,0x3F,0x7F,0xFF,0x1FF,0x3FF,0x7FF,0xFFF};
char *GIF_pic=NULL, *GIF_p;
int GIF_bytes_left;
char GIF_Pixel[4096];
char GIF_first_char;
int GIF_IsInterlaced, GIF_LineWidth, GIF_PixelsLeft, GIF_LineCnt, GIF_LineNo, GIF_IIndex;
int GIF_VOffs[] = {0,4,2,1,-1};
int GIF_VStep[] = {8,8,4,2,-1};
int GIF_error=0;

FILE *GIF_f;//, *tst;
unsigned char GIF_bstream[255];
int GIF_bstream_size;
int GIF_bstream_flag;
int GIF_bstream_index;
int GIF_bstream_shift;
int GIF_remainder;

void GIF_Init_HashTable() {
  int i;

  for (i=0;i<GIF_cClear;i++) {
    GIF_HashTable[i].prev = -1;
    GIF_HashTable[i].data = i;
  };
  GIF_HashTable[GIF_cClear].prev =
  GIF_HashTable[GIF_cEOI].prev = -2;
}

int GIF_Get_Char() {
  int res;
  if (GIF_bstream_flag==0) {
    if (fread(&GIF_bstream_size,1,1,GIF_f)!=1) return -1;
    if (GIF_bstream_size==0) return -1;
    if (fread(GIF_bstream,1,GIF_bstream_size,GIF_f)!=GIF_bstream_size) return -1;
    res = GIF_bstream[0];
    GIF_bstream_index=GIF_bstream_flag=1;
  } else
    res = GIF_bstream[GIF_bstream_index++];
  if (GIF_bstream_index == GIF_bstream_size) GIF_bstream_flag=0;
  return res;
};

int GIF_Get_Code() {
  int res, cnt;

  if (GIF_bstream_shift==0)
    if ((GIF_remainder = GIF_Get_Char()) == -1) return -1;
  res = (GIF_remainder >> GIF_bstream_shift);
  cnt = 8 - GIF_bstream_shift;
  GIF_bstream_shift += GIF_cur_code_size; GIF_bstream_shift &= 7;

  while (cnt < GIF_cur_code_size) {
    if ((GIF_remainder = GIF_Get_Char()) == -1) return -1;
    res |= (GIF_remainder << cnt);
    cnt += 8;
  };

//  fprintf (tst, "%X\n", res & GIF_cMask[GIF_cur_code_size]);
  return res & GIF_cMask[GIF_cur_code_size];
}

void GIF_Output_Pixels (int code) {
  int cnt=0, cnt2;

  while (code > GIF_cEOI) {
    GIF_Pixel[cnt] = GIF_HashTable[code].data;
    code = GIF_HashTable[code].prev;
    cnt++;
  };
  GIF_first_char = GIF_Pixel[cnt] = GIF_HashTable[code].data;
  cnt++;
  if (cnt > GIF_bytes_left) cnt = GIF_bytes_left;
  GIF_bytes_left -= cnt;
  if (!GIF_IsInterlaced)
    while (cnt) *GIF_p++ = GIF_Pixel[--cnt];
  else {
l:
    if (cnt < GIF_PixelsLeft) {
      GIF_PixelsLeft -= cnt;
      while (cnt) *GIF_p++ = GIF_Pixel[--cnt];
    } else {
      cnt2 = cnt;
      cnt -= GIF_PixelsLeft;
      while (GIF_PixelsLeft--) *GIF_p++ = GIF_Pixel[--cnt2];
      GIF_LineNo += GIF_VStep[GIF_IIndex];
      while (GIF_LineNo >= GIF_LineCnt) {
        GIF_IIndex++;
        GIF_LineNo = GIF_VOffs[GIF_IIndex];
      };
      GIF_p = GIF_pic + GIF_LineNo*GIF_LineWidth;
      GIF_PixelsLeft = GIF_LineWidth;
      if (cnt) goto l;
    };
  };
}

char *Load_GIF (char *name, int *l, int *h, char *pal, int *color_cnt,
                 char *bg_color) {
  GIF_Screen_Descriptor sd;
  GIF_Image_Descriptor id;
  int BitsPerPixel, MaxImgColors, MaxColors, Global, Interlaced;
  char ID[7];
  char code_size;
  int code, old_code;
  
  GIF_error=1;
  if (!(GIF_f = fopen(name, "rb"))) {
lerr:
//    fclose (tst);
    if (GIF_pic!=NULL) free (GIF_pic);
    fclose (GIF_f);
    *l=*h=*color_cnt=*bg_color=0;
//    printf ("bytes loaded: %d\n", GIF_p-GIF_pic);
    return NULL;
  };

/* Loading and displaying GIF screen descriptor */

  GIF_error=2;
  if (fread(&sd,1,sizeof(sd),GIF_f)!=sizeof(sd)) goto lerr;
  memset (&ID, 0, sizeof(ID));
  memcpy (&ID, &sd.ID, 6);
  GIF_error=3;
  if ((strcmp(ID,"GIF87a")!=0) && (strcmp(ID,"GIF89a"))!=0) goto lerr;
  BitsPerPixel = 1+(sd.Info&7);
  MaxImgColors = 1<<(((sd.Info>>4)&7)+1);
  MaxColors = 1<<BitsPerPixel;
  Global = sd.Info>>7;
  *bg_color = sd.BackGroundColor;

#ifdef _gif_info_
  printf (" -= General information =-\n");
  printf ("GIF ID:              %s\n", ID);
  printf ("Screen dimensions:   %dx%d\n", sd.Width, sd.Height);
  if (Global) printf ("Global color map:    present\n");
  else printf ("Global color map:    not present\n");
  printf ("Bits per pixel:      %d\n", BitsPerPixel);
  printf ("Max colors:          %d\n", MaxColors);
  printf ("Max colors in image: %d\n", MaxImgColors);
#endif

/* Loading GIF global color map */

  GIF_error=4;
  if (Global)
    if (fread(pal,sizeof(GIF_Color),MaxColors,GIF_f)!=
        MaxColors) goto lerr;

/* Loading and displaying GIF image descriptor */

  do {
    GIF_error=5;
    if (fread(&ID[0],1,1,GIF_f)!=1) goto lerr;
    if (ID[0] == 0x2C) break;                   // Image descriptor ID
    GIF_error=6;
    if (ID[0] != 0x21) goto lerr;               // Extension Block ID
    GIF_error=7;
    if (fread(&ID[1],1,1,GIF_f)!=1) goto lerr;  // Function code
lskip:
    GIF_error=8;
    if (fread(&ID[1],1,1,GIF_f)!=1) goto lerr;  // Function data count
    if (ID[1] == 0) continue;
    GIF_error=9;
    if (fseek (GIF_f, ID[1], SEEK_CUR)) goto lerr;
    goto lskip;
  } while (1);
  GIF_error=10;
  if (fread(&id,1,sizeof(id),GIF_f)!=sizeof(id)) goto lerr;

  GIF_error=11;
  if (!(id.Width*id.Height)) goto lerr;
  *l = id.Width; *h = id.Height;
  Global = !(id.Info>>7);
  if (!Global) BitsPerPixel = 1+(id.Info&7);
  *color_cnt = MaxColors = 1<<BitsPerPixel;
  Interlaced = (id.Info>>6)&1;

#ifdef _gif_info_
  printf (" -= Image information =-\n");
  printf ("Image dimensions:    %dx%d\n", id.Width, id.Height);
  if (Global) printf ("Color map used:      global\n");
  else printf ("Color map used:      local\n");
  if (Interlaced) printf ("Form of storage:     interlaced\n");
  else printf("Form of storage:     sequential\n");
  printf ("Bits per pixel:      %d\n", BitsPerPixel);
  printf ("Max colors:          %d\n", MaxColors);
#endif

/* Loading GIF local color map */

  GIF_error=12;
  if (!Global)
    if (fread(pal,sizeof(GIF_Color),MaxColors,GIF_f)!=
        MaxColors) goto lerr;

/* Loading raster data stream */

  GIF_error=13;
  if (fread(&code_size,1,1,GIF_f)!=1) goto lerr;
  GIF_error=14;
  if ((code_size < 2) || (code_size > 11)) goto lerr;

#ifdef _gif_info_
  printf (" -= Raster information =-\n");
  printf ("code size (bits):    %d\n", code_size);
#endif

  GIF_bytes_left=(*l)*(*h);
  GIF_error=15;
  if ((GIF_pic = malloc(GIF_bytes_left))==NULL) goto lerr;

/* Initialazing variables */

  GIF_p = GIF_pic;
  GIF_LineWidth = *l;
  GIF_LineCnt = *h;
  GIF_PixelsLeft = GIF_LineWidth;
  GIF_IIndex = GIF_LineNo = 0;
  GIF_IsInterlaced = Interlaced;
  GIF_cClear = 1<<code_size;
  GIF_cEOI = GIF_cClear+1;
  GIF_cMax = GIF_cEOI+1;
  GIF_cur_code_size = code_size+1;
  GIF_Init_HashTable();
  GIF_bstream_shift=0;                   // no shifts needed
  GIF_bstream_flag=0;                    // no bytes have yet been read

//  tst = fopen ("codes_in.txt", "wt");

  GIF_error=16;
  if (GIF_Get_Code() != GIF_cClear) goto lerr;
lclear:
  code = GIF_Get_Code();
  GIF_error=17;
  if (code<0) goto lerr;
  GIF_Output_Pixels (code);
  if (GIF_bytes_left == 0) goto lend;
  while (1) {
    old_code = code;
    code = GIF_Get_Code();
    GIF_error=18;
    if (code<0) goto lerr;
    if (code == GIF_cClear) {
      GIF_cMax = GIF_cEOI+1;
      GIF_cur_code_size = code_size+1;
      goto lclear;
    };
    if (code == GIF_cEOI) break;
    if (code < GIF_cMax) {
      GIF_Output_Pixels (code);
      if (GIF_bytes_left == 0) goto lend;
      GIF_HashTable[GIF_cMax].prev = old_code;
      GIF_HashTable[GIF_cMax].data = GIF_first_char;
      if ((++GIF_cMax == 1 << GIF_cur_code_size) && (GIF_cur_code_size<12))
        GIF_cur_code_size++;
    } else {
      GIF_HashTable[GIF_cMax].prev = old_code;
      GIF_HashTable[GIF_cMax].data = GIF_first_char;
      if ((++GIF_cMax == 1 << GIF_cur_code_size) && (GIF_cur_code_size<12))
        GIF_cur_code_size++;
      GIF_Output_Pixels (code);
      if (GIF_bytes_left == 0) goto lend;
    };
  };

//  fclose (tst);
lend:
  fclose (GIF_f);
  GIF_error = 0;
  return GIF_pic;
};

int GIF_Put_Char (unsigned char c) {
  GIF_bstream[GIF_bstream_size++] = c;
  if (GIF_bstream_size == 255) {
    if (fwrite(&GIF_bstream_size,1,1,GIF_f)!=1) return -1;
    if (fwrite(&GIF_bstream,1,GIF_bstream_size,GIF_f)!=GIF_bstream_size) return -1;
    GIF_bstream_size = 0;
  };
  return 0;
};

int GIF_Flush_Stream() {
  if (GIF_bstream_shift) {
    if (GIF_Put_Char((unsigned char)GIF_remainder)<0) return -1;
    GIF_bstream_shift = 0;
  };
  if (GIF_bstream_size) {
    if (fwrite(&GIF_bstream_size,1,1,GIF_f)!=1) return -1;
    if (fwrite(&GIF_bstream,1,GIF_bstream_size,GIF_f)!=GIF_bstream_size) return -1;
    GIF_bstream_size = 0;
  };
  return 0;
};

int GIF_Put_Code (int code) {
  int cnt;

//  fprintf (tst, "%X\n", code);
  if (!GIF_bstream_shift) GIF_remainder = 0;
  GIF_remainder |= code << GIF_bstream_shift;
  cnt = 8 - GIF_bstream_shift;
  if (cnt < GIF_cur_code_size) {
    code >>= cnt; 
    do {
      if (GIF_Put_Char ((unsigned char)GIF_remainder) < 0) return -1;
      GIF_remainder = code;
      code >>= 8;
      cnt += 8;
    } while (cnt < GIF_cur_code_size);
  };
  GIF_bstream_shift += GIF_cur_code_size; GIF_bstream_shift &= 7;
  if (!GIF_bstream_shift)
    if (GIF_Put_Char ((unsigned char)GIF_remainder) < 0) return -1;

  return 0;
}

int Save_GIF (char *name, char *pic, char *pal, int l, int h,
                 int color_cnt, char bg_color) {
  GIF_Screen_Descriptor sd;
  GIF_Image_Descriptor id;
  int BitsPerPixel, MaxColors;
  char ID[7]="GIF87a";
  char code_size, idID=0x2C;
  GIF_HT_Item X;
  int size, i, exists, search, ht_look=0;

  GIF_error=1;
  if ((l <= 0) || (h <= 0)) goto lerr;
  size = l*h;
  GIF_error=2;
  if ((color_cnt < 2) || (color_cnt > 256)) goto lerr;
  BitsPerPixel = 0;
  while (1<<BitsPerPixel < color_cnt) BitsPerPixel++;
  MaxColors = 1<<BitsPerPixel;
  
  GIF_error=3;
  if (!(GIF_f = fopen(name, "wb"))) {
lerr:
//    fclose (tst);
    fclose (GIF_f);
    return 0;
  };

/* Saving GIF screen descriptor */

  memcpy (sd.ID, ID, 6);
  sd.Width = l;
  sd.Height = h;
  sd.BackGroundColor = bg_color;
  sd.Res = 0;
  sd.Info = 0x80 |                      // Global color map is present
            ((BitsPerPixel-1)<<4) |     // Color resolution
            (BitsPerPixel-1);           // Bits per pixel
  GIF_error=4;
  if (fwrite(&sd,1,sizeof(sd),GIF_f)!=sizeof(sd)) goto lerr;

/* Saving GIF global color map */

  GIF_error=5;
  if (fwrite(pal,sizeof(GIF_Color),MaxColors,GIF_f)!=MaxColors) goto lerr;

/* Saving GIF image descriptor */

  id.LeftOffset = id.TopOffset = 0;
  id.Width = l;
  id.Height = h;
  id.Info = 0;                  // Global color map & sequential order
  GIF_error=6;
  if (fwrite(&idID,1,1,GIF_f)!=1) goto lerr;            // Img descr ID
  GIF_error=7;
  if (fwrite(&id,1,sizeof(id),GIF_f)!=sizeof(id)) goto lerr;// Img descriptor

/* Saving GIF raster data stream */

  if ((code_size = BitsPerPixel) == 1) code_size++;     // it's neded :(
  GIF_error=8;
  if (fwrite(&code_size,1,1,GIF_f)!=1) goto lerr;

/* Setting up needed variables */

  GIF_cClear = 1<<code_size;
  GIF_cEOI = GIF_cClear+1;
  GIF_cMax = GIF_cEOI+1;
  GIF_cur_code_size = code_size+1;
  GIF_Init_HashTable();
  GIF_bstream_shift=0;                   // no shifts needed
  GIF_bstream_size=0;                    // buffer is empty

//  tst = fopen ("codes_ou.txt", "wt");

/* Let's start encoding */

  GIF_error=9;
  if (GIF_Put_Code(GIF_cClear) < 0) goto lerr;// First code is always cClear

  search = X.prev = -1;

  do {
    X.data = *pic++; size--;
    if (X.prev == -1) {
      i = X.data;
      exists = 1;
    } else {
      exists = 0;
      for (i=1+search;i<GIF_cMax;i++) {
        ht_look++;
        if ((GIF_HashTable[i].prev==X.prev) && (GIF_HashTable[i].data==X.data)) {
          exists = 1;
          break;
        };
      };
    };
    if (exists) {
        search = X.prev = i;
    } else {
      GIF_error=10;
      if (GIF_Put_Code(X.prev) < 0) goto lerr;
      if ((GIF_cMax == 1 << GIF_cur_code_size) && (GIF_cur_code_size<12))
        GIF_cur_code_size++;
      if (GIF_cMax == 4096) {
        GIF_error=11;
        if (GIF_Put_Code(GIF_cClear) < 0) goto lerr;
        GIF_cMax = GIF_cEOI+1;
        GIF_cur_code_size = code_size+1;
        search=-1;
      } else {
        GIF_HashTable[GIF_cMax++] = X;
        search=GIF_cEOI;
      };
      X.prev = X.data;
    };
  } while (size);

  GIF_error=12;
  if (GIF_Put_Code(X.prev) < 0) goto lerr;
  GIF_error=13;
  if (GIF_Put_Code(GIF_cEOI) < 0) goto lerr;

/* Wow! We're near the end */

  GIF_error=14;
  if (GIF_Flush_Stream() < 0) goto lerr;        // Flush out the buffer
  idID = 0;
  GIF_error=15;
  if (fwrite(&idID,1,1,GIF_f)!=1) goto lerr;    // Raster data end ID

/* Saving GIF terminator */

  idID = 0x3B;
  GIF_error=16;
  if (fwrite(&idID,1,1,GIF_f)!=1) goto lerr;    // GIF end ID
  
//  fclose (tst);
  fclose (GIF_f);
  printf ("ht_look/size= %d\n", ht_look/(l*h-1));
  GIF_error = 0;
  return 1;
}
Hosted by uCoz