1 /*
2  * giflib/gifout.c
3  * kirk johnson
4  * may 1990
5  *
6  * 27-SEP-2012 modified by Anton Shterenlikht
7  *
8  * Copyright (C) 1989, 1990, 1993-1995, 1999 Kirk Lauritz Johnson
9  *
10  * Parts of the source code (as marked) are:
11  *   Copyright (C) 1989, 1990, 1991 by Jim Frost
12  *   Copyright (C) 1992 by Jamie Zawinski <jwz@lucid.com>
13  *
14  * Permission to use, copy, modify and freely distribute xearth for
15  * non-commercial and not-for-profit purposes is hereby granted
16  * without fee, provided that both the above copyright notice and this
17  * permission notice appear in all copies and in supporting
18  * documentation.
19  *
20  * Unisys Corporation holds worldwide patent rights on the Lempel Zev
21  * Welch (LZW) compression technique employed in the CompuServe GIF
22  * image file format as well as in other formats. Unisys has made it
23  * clear, however, that it does not require licensing or fees to be
24  * paid for freely distributed, non-commercial applications (such as
25  * xearth) that employ LZW/GIF technology. Those wishing further
26  * information about licensing the LZW patent should contact Unisys
27  * directly at (lzw_info@unisys.com) or by writing to
28  *
29  *   Unisys Corporation
30  *   Welch Licensing Department
31  *   M/S-C1SW19
32  *   P.O. Box 500
33  *   Blue Bell, PA 19424
34  *
35  * The author makes no representations about the suitability of this
36  * software for any purpose. It is provided "as is" without express or
37  * implied warranty.
38  *
39  * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
40  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
41  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT
42  * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
43  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
44  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
45  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
46  */
47 
48 #include "xearth.h"
49 #include "gifint.h"
50 
51 
52 /****
53  **
54  ** local #defines
55  **
56  ****/
57 
58 #define HASHSZ     (2048)
59 #define HASH(p, e) (((p)&(HASHSZ-1))^(e))
60 
61 #define PUT_CODE(val)                       \
62 {                                           \
63   work_data |= ((long) (val) << work_bits); \
64   work_bits += code_size;                   \
65   while (work_bits >= 8)                    \
66   {                                         \
67     PUT_BYTE(work_data & 0xFF);             \
68     work_data >>= 8;                        \
69     work_bits  -= 8;                        \
70   }                                         \
71 }
72 
73 #define PUT_BYTE(val)                 \
74 {                                     \
75   buf[buf_idx++] = (val);             \
76   if (buf_idx == 255)                 \
77   {                                   \
78     write_data_block(255, buf, outs); \
79     buf_idx = 0;                      \
80   }                                   \
81 }
82 
83 
84 /****
85  **
86  ** local variables
87  **
88  ****/
89 
90 static int  cmap_bits _P((int));
91 static int  root_bits _P((int));
92 static void put_clr_code _P((void));
93 static void write_data_block _P((int, BYTE *, FILE *));
94 static void reset_string_out _P((void));
95 static void add_string_out _P((int, int));
96 static int  find_string_out _P((int, int));
97 static void gifout_fatal _P((const char *)) _noreturn;
98 
99 
100 static BYTE file_open  = 0;     /* status flags */
101 static BYTE image_open = 0;
102 
103 static int rast_width;          /* raster width */
104 static int rast_height;         /* raster height */
105 static int ncolors;             /* number of colors */
106 static int img_width;           /* image width */
107 static int img_height;          /* image height */
108 
109 static FILE *outs;              /* output file */
110 
111 static int root_size;           /* root code size */
112 static int clr_code;            /* clear code */
113 static int eoi_code;            /* end of info code */
114 static int code_size;           /* current code size */
115 static int code_mask;           /* current code mask */
116 static int old_code;            /* previous code */
117 
118 static long work_data;          /* working bits */
119 static int  work_bits;          /* working bit count */
120 
121 static BYTE buf[256];           /* byte buffer */
122 static int  buf_idx;            /* buffer index */
123 
124 static int table_size;          /* string table size */
125 static int htable[HASHSZ];
126 static int pref_extn[STAB_SIZE]; /* (prefix << 16) | extension */
127 static int next[STAB_SIZE];
128 
129 
130 /****
131  **
132  ** exported procedures
133  **
134  ****/
135 
136 /*
137  * open a GIF file for writing on stream s
138  */
gifout_open_file(s,w,h,sz,cmap,bg)139 int gifout_open_file(s, w, h, sz, cmap, bg)
140      FILE *s;
141      int   w;                   /* raster width (in pixels) */
142      int   h;                   /* raster height (in pixels) */
143      int   sz;                  /* number of colors */
144      BYTE  cmap[3][256];        /* global colormap */
145      int   bg;                  /* background color index */
146 {
147   int i;
148   int pixel_bits;
149 
150   /* make sure there isn't already a file open */
151   if (file_open)
152     return GIFLIB_ERR_FAO;
153 
154   /* remember that we've got this file open */
155   file_open   = 1;
156   outs        = s;
157   rast_width  = w;
158   rast_height = h;
159 
160   /* write GIF signature */
161   if (fwrite(GIF_SIG, sizeof(char), GIF_SIG_LEN, outs) != GIF_SIG_LEN)
162     return GIFLIB_ERR_OUT;
163 
164   /* write screen descriptor */
165   pixel_bits = cmap_bits(sz);
166   ncolors    = 1 << pixel_bits;
167 
168   buf[0] = (w & 0x00FF);
169   buf[1] = (w & 0xFF00) >> 8;
170   buf[2] = (h & 0x00FF);
171   buf[3] = (h & 0xFF00) >> 8;
172   buf[4] = (pixel_bits - 1) | 0x80;
173   buf[5] = bg;
174   buf[6] = 0;
175 
176   if (fwrite(buf, sizeof(char), GIF_SD_SIZE, outs) != GIF_SD_SIZE)
177     return GIFLIB_ERR_OUT;
178 
179   /* write (global) color map */
180   for (i=0; i<sz; i++)
181   {
182     buf[GIFLIB_RED] = cmap[GIFLIB_RED][i];
183     buf[GIFLIB_GRN] = cmap[GIFLIB_GRN][i];
184     buf[GIFLIB_BLU] = cmap[GIFLIB_BLU][i];
185 
186     if (fwrite(buf, sizeof(BYTE), (unsigned) 3, outs) != 3)
187       return GIFLIB_ERR_OUT;
188   }
189 
190   for (i=sz; i<ncolors; i++)
191   {
192     buf[GIFLIB_RED] = 0;
193     buf[GIFLIB_GRN] = 0;
194     buf[GIFLIB_BLU] = 0;
195 
196     if (fwrite(buf, sizeof(BYTE), (unsigned) 3, outs) != 3)
197       return GIFLIB_ERR_OUT;
198   }
199 
200   /* done! */
201   return GIFLIB_SUCCESS;
202 }
203 
204 
205 /*
206  * open a new GIF image for writing in the current GIF file
207  */
gifout_open_image(left,top,w,h)208 int gifout_open_image(left, top, w, h)
209      int left;                  /* column index for left edge */
210      int top;                   /* row index for top edge */
211      int w;                     /* image width (in pixels) */
212      int h;                     /* image height (in pixels) */
213 {
214   /* make sure there's a file open */
215   if (!file_open)
216     return GIFLIB_ERR_NFO;
217 
218   /* make sure there isn't already an image open */
219   if (image_open)
220     return GIFLIB_ERR_IAO;
221 
222   /* remember that we've got this image open */
223   image_open = 1;
224   img_width  = w;
225   img_height = h;
226 
227   /* write image separator */
228   putc(GIF_SEPARATOR, outs);
229 
230   /* write image descriptor */
231   buf[0] = (left & 0x00FF);
232   buf[1] = (left & 0xFF00) >> 8;
233   buf[2] = (top  & 0x00FF);
234   buf[3] = (top  & 0xFF00) >> 8;
235   buf[4] = (w & 0x00FF);
236   buf[5] = (w & 0xFF00) >> 8;
237   buf[6] = (h & 0x00FF);
238   buf[7] = (h & 0xFF00) >> 8;
239   buf[8] = 0;
240 
241   if (fwrite(buf, sizeof(BYTE), GIF_ID_SIZE, outs) != GIF_ID_SIZE)
242     return GIFLIB_ERR_OUT;
243 
244   /* initialize raster data stream */
245   root_size = root_bits(ncolors);
246   putc(root_size, outs);
247 
248   clr_code  = 1 << root_size;
249   eoi_code  = clr_code + 1;
250   code_size = root_size + 1;
251   code_mask = (1 << code_size) - 1;
252   old_code  = NULL_CODE;
253 
254   work_bits = 0;
255   work_data = 0;
256 
257   buf_idx = 0;
258 
259   /* initialize string table */
260   reset_string_out();
261 
262   /* output initial clear code */
263   put_clr_code();
264 
265   /* done! */
266   return GIFLIB_SUCCESS;
267 }
268 
269 
270 /*
271  * write a pixel into the current image
272  */
gifout_put_pixel(val)273 void gifout_put_pixel(val)
274      int val;                   /* pixel color index */
275 {
276   int idx;
277 
278   /* see if string is in table already */
279   idx = find_string_out(old_code, val);
280 
281   if (idx != NULL_CODE)
282   {
283     /* found a match */
284     old_code = idx;
285   }
286   else
287   {
288     /* no match */
289     PUT_CODE(old_code);
290     add_string_out(old_code, val);
291     old_code = val;
292 
293     /* check for full string table */
294     if (table_size == STAB_SIZE)
295     {
296       /* output remaining code */
297       PUT_CODE(old_code);
298 
299       /* reset encoder */
300       put_clr_code();
301     }
302   }
303 }
304 
305 
306 /*
307  * write a row of pixels into the current image
308  */
gifout_put_row(row)309 void gifout_put_row(row)
310      int *row;                  /* array of size img_width */
311 {
312   int col;
313   int idx;
314 
315   for (col=0; col<img_width; col++)
316   {
317     /* see if string is in table already */
318     idx = find_string_out(old_code, row[col]);
319 
320     if (idx != NULL_CODE)
321     {
322       /* found a match */
323       old_code = idx;
324     }
325     else
326     {
327       /* no match */
328       PUT_CODE(old_code);
329       add_string_out(old_code, row[col]);
330       old_code = row[col];
331 
332       /* check for full string table */
333       if (table_size == STAB_SIZE)
334       {
335         /* output remaining code */
336         PUT_CODE(old_code);
337 
338         /* reset encoder */
339         put_clr_code();
340       }
341     }
342   }
343 }
344 
345 
346 /*
347  * close an open GIF image
348  */
gifout_close_image()349 int gifout_close_image()
350 {
351   /* make sure there's an image open */
352   if (!image_open)
353     return GIFLIB_ERR_NIO;
354 
355   /* flush any remaining code */
356   if (old_code != NULL_CODE)
357     PUT_CODE(old_code);
358 
359   /* output end of info code */
360   PUT_CODE(eoi_code);
361 
362   /* flush any extra bits */
363   while (work_bits > 0)
364   {
365     PUT_BYTE(work_data & 0xFF);
366     work_data >>= 8;
367     work_bits  -= 8;
368   }
369 
370   /* flush any extra bytes */
371   if (buf_idx > 0)
372     write_data_block(buf_idx, buf, outs);
373 
374   /* trailing zero byte */
375   putc(0, outs);
376 
377   /* mark image as closed */
378   image_open = 0;
379 
380   /* done! */
381   return GIFLIB_SUCCESS;
382 }
383 
384 
385 /*
386  * close an open GIF file
387  */
gifout_close_file()388 int gifout_close_file()
389 {
390   /* make sure there's a file open */
391   if (!file_open)
392     return GIFLIB_ERR_NFO;
393 
394   /* make sure there isn't an image open */
395   if (image_open)
396     return GIFLIB_ERR_ISO;
397 
398   /* write gif terminator */
399   putc(GIF_TERMINATOR, outs);
400 
401   /* mark file as closed */
402   file_open = 0;
403 
404   /* done! */
405   return GIFLIB_SUCCESS;
406 }
407 
408 
409 /****
410  **
411  ** internal procedures
412  **
413  ****/
414 
cmap_bits(n)415 static int cmap_bits(n)
416      int n;
417 {
418   int nbits;
419 
420   if (n < 2)
421     gifout_fatal("cmap_bits(): argument out of range");
422 
423   n    -= 1;
424   nbits = 0;
425 
426   while (n != 0)
427   {
428     n    >>= 1;
429     nbits += 1;
430   }
431 
432   return nbits;
433 }
434 
435 
root_bits(n)436 static int root_bits(n)
437      int n;
438 {
439   int rslt;
440 
441   rslt = cmap_bits(n);
442   if (rslt < 2)
443     rslt = 2;
444 
445   return rslt;
446 }
447 
448 
put_clr_code()449 static void put_clr_code()
450 {
451   /* output clear code */
452   PUT_CODE(clr_code);
453 
454   /* reset raster data stream */
455   code_size = root_size + 1;
456   code_mask = (1 << code_size) - 1;
457   old_code  = NULL_CODE;
458 
459   /* clear the string table */
460   reset_string_out();
461 }
462 
463 
write_data_block(cnt,outbuf,s)464 static void write_data_block(cnt, outbuf, s)
465      int   cnt;
466      BYTE *outbuf;
467      FILE *s;
468 {
469   putc(cnt, s);
470 
471   if (fwrite(outbuf, sizeof(BYTE), (unsigned) cnt, s) != cnt)
472     gifout_fatal("write_data_block(): problems writing data block");
473 }
474 
475 
reset_string_out()476 static void reset_string_out()
477 {
478   int i;
479 
480   for (i=0; i<HASHSZ; i++)
481     htable[i] = NULL_CODE;
482 
483   table_size = eoi_code + 1;
484 }
485 
486 
add_string_out(p,e)487 static void add_string_out(p, e)
488      int p;
489      int e;
490 {
491   int idx;
492 
493   idx = HASH(p, e);
494 
495   pref_extn[table_size] = (p << 16) | e;
496   next[table_size]      = htable[idx];
497   htable[idx]           = table_size;
498 
499   if ((table_size > code_mask) && (code_size < 12))
500   {
501     code_size += 1;
502     code_mask  = (1 << code_size) - 1;
503   }
504 
505   table_size += 1;
506 }
507 
508 
find_string_out(p,e)509 static int find_string_out(p, e)
510      int p;
511      int e;
512 {
513   int idx;
514   int tmp;
515   int rslt;
516 
517   if (p == NULL_CODE)
518   {
519     /* a lone symbol is always in table */
520     rslt = e;
521   }
522   else
523   {
524     rslt = NULL_CODE;
525 
526     /* search the hash table */
527     idx = htable[HASH(p, e)];
528     tmp = (p << 16) | e;
529     while (idx != NULL_CODE)
530     {
531       if (pref_extn[idx] == tmp)
532       {
533         rslt = idx;
534         break;
535       }
536       else
537       {
538         idx = next[idx];
539       }
540     }
541   }
542 
543   return rslt;
544 }
545 
546 
547 /*
548  * semi-graceful fatal error mechanism
549  */
gifout_fatal(msg)550 static void gifout_fatal(msg)
551      const char *msg;
552 {
553   fprintf(stderr, "\n");
554   fprintf(stderr, "gifout.c: fatal error\n");
555   fprintf(stderr, "    %s\n", msg);
556   exit(1);
557 }
558