/* * giflib/gifout.c * kirk johnson * may 1990 * * Copyright (C) 1989, 1990, 1993-1995, 1999 Kirk Lauritz Johnson * * Parts of the source code (as marked) are: * Copyright (C) 1989, 1990, 1991 by Jim Frost * Copyright (C) 1992 by Jamie Zawinski * * Permission to use, copy, modify and freely distribute xearth for * non-commercial and not-for-profit purposes is hereby granted * without fee, provided that both the above copyright notice and this * permission notice appear in all copies and in supporting * documentation. * * Unisys Corporation holds worldwide patent rights on the Lempel Zev * Welch (LZW) compression technique employed in the CompuServe GIF * image file format as well as in other formats. Unisys has made it * clear, however, that it does not require licensing or fees to be * paid for freely distributed, non-commercial applications (such as * xearth) that employ LZW/GIF technology. Those wishing further * information about licensing the LZW patent should contact Unisys * directly at (lzw_info@unisys.com) or by writing to * * Unisys Corporation * Welch Licensing Department * M/S-C1SW19 * P.O. Box 500 * Blue Bell, PA 19424 * * The author makes no representations about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "port.h" #include "gifint.h" #include "kljcpyrt.h" /**** ** ** local #defines ** ****/ #define HASHSZ (2048) #define HASH(p, e) (((p)&(HASHSZ-1))^(e)) #define PUT_CODE(val) \ { \ work_data |= ((long) (val) << work_bits); \ work_bits += code_size; \ while (work_bits >= 8) \ { \ PUT_BYTE(work_data & 0xFF); \ work_data >>= 8; \ work_bits -= 8; \ } \ } #define PUT_BYTE(val) \ { \ buf[buf_idx++] = (val); \ if (buf_idx == 255) \ { \ write_data_block(255, buf, outs); \ buf_idx = 0; \ } \ } /**** ** ** local variables ** ****/ static int cmap_bits _P((int)); static int root_bits _P((int)); static void put_clr_code _P((void)); static void write_data_block _P((int, BYTE *, FILE *)); static void reset_string_out _P((void)); static void add_string_out _P((int, int)); static int find_string_out _P((int, int)); static void gifout_fatal _P((const char *)) _noreturn; static BYTE file_open = 0; /* status flags */ static BYTE image_open = 0; static int rast_width; /* raster width */ static int rast_height; /* raster height */ static int ncolors; /* number of colors */ static int img_width; /* image width */ static int img_height; /* image height */ static FILE *outs; /* output file */ static int root_size; /* root code size */ static int clr_code; /* clear code */ static int eoi_code; /* end of info code */ static int code_size; /* current code size */ static int code_mask; /* current code mask */ static int old_code; /* previous code */ static long work_data; /* working bits */ static int work_bits; /* working bit count */ static BYTE buf[256]; /* byte buffer */ static int buf_idx; /* buffer index */ static int table_size; /* string table size */ static int htable[HASHSZ]; static int pref_extn[STAB_SIZE]; /* (prefix << 16) | extension */ static int next[STAB_SIZE]; /**** ** ** exported procedures ** ****/ /* * open a GIF file for writing on stream s */ int gifout_open_file(s, w, h, sz, cmap, bg) FILE *s; int w; /* raster width (in pixels) */ int h; /* raster height (in pixels) */ int sz; /* number of colors */ BYTE cmap[3][256]; /* global colormap */ int bg; /* background color index */ { int i; int pixel_bits; /* make sure there isn't already a file open */ if (file_open) return GIFLIB_ERR_FAO; /* remember that we've got this file open */ file_open = 1; outs = s; rast_width = w; rast_height = h; /* write GIF signature */ if (fwrite(GIF_SIG, sizeof(char), GIF_SIG_LEN, outs) != GIF_SIG_LEN) return GIFLIB_ERR_OUT; /* write screen descriptor */ pixel_bits = cmap_bits(sz); ncolors = 1 << pixel_bits; buf[0] = (w & 0x00FF); buf[1] = (w & 0xFF00) >> 8; buf[2] = (h & 0x00FF); buf[3] = (h & 0xFF00) >> 8; buf[4] = (pixel_bits - 1) | 0x80; buf[5] = bg; buf[6] = 0; if (fwrite(buf, sizeof(char), GIF_SD_SIZE, outs) != GIF_SD_SIZE) return GIFLIB_ERR_OUT; /* write (global) color map */ for (i=0; i> 8; buf[2] = (top & 0x00FF); buf[3] = (top & 0xFF00) >> 8; buf[4] = (w & 0x00FF); buf[5] = (w & 0xFF00) >> 8; buf[6] = (h & 0x00FF); buf[7] = (h & 0xFF00) >> 8; buf[8] = 0; if (fwrite(buf, sizeof(BYTE), GIF_ID_SIZE, outs) != GIF_ID_SIZE) return GIFLIB_ERR_OUT; /* initialize raster data stream */ root_size = root_bits(ncolors); putc(root_size, outs); clr_code = 1 << root_size; eoi_code = clr_code + 1; code_size = root_size + 1; code_mask = (1 << code_size) - 1; old_code = NULL_CODE; work_bits = 0; work_data = 0; buf_idx = 0; /* initialize string table */ reset_string_out(); /* output initial clear code */ put_clr_code(); /* done! */ return GIFLIB_SUCCESS; } /* * write a pixel into the current image */ void gifout_put_pixel(val) int val; /* pixel color index */ { int idx; /* see if string is in table already */ idx = find_string_out(old_code, val); if (idx != NULL_CODE) { /* found a match */ old_code = idx; } else { /* no match */ PUT_CODE(old_code); add_string_out(old_code, val); old_code = val; /* check for full string table */ if (table_size == STAB_SIZE) { /* output remaining code */ PUT_CODE(old_code); /* reset encoder */ put_clr_code(); } } } /* * write a row of pixels into the current image */ void gifout_put_row(row) int *row; /* array of size img_width */ { int col; int idx; for (col=0; col 0) { PUT_BYTE(work_data & 0xFF); work_data >>= 8; work_bits -= 8; } /* flush any extra bytes */ if (buf_idx > 0) write_data_block(buf_idx, buf, outs); /* trailing zero byte */ putc(0, outs); /* mark image as closed */ image_open = 0; /* done! */ return GIFLIB_SUCCESS; } /* * close an open GIF file */ int gifout_close_file() { /* make sure there's a file open */ if (!file_open) return GIFLIB_ERR_NFO; /* make sure there isn't an image open */ if (image_open) return GIFLIB_ERR_ISO; /* write gif terminator */ putc(GIF_TERMINATOR, outs); /* mark file as closed */ file_open = 0; /* done! */ return GIFLIB_SUCCESS; } /**** ** ** internal procedures ** ****/ static int cmap_bits(n) int n; { int nbits; if (n < 2) gifout_fatal("cmap_bits(): argument out of range"); n -= 1; nbits = 0; while (n != 0) { n >>= 1; nbits += 1; } return nbits; } static int root_bits(n) int n; { int rslt; rslt = cmap_bits(n); if (rslt < 2) rslt = 2; return rslt; } static void put_clr_code() { /* output clear code */ PUT_CODE(clr_code); /* reset raster data stream */ code_size = root_size + 1; code_mask = (1 << code_size) - 1; old_code = NULL_CODE; /* clear the string table */ reset_string_out(); } static void write_data_block(cnt, outbuf, s) int cnt; BYTE *outbuf; FILE *s; { putc(cnt, s); if (fwrite(outbuf, sizeof(BYTE), (unsigned) cnt, s) != cnt) gifout_fatal("write_data_block(): problems writing data block"); } static void reset_string_out() { int i; for (i=0; i code_mask) && (code_size < 12)) { code_size += 1; code_mask = (1 << code_size) - 1; } table_size += 1; } static int find_string_out(p, e) int p; int e; { int idx; int tmp; int rslt; if (p == NULL_CODE) { /* a lone symbol is always in table */ rslt = e; } else { rslt = NULL_CODE; /* search the hash table */ idx = htable[HASH(p, e)]; tmp = (p << 16) | e; while (idx != NULL_CODE) { if (pref_extn[idx] == tmp) { rslt = idx; break; } else { idx = next[idx]; } } } return rslt; } /* * semi-graceful fatal error mechanism */ static void gifout_fatal(msg) const char *msg; { fprintf(stderr, "\n"); fprintf(stderr, "gifout.c: fatal error\n"); fprintf(stderr, " %s\n", msg); exit(1); }