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