/* out_gif.cpp
* by
[email protected] at Sat Mar 23 11:02:36 CET 2002
*/
#include "image.hpp"
#if USE_OUT_GIF
#include <string.h>
#include <stdio.h>
/**** pts ****/
#ifdef __cplusplus
# define AALLOC(var,len,itemtype) var=new itemtype[len]
# define AFREE(expr) delete [] (expr)
#else
# include <stdlib.h> /* malloc(), free() */
# define AALLOC(var,len,itemtype) var=(itemtype*)malloc(len*sizeof(itemtype));
# define AFREE(expr) free((expr))
#endif
#define HasLZW HAVE_LZW /* Imp: or USE_BUILTIN_LZW? */
#define True true
#define False false
/* The code of GIFEncodeImage is based on an early version of ImageMagick. */
static unsigned int GIFEncodeImage(GenBuffer::Writable& out, char const*ppbeg, register char const*ppend, const unsigned int data_size)
{
#define MaxCode(number_bits) ((1 << (number_bits))-1)
#define MaxHashTable 5003
#define MaxGIFBits 12
#if defined(HasLZW)
#define MaxGIFTable (1 << MaxGIFBits)
#else
#define MaxGIFTable max_code
#endif
#define GIFOutputCode(code) \
{ \
/* \
Emit a code. \
*/ \
if (bits > 0) \
datum|=((long) code << bits); \
else \
datum=(long) code; \
bits+=number_bits; \
while (bits >= 8) \
{ \
/* \
Add a character to current packet. \
*/ \
packet[byte_count++]=(unsigned char) (datum & 0xff); \
if (byte_count >= 254) \
{ \
packet[-1]=byte_count; \
out.vi_write((char*)packet-1, byte_count+1); \
byte_count=0; \
} \
datum>>=8; \
bits-=8; \
} \
if (free_code > max_code) \
{ \
number_bits++; \
if (number_bits == MaxGIFBits) \
max_code=MaxGIFTable; \
else \
max_code=MaxCode(number_bits); \
} \
}
int
bits,
byte_count,
i,
next_pixel,
number_bits;
long
datum;
register int
displacement,
k;
register char const*pp;
short
clear_code,
end_of_information_code,
free_code,
*hash_code,
*hash_prefix,
index,
max_code,
waiting_code;
unsigned char
*packet,
*hash_suffix;
/*
Allocate encoder tables.
*/
AALLOC(packet,257,unsigned char);
AALLOC(hash_code,MaxHashTable,short);
AALLOC(hash_prefix,MaxHashTable,short);
AALLOC(hash_suffix,MaxHashTable,unsigned char);
if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) ||
(hash_prefix == (short *) NULL) ||
(hash_suffix == (unsigned char *) NULL))
return(False);
packet++;
/* Now: packet-1 == place for byte_count */
/*
Initialize GIF encoder.
*/
number_bits=data_size;
max_code=MaxCode(number_bits);
clear_code=((short) 1 << (data_size-1));
end_of_information_code=clear_code+1;
free_code=clear_code+2;
byte_count=0;
datum=0;
bits=0;
for (i=0; i < MaxHashTable; i++)
hash_code[i]=0;
GIFOutputCode(clear_code);
/*
Encode pixels.
*/
/**** pts ****/
pp=ppbeg;
waiting_code=*(unsigned char const*)pp++; /* unsigned char BUGFIX at Sun Dec 8 13:17:00 CET 2002 */
while (pp!=ppend) {
/*
Probe hash table.
*/
index=*(unsigned char const*)pp++;
k=(int) ((int) index << (MaxGIFBits-8))+waiting_code;
if (k >= MaxHashTable)
k-=MaxHashTable;
#if defined(HasLZW)
if (hash_code[k] > 0)
{
if ((hash_prefix[k] == waiting_code) && (hash_suffix[k] == index))
{
waiting_code=hash_code[k];
continue;
}
if (k == 0)
displacement=1;
else
displacement=MaxHashTable-k;
next_pixel=False;
for ( ; ; )
{
k-=displacement;
if (k < 0)
k+=MaxHashTable;
if (hash_code[k] == 0)
break;
if ((hash_prefix[k] == waiting_code) && (hash_suffix[k] == index))
{
waiting_code=hash_code[k];
next_pixel=True;
break;
}
}
if (next_pixel != False) /* pacify VC6.0 */
continue;
}
#endif
GIFOutputCode(waiting_code);
// printf("wc=%u\n", waiting_code);
if (free_code < MaxGIFTable)
{
hash_code[k]=free_code++;
hash_prefix[k]=waiting_code;
hash_suffix[k]=index;
}
else
{
/*
Fill the hash table with empty entries.
*/
for (k=0; k < MaxHashTable; k++)
hash_code[k]=0;
/*
Reset compressor and issue a clear code.
*/
free_code=clear_code+2;
GIFOutputCode(clear_code);
number_bits=data_size;
max_code=MaxCode(number_bits);
}
waiting_code=index;
#if 0 /**** pts ****/
if (QuantumTick(i,image) && (image->previous == (Image2 *) NULL))
ProgressMonitor(SaveImageText,i,image->packets);
#endif
}
/*
Flush out the buffered code.
*/
GIFOutputCode(waiting_code);
GIFOutputCode(end_of_information_code);
if (bits > 0)
{
/*
Add a character to current packet.
*/
packet[byte_count++]=(unsigned char) (datum & 0xff);
if (byte_count >= 254)
{
packet[-1]=byte_count;
out.vi_write((char*)packet-1, byte_count+1);
byte_count=0;
}
}
/*
Flush accumulated data.
*/
if (byte_count > 0)
{
packet[-1]=byte_count;
out.vi_write((char*)packet-1, byte_count+1);
}
/*
Free encoder memory.
*/
AFREE(hash_suffix);
AFREE(hash_prefix);
AFREE(hash_code);
AFREE(packet-1);
return pp==ppend;
}
/** This isn't a complete GIF writer. For example, it doesn't support
* animation or multiple sub-images. But it supports transparency and
* compression. Only works when . The user should call
* packPal() first to ensure img->getBpc()==8, and to get a minimal palette.
*/
void out_gif_write(GenBuffer::Writable& out, Image::Indexed *img) {
/* Tested and proven to work at Sat Mar 23 13:11:41 CET 2002 */
unsigned i, c, bits_per_pixel;
signed transp;
char hd[19];
assert(img->getBpc()==8); /* 1 palette entry == 8 bits */
transp=img->getTransp();
memcpy(hd, transp!=-1 ? "GIF89a" : "GIF87a", 6);
i=img->getWd(); hd[6]=i; hd[7]=i>>8;
i=img->getHt(); hd[8]=i; hd[9]=i>>8;
// transp=-1; /* With this, transparency will be ignored */
c=img->getNcols();
bits_per_pixel=1; while (((c-1)>>bits_per_pixel)!=0) bits_per_pixel++;
/* ^^^ (c-1) BUGFIX at Mon Oct 20 15:18:24 CEST 2003 */
/* 63 -> 6, 64 -> 6, 65 -> 7 */
// if (bits_per_pixel>1) bits_per_pixel--; /* BUGFIX at Wed Apr 30 15:55:27 CEST 2003 */ /* BUGFIX at Mon Oct 20 15:18:14 CEST 2003 */
// fprintf(stderr, "GIF89 write transp=%d ncols=%d bpp=%d\n", transp, c, bits_per_pixel);
assert(1<=bits_per_pixel && bits_per_pixel<=8);
c=3*((1<<bits_per_pixel)-c);
/* Now: c is the number of padding bytes */
hd[10]= 0x80 /* have global colormap */
| ((8-1) << 4) /* color resolution: bpc==8 */
| (bits_per_pixel-1); /* size of global colormap */
hd[11]=0; /* background color: currently unused */
hd[12]=0; /* reversed */
out.vi_write(hd, 13);
// out.vi_write("\xFF\x00\x00" "\x00\xFF\x00" "\x00\x00\xFF", 9);
out.vi_write(img->getHeadp(), img->getRowbeg()-img->getHeadp()); /* write colormap */
if (c!=0) {
char *padding=new char[(unsigned char)c]; /* BUGFIX at Fri Oct 17 18:05:09 CEST 2003 */
memset(padding, '\0', (unsigned char)c); /* Not automatic! */
out.vi_write(padding, (unsigned char)c);
delete [] padding;
}
if (transp!=-1) {
/* Write Graphics Control extension. Only GIF89a */
hd[0]=0x21; hd[1]=(char)0xf9; hd[2]=0x04;
hd[3]=transp!=-1; /* dispose==0 */
hd[4]=hd[5]=0; /* delay==0 */
hd[6]=transp; /* transparent color index -- or 255 */
hd[7]=0;
out.vi_write(hd, 8);
}
/* Write image header */
hd[8]=',';
hd[ 9]=hd[10]=0; /* left */
hd[11]=hd[12]=0; /* top */
i=img->getWd(); hd[13]=i; hd[14]=i>>8;
i=img->getHt(); hd[15]=i; hd[16]=i>>8;
hd[17]=0; /* no interlace, no local colormap, no bits in local colormap */
if ((c=bits_per_pixel)<2) c=4;
hd[18]=c; /* compression bits_per_pixel */
out.vi_write(hd+8, 11);
#if 0
printf("GIFEncodeImage out r r+%u %u; off=%u\n", img->getRlen()*img->getHt(), c+1, img->getRowbeg()-img->getHeadp());
FILE *f=fopen("tjo.dat","wb");
fprintf(f, "P6 %u %u 255\n", img->getWd(), img->getHt());
// fwrite(img->getRowbeg(), 1, img->getRlen()*img->getHt(), f);
for (unsigned u=0; u<img->getRlen()*img->getHt(); u++) {
char *p=img->getHeadp()+3* *(unsigned char*)(img->getRowbeg()+u);
putc(p[0],f);
putc(p[1],f);
putc(p[2],f);
}
#endif
i=GIFEncodeImage(out, img->getRowbeg(), img->getRowbeg()+img->getRlen()*img->getHt(), c+1);
#if 0
{ char buf[500000];
FILE *f=fopen("tjo.dat","rb");
int got=fread(buf, 1, sizeof(buf), f);
assert(got==486109);
assert(got==img->getRlen()*img->getHt());
i=GIFEncodeImage(out, buf, buf+img->getRlen()*img->getHt(), c+1);
}
#endif
assert(i!=0);
/* Write trailer */
hd[0]=0; hd[1]=';';
out.vi_write(hd, 2);
}
#else
#include <stdlib.h>
void out_gif_write(GenBuffer::Writable&, Image::Indexed *) {
assert(0);
abort();
}
#endif