/* out_gif.cpp * by pts@fazekas.hu at Sat Mar 23 11:02:36 CET 2002 */ #include "image.hpp" #if USE_OUT_GIF #include #include /**** pts ****/ #ifdef __cplusplus # define AALLOC(var,len,itemtype) var=new itemtype[len] # define AFREE(expr) delete [] (expr) #else # include /* 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<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; ugetRlen()*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 void out_gif_write(GenBuffer::Writable&, Image::Indexed *) { assert(0); abort(); } #endif