/* 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-8 | 0
|