1 /* out_gif.cpp
2  * by pts@fazekas.hu at Sat Mar 23 11:02:36 CET 2002
3  */
4 
5 #include "image.hpp"
6 
7 #if USE_OUT_GIF
8 #include <string.h>
9 #include <stdio.h>
10 
11 /**** pts ****/
12 #ifdef __cplusplus
13 #  define AALLOC(var,len,itemtype) var=new itemtype[len]
14 #  define AFREE(expr) delete [] (expr)
15 #else
16 #  include <stdlib.h> /* malloc(), free() */
17 #  define AALLOC(var,len,itemtype) var=(itemtype*)malloc(len*sizeof(itemtype));
18 #  define AFREE(expr) free((expr))
19 #endif
20 #define HasLZW HAVE_LZW /* Imp: or USE_BUILTIN_LZW? */
21 #define True true
22 #define False false
23 
24 /* The code of GIFEncodeImage is based on an early version of ImageMagick. */
GIFEncodeImage(GenBuffer::Writable & out,char const * ppbeg,register char const * ppend,const unsigned int data_size)25 static unsigned int GIFEncodeImage(GenBuffer::Writable& out, char const*ppbeg, register char const*ppend, const unsigned int data_size)
26 {
27 #define MaxCode(number_bits)  ((1 << (number_bits))-1)
28 #define MaxHashTable  5003
29 #define MaxGIFBits  12
30 #if defined(HasLZW)
31 #define MaxGIFTable  (1 << MaxGIFBits)
32 #else
33 #define MaxGIFTable  max_code
34 #endif
35 #define GIFOutputCode(code) \
36 { \
37   /*  \
38     Emit a code. \
39   */ \
40   if (bits > 0) \
41     datum|=((long) code << bits); \
42   else \
43     datum=(long) code; \
44   bits+=number_bits; \
45   while (bits >= 8) \
46   { \
47     /*  \
48       Add a character to current packet. \
49     */ \
50     packet[byte_count++]=(unsigned char) (datum & 0xff); \
51     if (byte_count >= 254) \
52       { \
53         packet[-1]=byte_count; \
54         out.vi_write((char*)packet-1, byte_count+1); \
55         byte_count=0; \
56       } \
57     datum>>=8; \
58     bits-=8; \
59   } \
60   if (free_code > max_code)  \
61     { \
62       number_bits++; \
63       if (number_bits == MaxGIFBits) \
64         max_code=MaxGIFTable; \
65       else \
66         max_code=MaxCode(number_bits); \
67     } \
68 }
69 
70   int
71     bits,
72     byte_count,
73     i,
74     next_pixel,
75     number_bits;
76 
77   long
78     datum;
79 
80   register int
81     displacement,
82     k;
83 
84   register char const*pp;
85 
86   short
87     clear_code,
88     end_of_information_code,
89     free_code,
90     *hash_code,
91     *hash_prefix,
92     index,
93     max_code,
94     waiting_code;
95 
96   unsigned char
97     *packet,
98     *hash_suffix;
99 
100   /*
101     Allocate encoder tables.
102   */
103   AALLOC(packet,257,unsigned char);
104   AALLOC(hash_code,MaxHashTable,short);
105   AALLOC(hash_prefix,MaxHashTable,short);
106   AALLOC(hash_suffix,MaxHashTable,unsigned char);
107   if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) ||
108       (hash_prefix == (short *) NULL) ||
109       (hash_suffix == (unsigned char *) NULL))
110     return(False);
111   packet++;
112   /* Now: packet-1 == place for byte_count */
113   /*
114     Initialize GIF encoder.
115   */
116   number_bits=data_size;
117   max_code=MaxCode(number_bits);
118   clear_code=((short) 1 << (data_size-1));
119   end_of_information_code=clear_code+1;
120   free_code=clear_code+2;
121   byte_count=0;
122   datum=0;
123   bits=0;
124   for (i=0; i < MaxHashTable; i++)
125     hash_code[i]=0;
126   GIFOutputCode(clear_code);
127   /*
128     Encode pixels.
129   */
130   /**** pts ****/
131   pp=ppbeg;
132   waiting_code=*(unsigned char const*)pp++; /* unsigned char BUGFIX at Sun Dec  8 13:17:00 CET 2002 */
133 
134   while (pp!=ppend) {
135       /*
136         Probe hash table.
137       */
138       index=*(unsigned char const*)pp++;
139       k=(int) ((int) index << (MaxGIFBits-8))+waiting_code;
140       if (k >= MaxHashTable)
141         k-=MaxHashTable;
142 #if defined(HasLZW)
143       if (hash_code[k] > 0)
144         {
145           if ((hash_prefix[k] == waiting_code) && (hash_suffix[k] == index))
146             {
147               waiting_code=hash_code[k];
148               continue;
149             }
150           if (k == 0)
151             displacement=1;
152           else
153             displacement=MaxHashTable-k;
154           next_pixel=False;
155           for ( ; ; )
156           {
157             k-=displacement;
158             if (k < 0)
159               k+=MaxHashTable;
160             if (hash_code[k] == 0)
161               break;
162             if ((hash_prefix[k] == waiting_code) && (hash_suffix[k] == index))
163               {
164                 waiting_code=hash_code[k];
165                 next_pixel=True;
166                 break;
167               }
168           }
169           if (next_pixel != False) /* pacify VC6.0 */
170             continue;
171         }
172 #endif
173       GIFOutputCode(waiting_code);
174       // printf("wc=%u\n", waiting_code);
175       if (free_code < MaxGIFTable)
176         {
177           hash_code[k]=free_code++;
178           hash_prefix[k]=waiting_code;
179           hash_suffix[k]=index;
180         }
181       else
182         {
183           /*
184             Fill the hash table with empty entries.
185           */
186           for (k=0; k < MaxHashTable; k++)
187             hash_code[k]=0;
188           /*
189             Reset compressor and issue a clear code.
190           */
191           free_code=clear_code+2;
192           GIFOutputCode(clear_code);
193           number_bits=data_size;
194           max_code=MaxCode(number_bits);
195         }
196       waiting_code=index;
197 #if 0 /**** pts ****/
198       if (QuantumTick(i,image) && (image->previous == (Image2 *) NULL))
199         ProgressMonitor(SaveImageText,i,image->packets);
200 #endif
201   }
202   /*
203     Flush out the buffered code.
204   */
205   GIFOutputCode(waiting_code);
206   GIFOutputCode(end_of_information_code);
207   if (bits > 0)
208     {
209       /*
210         Add a character to current packet.
211       */
212       packet[byte_count++]=(unsigned char) (datum & 0xff);
213       if (byte_count >= 254)
214         {
215           packet[-1]=byte_count;
216           out.vi_write((char*)packet-1, byte_count+1);
217           byte_count=0;
218         }
219     }
220   /*
221     Flush accumulated data.
222   */
223   if (byte_count > 0)
224     {
225       packet[-1]=byte_count;
226       out.vi_write((char*)packet-1, byte_count+1);
227     }
228   /*
229     Free encoder memory.
230   */
231   AFREE(hash_suffix);
232   AFREE(hash_prefix);
233   AFREE(hash_code);
234   AFREE(packet-1);
235   return pp==ppend;
236 }
237 
238 /** This isn't a complete GIF writer. For example, it doesn't support
239  * animation or multiple sub-images. But it supports transparency and
240  * compression. Only works when . The user should call
241  * packPal() first to ensure img->getBpc()==8, and to get a minimal palette.
242  */
out_gif_write(GenBuffer::Writable & out,Image::Indexed * img)243 void out_gif_write(GenBuffer::Writable& out, Image::Indexed *img) {
244   /* Tested and proven to work at Sat Mar 23 13:11:41 CET 2002 */
245   unsigned i, c, bits_per_pixel;
246   signed transp;
247   char hd[19];
248 
249   assert(img->getBpc()==8); /* 1 palette entry == 8 bits */
250 
251   transp=img->getTransp();
252   memcpy(hd, transp!=-1 ? "GIF89a" : "GIF87a", 6);
253   i=img->getWd(); hd[6]=i; hd[7]=i>>8;
254   i=img->getHt(); hd[8]=i; hd[9]=i>>8;
255 
256   // transp=-1; /* With this, transparency will be ignored */
257   c=img->getNcols();
258   bits_per_pixel=1; while (((c-1)>>bits_per_pixel)!=0) bits_per_pixel++;
259   /* ^^^ (c-1) BUGFIX at Mon Oct 20 15:18:24 CEST 2003 */
260   /* 63 -> 6, 64 -> 6, 65 -> 7 */
261   // 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 */
262   // fprintf(stderr, "GIF89 write transp=%d ncols=%d bpp=%d\n", transp, c, bits_per_pixel);
263   assert(1<=bits_per_pixel && bits_per_pixel<=8);
264   c=3*((1<<bits_per_pixel)-c);
265   /* Now: c is the number of padding bytes */
266 
267   hd[10]= 0x80 /* have global colormap */
268         | ((8-1) << 4) /* color resolution: bpc==8 */
269         | (bits_per_pixel-1); /* size of global colormap */
270   hd[11]=0; /* background color: currently unused */
271   hd[12]=0; /* reversed */
272   out.vi_write(hd, 13);
273 
274   // out.vi_write("\xFF\x00\x00" "\x00\xFF\x00" "\x00\x00\xFF", 9);
275 
276   out.vi_write(img->getHeadp(), img->getRowbeg()-img->getHeadp()); /* write colormap */
277   if (c!=0) {
278     char *padding=new char[(unsigned char)c]; /* BUGFIX at Fri Oct 17 18:05:09 CEST 2003 */
279     memset(padding, '\0', (unsigned char)c); /* Not automatic! */
280     out.vi_write(padding, (unsigned char)c);
281     delete [] padding;
282   }
283 
284   if (transp!=-1) {
285     /* Write Graphics Control extension. Only GIF89a */
286     hd[0]=0x21; hd[1]=(char)0xf9; hd[2]=0x04;
287     hd[3]=transp!=-1; /* dispose==0 */
288     hd[4]=hd[5]=0; /* delay==0 */
289     hd[6]=transp; /* transparent color index -- or 255 */
290     hd[7]=0;
291     out.vi_write(hd, 8);
292   }
293 
294   /* Write image header */
295   hd[8]=',';
296   hd[ 9]=hd[10]=0;   /* left */
297   hd[11]=hd[12]=0; /* top  */
298   i=img->getWd(); hd[13]=i; hd[14]=i>>8;
299   i=img->getHt(); hd[15]=i; hd[16]=i>>8;
300   hd[17]=0; /* no interlace, no local colormap, no bits in local colormap */
301 
302   if ((c=bits_per_pixel)<2) c=4;
303   hd[18]=c; /* compression bits_per_pixel */
304   out.vi_write(hd+8, 11);
305 
306 #if 0
307   printf("GIFEncodeImage out r r+%u %u; off=%u\n", img->getRlen()*img->getHt(), c+1, img->getRowbeg()-img->getHeadp());
308   FILE *f=fopen("tjo.dat","wb");
309   fprintf(f, "P6 %u %u 255\n", img->getWd(), img->getHt());
310   // fwrite(img->getRowbeg(), 1, img->getRlen()*img->getHt(), f);
311   for (unsigned u=0; u<img->getRlen()*img->getHt(); u++) {
312     char *p=img->getHeadp()+3* *(unsigned char*)(img->getRowbeg()+u);
313     putc(p[0],f);
314     putc(p[1],f);
315     putc(p[2],f);
316   }
317 #endif
318 
319   i=GIFEncodeImage(out, img->getRowbeg(), img->getRowbeg()+img->getRlen()*img->getHt(), c+1);
320 #if 0
321   { char buf[500000];
322     FILE *f=fopen("tjo.dat","rb");
323     int got=fread(buf, 1, sizeof(buf), f);
324     assert(got==486109);
325     assert(got==img->getRlen()*img->getHt());
326     i=GIFEncodeImage(out, buf, buf+img->getRlen()*img->getHt(), c+1);
327   }
328 #endif
329   assert(i!=0);
330 
331   /* Write trailer */
332   hd[0]=0; hd[1]=';';
333   out.vi_write(hd, 2);
334 }
335 #else
336 #include <stdlib.h>
out_gif_write(GenBuffer::Writable &,Image::Indexed *)337 void out_gif_write(GenBuffer::Writable&, Image::Indexed *) {
338   assert(0);
339   abort();
340 }
341 #endif
342