Source file: /~heha/vt/viewers/vtx.zip/gifsave.c

/* This source code relies on gcc's nested function capability.
 * heha, 141208
 */

#include "gifsave.h"
#include <stdlib.h>

bool WriteGIF89(FILE *f, word width, word height, byte depth,
 const tRGBTriple *rgbtab, short transcolor, char *comment,
 byte (*inputbyte)(short x, short y)){

/*-------------------------------------------------------------------------
 *  NAME:           Write()
 *  DESCRIPTION:    Output bytes to the current f.
 *  PARAMETERS:     buf - Pointer to buffer to write
 *                  len - Number of bytes to write
 *  RETURNS:        GIF_OK       - OK
 *                  GIF_ERRWRITE - Error writing to the file
 */
 bool Write(const void *buf, word len){
  return fwrite(buf, len, 1, f);	/* liefert 1 wenn erfolgreich */
 }

/*-------------------------------------------------------------------------
 *  NAME:           WriteByte()
 *  DESCRIPTION:    Output one byte to the current f.
 *  PARAMETERS:     b - Byte to write
 *  RETURNS:        GIF_OK       - OK
 *                  GIF_ERRWRITE - Error writing to the file
 */
 bool WriteByte(byte b){
  return (putc(b, f)!=EOF);
 }

#define wordswap(x) byteswap(x)

/*========================================================================*
 =                      "Object" to write a bit-file                      =
 *========================================================================*/
 byte PascalStr[256];	/* Ein ganz normaler Pascal-String */
 byte BitsLeft;		/* Freie Bits im durch PascalLen */
#define PascalLen PascalStr[0]	/* adressierten Byte */
 bool SaveLength;	/* entscheidet, ob bei Bitfile_Write das */
			/* Längenbyte mit abgespeichert wird */

 void BitFile_Init(bool savelength){
  PascalLen=BitsLeft=0;
  SaveLength=savelength;	/* Parameter für fortan "verschlucken" */
 }
/*-------------------------------------------------------------------------
 *  NAME:           BitFile_Done()
 *  DESCRIPTION:    Tidy up after using a bitfile
 */
 void BitFile_Done(){
  if (PascalLen){			/* Länge größer Null? */
   if (SaveLength){
    Write(PascalStr,(word)PascalLen+1);	/* Ja - ganz schreiben */
   }else{
    Write(&PascalStr[1],PascalLen);	/* bis aufs Längenbyte schreiben */
   }
  }
  PascalLen=BitsLeft=0;
/*  BitFile_Init();*/
fflush(f);
 }
/*-------------------------------------------------------------------------
 *  NAME:           BitFile_Write()
 *  DESCRIPTION:    Put the given number of bits to the f.
 *  PARAMETERS:     bits    - bits to write from (right justified)
 *                  numbits - number of bits to write
 */

 void BitFile_Write(word bits, byte numbits){
  bits&=(1<<numbits)-1;	/* Eventuell überflüssige Einsen killen */
/* printf("BitFile.Write(%hXh,%hd)\n",bits,numbits); */
  while (numbits>0){	/* Nichts tun bei numbits=0! (Falsche Schleife im Original) */
   if (!BitsLeft){	/* Kein Bit im durch PascalLen adressierten Byte frei? */
    if (PascalLen==255){	/* Puffer randvoll? */
     BitFile_Done();	/* Puffer schreiben (dabei PascalLen auf Null setzen) */
    }
    BitsLeft=8;		/* Dieses Byte hat nun 8 freie Bits */
    PascalStr[++PascalLen]=0;	/* nächstes Byte ansteuern und dieses nullsetzen */
   }
   PascalStr[PascalLen]|=(byte)(bits << (8-BitsLeft));	/* einODERn */
   if (BitsLeft>=numbits){	/* Alle übrigen Bits passen herein? */
    BitsLeft-=numbits;	/* Ja, BitsLeft auf Rest setzen */
    numbits=0;		/* Abbruchbedingung schaffen */
   }else{		/* Bits paßten nicht alle */
    numbits-=BitsLeft;	/* Nun übriggebliebene Bits */
    bits>>=BitsLeft;	/* bereits erledigte Bits rechts herausschieben */
    BitsLeft=0;		/* Kein Bit mehr frei im Byte */
   }
  }			/* Auf zur neuen WHILE-Runde! */
 }			/* So kurz und knapp kann man Programme schreiben */


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

#define HASHSIZE 9973
#define HASHSTEP 2039

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

static byte *StrChr = NULL;
static word *StrNxt = NULL,
	    *StrHsh = NULL,
	    NumStrings;

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

/*-------------------------------------------------------------------------
 *  NAME:           FreeStrtab()
 *  DESCRIPTION:    Free arrays used in string table routines
 *  PARAMETERS:     None
 *  RETURNS:        Nothing
 */
 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
 */
 bool AllocStrtab(){
  StrChr=(byte *)malloc(4096);
  if (!StrChr) return false;
  StrNxt=(word *)malloc(4096*sizeof(word));
  if (!StrNxt) return false;
  StrHsh=(word *)malloc(HASHSIZE*sizeof(word));
  if (!StrHsh) return false;
  return true;
 }
/*-------------------------------------------------------------------------
 *  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
 */
 word AddCharString(word index, byte b){
  word 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
 */
word FindCharString(word index, byte b){
  word 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 (word) 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
 */
 void ClearStrtab(byte codesize){
  word i;
  word *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.
 *                  inputbyte - Function that fetches each byte to compress.
 *                              Must return -1 when no more bytes. (EOF)
 *  RETURNS:        GIF_OK     - OK
 *                  GIF_OUTMEM - Out of memory
 */
 bool LZWCompress(byte codesize){
  byte c, numbits;
  word index;
  word clearcode, endofinfo, limit;
  word prefix = 0xFFFF;
  short x,y;

  BitFile_Init(true);		/* Bit-Ausgabe "mit Länge" initialisieren */
  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 false;
  ClearStrtab(codesize);		/* Tabellen initialisieren */
  WriteByte(codesize);		/* Kennung z.B. mit "4" */
  BitFile_Write(clearcode, numbits);	/* zuerst ein Clear-Code schicken */
/* Packe los! */
  for (y=0; y<height; y++){
   for (x=0; x<width; x++){
    c=inputbyte(x,y);		/* Byte über Callback holen */
    index=FindCharString(prefix, c);
/* printf("index %hXh=FindCharString(prefix %hXh,byte %hXh)\n",index,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. */
     BitFile_Write(prefix, numbits);
/* Add the new string (the prefix + the new character)
 * to the stringtable. */
     if (AddCharString(prefix, c) > limit){
      if (++numbits>12){		/* Drohender Überlauf? */
       BitFile_Write(clearcode, 12);	/* 12 bit breiten Clearcode schicken */
       ClearStrtab(codesize);
/* printf("Clearcode ausgegeben.\n"); */
       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 x */
  } /* for y */
/* End of info is reached. Write last prefix. */
  if (prefix != 0xFFFF)
   BitFile_Write(prefix, numbits);
  BitFile_Write(endofinfo, numbits);	/* End-Of-Info schreiben */
  BitFile_Done();		/* Bits vom String zum Stream schreiben */
  FreeStrtab();			/* aufräumen */
  return WriteByte(0);		/* Terminator schreiben */
 }

 bool WriteGIFStart(){		/* mit Farbtabelle */

/* Kennungs-String schreiben */
  if (!Write("GIF89a", 6)) return false;
/* Dieses tolle Bitfile-Objekt sorgt "ganz nebenbei" für die
   Bytedrehung für Motorola-Systeme */
/* Screendeskriptor füllen und schreiben */
  BitFile_Init(false);		/* Kein Längenbyte fallenlassen! */
  BitFile_Write(width,16);
  BitFile_Write(height,16);
  BitFile_Write(depth-1,3);	/* GlobalColorTableSize-1 */
  BitFile_Write(0,1);		/* SortFlag */
  BitFile_Write(8-1,3);		/* ColorResolution-1 */
  BitFile_Write(1,1);		/* GlobalColorTableFlag */
  BitFile_Write((transcolor==-1)? 0 : transcolor,8);	/* HFarbe */
  BitFile_Write(0,8);		/* PixelAspectRatio */
  BitFile_Done();		/* Schreibbefehl! */

/* RGB-Palette schreiben */
  if (rgbtab)			/* Never follow the NULL pointer */
   if (!Write(rgbtab,(1<<depth)*3)) return false;
  if (transcolor!=-1){
/* Grafiksteuererweiterung füllen und schreiben */
   WriteByte(0x21);		/* Introducer */
   WriteByte(0xF9);		/* GraphicControlLabel */

   BitFile_Init(true);		/* Längenbyte (=4) fallenlassen! */
   BitFile_Write(1,1);		/* TransparentFlag */
   BitFile_Write(0,1);		/* UserInputFlag */
   BitFile_Write(0,3);		/* DisposalMethod */
   BitFile_Write(0,3);		/* Reserved */
   BitFile_Write(0,16);		/* DelayTime */
   BitFile_Write(transcolor,8);	/* TransparentColorIndex */
   BitFile_Done();		/* Schreibbefehl! */
   WriteByte(0);		/* Terminator */

  }
/* Imagedeskriptor füllen und schreiben */
  BitFile_Init(false);		/* Kein Längenbyte fallenlassen! */
  BitFile_Write(0x2C,8);	/* Separator */
  BitFile_Write(0,16);		/* LeftPosition */
  BitFile_Write(0,16);		/* TopPosition */
  BitFile_Write(width,16);	/* Width */
  BitFile_Write(height,16);	/* Height */
  BitFile_Write(0,3);		/* LocalColorTableSize */
  BitFile_Write(0,2);		/* Reserved */
  BitFile_Write(0,1);		/* SortFlag */
  BitFile_Write(0,1);		/* InterlaceFlag */
  BitFile_Write(0,1);		/* LocalColorTableFlag */
  BitFile_Done();		/* Schreibbefehl! */

  return true;
 } /* Ende von WriteGIFStart() */

 bool WriteComment(char *comment){
  WriteByte(0x21);
  WriteByte(0xFE);	/* Comment Introducer */
  BitFile_Init(true);	/* Mißbrauch zum Erzeugen der Pascal-Strings */
  for (; *comment; comment++){
   BitFile_Write(*comment,8);	/* immer nur 8 bit */
  }
  BitFile_Done();	/* Letzten String schreiben */
  return WriteByte(0);	/* Block Terminator schreiben */
 }

#define GIFTerminate() WriteByte(';')

/* Hier das klitzekleine Hauptprogramm */
 if (!WriteGIFStart()) return false;
/* printf("GifStart\n"); */
fflush(f);
 if (!LZWCompress(depth)) return false;
/* printf("LZWCompress fertig\n"); */
 if (comment){
  if (!WriteComment(comment)) return false;
 }
 return GIFTerminate();
}
Detected encoding: UTF-80