// 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;
}
