Source file: /~heha/hs/ico2gif.zip/savegif.c

#include <stdio.h>
#include <stdlib.h>

#include "savegif.h"

#define FLUSH_WITHLEN -2
#define FLUSH_NOLEN -1


typedef struct {	//Für Data Sub-Blocks
 BYTE len, str[255];	//Data Sub-Blocks brauchen die Länge am Anfang
}PASCALSTR;


static PASCALSTR passtr;
static FILE *fid;

static int writechar(int c) {
 if (c>=0) passtr.str[passtr.len++]=(unsigned char)c;	//Anhängen
 if (passtr.len==255) c=-2;	//FLUSH erzwingen
 if (c<0) {			//FLUSH
 				//c==-2 --> mit längenangabe
				//c==-1 --> ohne Längenangabe
  if (fwrite((char*)&passtr+2+c, passtr.len-1-c, 1, fid)<1) return -1;
  passtr.len=0;
 }
 return 0;
}


static int writestr(char *str, int anz) {
 while (anz--) if (writechar(*(BYTE*)str++)) return -1;
 return 0;
}


//anz>0  bits schreiben
//anz==0 bits bis jetzt schreiben (flush)
//anz<0  zurücksetzen
static int writebits(int x, int anz){
 static BYTE c=0;
 static int stelle=0;		//merke stelle in c
 BYTE hc;

 if (!anz) {			//FLUSH
  if (!stelle) return 0;
  hc=0;
  while (stelle--) {		//hc füllen mit den interessanten Stellen
   hc<<=1;
   hc|=1;
  }
  c&=hc;			//uninteressante Stellen in c löschen
  if (writechar(c)) return -1;
  c=0;
  stelle=0;
 }else if (anz<0) {		//RESET
  stelle=0;
  c=0;
 }else{				//WRITE
  while (anz--) {
   c|=(x&1)<<stelle;		//Bit aus x holen und in c schreiben
   x>>=1;			//x immer runterschieben
   stelle++;
   if (stelle==8) {
    if (writechar(c)) return -1;
    stelle=0;
    c=0;
   }
  }
 }
 return 0;
}

#include <stdlib.h>

/*========================================================================*
 =                                                                        =
 =                Routines to maintain an LZW-string table                =
 =                                                                        =
 *========================================================================*/

#define HASHSIZE 9973
#define HASHSTEP 2039

#define HASH(index, lastbyte) (((lastbyte << 8) ^ index) % HASHSIZE)

static BYTE  *StrChr;
static unsigned short *StrNxt, *StrHsh, NumStrings;


/*========================================================================*
 =                                                                        =
 =                Routines to maintain an LZW-string table                =
 =                                                                        =
 *========================================================================*/ 

/*-------------------------------------------------------------------------
 *  NAME:           FreeStrtab()
 *  DESCRIPTION:    Free arrays used in string table routines
 *  PARAMETERS:     None
 *  RETURNS:        Nothing
 */
static void FreeStrtab(void){
 if (StrHsh) {free(StrHsh); StrHsh = NULL;}
 if (StrNxt) {free(StrNxt); StrNxt = NULL;}
 if (StrChr) {free(StrChr); StrChr = NULL;}
}


/*-------------------------------------------------------------------------
 *  NAME:           AllocStrtab()
 *  DESCRIPTION:    Allocate arrays used in string table routines
 *  PARAMETERS:     None
 *  RETURNS:        GIF_OK     - OK
 *                  GIF_OUTMEM - Out of memory
 */
static int AllocStrtab(void){
 StrChr=/*(unsigned char *)*/malloc(4096);
 StrNxt=/*(unsigned short *)*/malloc(4096*sizeof(unsigned short));
 StrHsh=/*(unsigned short *)*/malloc(HASHSIZE*sizeof(unsigned short));
 if (!StrChr) return 0;
 if (!StrNxt) return 0;
 if (!StrHsh) return 0;
 return 1;
}


/*-------------------------------------------------------------------------
 *  NAME:           AddCharString()
 *  DESCRIPTION:    Add a string consisting of the string of index plus
 *                  the byte b.
 *                  If a string of length 1 is wanted, the index should
 *                  be 0xFFFF.
 *  PARAMETERS:     index - Index to first part of string, or 0xFFFF is
 *                          only 1 byte is wanted
 *                  b     - Last byte in new string
 *  RETURNS:        Index to new string, or 0xFFFF if no more room
 */
static unsigned short AddCharString(unsigned short index, BYTE b){
 unsigned short hshidx;
/* Check if there is more room */
 if (NumStrings >= 4096) return 0xFFFF;
/* Search the string table until a free position is found */
 hshidx = HASH(index, b);
 while (StrHsh[hshidx] != 0xFFFF)
  hshidx = (hshidx + HASHSTEP) % HASHSIZE;
/* Insert new string */
 StrHsh[hshidx] = NumStrings;
 StrChr[NumStrings] = b;
 StrNxt[NumStrings] = index;

 return NumStrings++;
}


/*-------------------------------------------------------------------------
 *
 *  NAME:           FindCharString()
 *  DESCRIPTION:    Find index of string consisting of the string of index
 *                  plus the byte b.
 *                  If a string of length 1 is wanted, the index should
 *                  be 0xFFFF.
 *  PARAMETERS:     index - Index to first part of string, or 0xFFFF is
 *                          only 1 byte is wanted
 *                  b     - Last byte in string
 *  RETURNS:        Index to string, or 0xFFFF if not found
 */
static unsigned short FindCharString(unsigned short index, BYTE b){
 unsigned short hshidx, nxtidx;
/* Check if index is 0xFFFF. In that case we need only
 * return b, since all one-character strings has their
 * bytevalue as their index */
 if (index == 0xFFFF) return (unsigned short) b;
/* Search the string table until the string is found, or
 * we find 0xFFFF. In that case the string does not exist. */
 hshidx = HASH(index, b);
 while ((nxtidx = StrHsh[hshidx]) != 0xFFFF) {
  if ((StrNxt[nxtidx] == index) && (StrChr[nxtidx] == b)) return nxtidx;
  hshidx = (hshidx + HASHSTEP) % HASHSIZE;
 }
/* No match is found */
 return 0xFFFF;
}


/*-------------------------------------------------------------------------
 *  NAME:           ClearStrtab()
 *  DESCRIPTION:    Mark the entire table as free, enter the 2**codesize
 *                  one-byte strings, and reserve the 2 reserved
 *                  codes.
 *  PARAMETERS:     codesize - Number of bits to encode one pixel
 *  RETURNS:        Nothing
 */
static void ClearStrtab(BYTE codesize){
 unsigned short i;
 unsigned short *wp;
 NumStrings = 0;/* Stringtabelle ist nun leer */
 wp = StrHsh;
 for (i=0; i<HASHSIZE; i++)/* HASHtabelle leeren */
  *wp++ = 0xFFFF;
 for (i=0; i< ((1 << codesize) + 2); i++)
  AddCharString(0xFFFF, i);/* "Terminale" Strings und reservierte Codes seten */
}


/*========================================================================*
 =                                                                        =
 =                        LZW compression routine                         =
 =                                                                        =
 *========================================================================*/
/*-------------------------------------------------------------------------
 *  NAME:           LZW_Compress()
 *  DESCRIPTION:    Perform LZW compression as specified in the
 *                  GIF-standard.
 *  PARAMETERS:     codesize  - Number of bits needed to represent
 *                              one pixelvalue.
 *                  buf       - Pixel-Puffer (jedes Byte ein Pixel!)
 *  RETURNS:        GIF_OK     - OK
 *                  GIF_OUTMEM - Out of memory
 */
static int LZWCompress(const unsigned char *buf, int len, BYTE codesize){
 BYTE c, numbits;
 unsigned short index;
 unsigned short clearcode, endofinfo, limit;
 unsigned short prefix = 0xFFFF;
 int i;

 StrChr=NULL; StrNxt=NULL; StrHsh=NULL;

 if (codesize<2) codesize=2;	/* Muß mindestens 2 sein, auch bei s/w */
 clearcode = 1 << codesize;	/* Fixwerte berechnen */
 endofinfo = clearcode + 1;
 numbits = codesize + 1;	/* bei 16 Farben gehts mit 5 Bits los! */
 limit = (1 << numbits) - 1;	/* dann wäre das Limit=1F */
 if (!AllocStrtab()) return 0;
 ClearStrtab(codesize);		/* Tabellen initialisieren */
 writechar(codesize);		/* Kennung z.B. mit "4" */
 writechar(FLUSH_NOLEN);
 writebits(clearcode, numbits);	/* zuerst ein Clear-Code schicken */

 for (i=0; i<len; i++){		/* Packe los! */
  c=*buf++;
  index=FindCharString(prefix, c);

// Check if the prefix + the new character is a string that exists in the table
  if (index!=0xFFFF){
// The string exists in the table. Make this string the new prefix.
   prefix = index;
  }else{
// The string does not exist in the table. First write code of the old prefix to
// the file.
   writebits(prefix, numbits);
// Add the new string (the prefix + the new character) to the stringtable.
   if (AddCharString(prefix, c) > limit){
    if (++numbits>12){		/* Drohender Überlauf? */
     writebits(clearcode, 12);	/* 12 bit breiten Clearcode schicken */
     ClearStrtab(codesize);
     numbits = codesize + 1;
    }
    limit = (1 << numbits) - 1;/* Neues Limit (ggf. wieder 1F) */
   }
//Set prefix to a string containing only the character read. Since all possible
//one-character strings exists int the table, there's no need to check if it is
//found.
   prefix = c;
  }				/* else */
 }				/* for i */

// End of info is reached. Write last prefix.
 if (prefix != 0xFFFF) writebits(prefix, numbits);
 writebits(endofinfo, numbits);/* End-Of-Info schreiben */
 writebits(0,0);
 writechar(FLUSH_WITHLEN);
 FreeStrtab();			/* aufräumen */
 writechar(FLUSH_WITHLEN);	//Terminator rausschreiben
 return 0;
}


int savegif(FILE *f, const BYTE *b, int w, int h, const RGBCOLOR *c, int bitcount,
  int transcolor) {
 int nc;

 passtr.len=0;

 nc=1<<bitcount;

 fid=f;
 if (!fid) return -1;

 writestr("GIF89a", 6);		//GIF-Header


//Logical Screen Descriptor
 writebits(0, -1);
 writebits(w, 16);		//Breite
 writebits(h, 16);		//Höhe

 writebits(bitcount-1, 3);	//Size of Global Color Table  0 --> 2 Farben
 writebits(0, 1);		//Nicht sortiert (sowieso sinnlos)
 writebits(bitcount-1, 3);	//Color Resolution  0 --> 2 Farben
 writebits(1, 1);		//Global Color Table Flag

 writestr("\0", 2);		//Background Color Index, Pixel Aspect Ratio
				// (sinnlos)
 writechar(FLUSH_NOLEN);


//Global Color Table
// writestr((char *)colors, nc*sizeof(RGBCOLOR));
 if (fwrite(c, nc*sizeof(RGBCOLOR), 1, fid)<1) return -1;


 if (transcolor>=0) {		//Graphic Control Extension  (Transparente Farbe)
  writestr("\x21\xF9\x04\x01\x00",6);
  				//Introducer, Control Label, Block Size,
				//Packed Fields, Delay Time

  writebits(transcolor, 8);	//Transparent Color Index
  writestr("", 1);		//Block Terminator
 }


//Image Descriptor
 writestr("\x2c\0\0\0", 5);	//Trick 17c ...
 writebits(w, 16);		//Breite
 writebits(h, 16);		//Höhe
 writebits(bitcount-1, 8);	//Size of Global Color Table, Rest ist 0


//Image Data
 writechar(FLUSH_NOLEN);
 LZWCompress(b, w*h, bitcount);

 writechar(0x3B);
 writechar(FLUSH_NOLEN);

 return 0;
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded