386BSD 0.1 development
[unix-history] / usr / othersrc / public / ghostscript-2.4.1 / gdevgif.c
CommitLineData
264eb8c7
WJ
1/* Copyright (C) 1992 Aladdin Enterprises. All rights reserved.
2 Distributed by Free Software Foundation, Inc.
3
4This file is part of Ghostscript.
5
6Ghostscript is distributed in the hope that it will be useful, but
7WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
8to anyone for the consequences of using it or for whether it serves any
9particular purpose or works at all, unless he says so in writing. Refer
10to the Ghostscript General Public License for full details.
11
12Everyone is granted permission to copy, modify and redistribute
13Ghostscript, but only under the conditions described in the Ghostscript
14General Public License. A copy of this license is supposed to have been
15given to you along with Ghostscript so you can know your rights and
16responsibilities. It should be in a file named COPYING. Among other
17things, the copyright notice and this notice must be preserved on all
18copies. */
19
20/* gdevgif.c */
21/* GIF output device for Ghostscript. */
22#include "gdevprn.h"
23#include "gserrors.h" /* REALLY? */
24#include "gdevpccm.h"
25
26/* Thanks to Phil Conrad for donating the original version */
27/* of these drivers to Aladdin Enterprises. */
28
29/* ------ The device descriptors ------ */
30
31/*
32 * Standard U.S. page width and height. A4 paper is 8.4" x 11.7".
33 */
34#define WIDTH_10THS 85
35#define HEIGHT_10THS 110
36
37/*
38 * Default X and Y resolution.
39 */
40#define X_DPI 72
41#define Y_DPI 72
42
43/* The same print_page routine currently serves for */
44/* both monochrome and color. */
45private dev_proc_print_page(gif_print_page);
46
47/* Monochrome. */
48
49gx_device_printer gs_gifmono_device =
50 prn_device(prn_std_procs, "gifmono",
51 WIDTH_10THS, HEIGHT_10THS,
52 X_DPI, Y_DPI,
53 0,0,0,0, /* margins */
54 1, gif_print_page);
55
56/* Chunky 8-bit (SuperVGA-style) color. */
57/* (Uses a fixed palette of 3,3,2 bits.) */
58
59private gx_device_procs gif8_procs =
60 prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
61 pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
62gx_device_printer gs_gif8_device =
63 prn_device(gif8_procs, "gif8",
64 WIDTH_10THS, HEIGHT_10THS,
65 X_DPI, Y_DPI,
66 0,0,0,0, /* margins */
67 8, gif_print_page);
68
69/* ------ Private definitions ------ */
70
71typedef struct gif_header_s {
72 byte signature[3]; /* magic number == 'GIF' */
73 byte version[3]; /* version # '87a' or '89a' */
74 ushort width; /* screen width */
75 ushort height; /* screen height */
76
77/* struct { /* bit structure of flags */
78/* unsigned globalcolor:1; /* global color table flag - MSB*/
79#define globalcolor_shift 7
80/* unsigned colorres:3; /* bits/color */
81#define colorres_shift 4
82/* unsigned sort:1; /* color table sorted */
83#define sort_shift 3
84/* unsigned colorsize:3; /* 2^colorsize bytes in color table -LSB */
85#define colorsize_shift 0
86/* } flags; */
87
88 byte flags;
89 byte background; /* background color index */
90 byte aspect; /* pixel aspect ratio */
91 /* ratio = (aspect + 15) / 64 */
92} gif_header;
93
94typedef struct image_descriptor_s {
95/* byte separator; /* image separator == 0x2c */
96 ushort left_pos; /* image left pos (pixels) */
97 ushort top_pos; /* image top pos (pixels) */
98 ushort width; /* image width (pixels) */
99 ushort height; /* image height (pixels) */
100
101/* struct { */
102/* unsigned localcolor:1; /* local color table flag */
103/* unsigned interlace:1; /* image interlaced 0=no */
104/* unsigned sort:1; /* color table sorted 0=no*/
105/* unsigned resv:2; */
106/* unsigned localsize:3; /* 2^localsize+1 = color table size */
107/* } flags; */
108
109 byte flags;
110} image_descriptor;
111
112/* State for reading bits from image. */
113typedef struct pixel_reader_s {
114 byte *next;
115 int bits_left;
116 uint bit_buffer; /* low-order bits_left bits are valid */
117} pixel_reader;
118
119/********************************************************/
120/* LZW routines are based on: */
121/* Dr. Dobbs Journal --- Oct. 1989. */
122/* Article on LZW Data Compression by Mark R. Nelson */
123/********************************************************/
124
125#define MAX_BITS 12 /* this is max for GIF. */
126
127#define TABLE_SIZE 5021 /* this is max for 12-bit codes */
128
129/* State of LZW encoder */
130typedef struct code_entry_s {
131 int code_value;
132 ushort prefix_code;
133 byte append_character;
134} code_entry;
135typedef struct lzw_encoder_s {
136 int bits;
137 ushort Max_Code;
138 ushort Clear_code;
139 ushort next_code;
140 FILE *file;
141 code_entry *table;
142 ushort string_code;
143 /* State of output buffer */
144 byte output_bit_buffer;
145 int output_bit_count; /* # of valid low-order bits */
146 /* (between 0 and 7) in buffer */
147 uint byte_count;
148 byte gif_buffer[260];
149} lzw_encoder;
150
151/* Initialize LZW encoder */
152private void lzw_set_bits(P2(register lzw_encoder _ss *, int));
153private void lzw_reset(P1(register lzw_encoder _ss *));
154private int
155lzw_init(register lzw_encoder _ss *pe, int bits, FILE *file)
156{ lzw_set_bits(pe, bits);
157 pe->Clear_code = (1 << bits);
158 pe->file = file;
159 pe->byte_count = 1;
160 pe->output_bit_count = 0;
161 pe->output_bit_buffer = 0;
162 pe->table = (code_entry *)gs_malloc(TABLE_SIZE, sizeof(code_entry), "GIF code table");
163
164 if ( pe->table == 0 )
165 return gs_error_VMerror; /* can't allocate buffers */
166
167 lzw_reset(pe);
168 pe->string_code = 0;
169 return 0;
170}
171/* Establish the width of the code in bits */
172private void
173lzw_set_bits(register lzw_encoder _ss *pe, int bits)
174{ pe->bits = bits;
175 pe->Max_Code = (1 << (bits+1)) - 1;
176}
177/* Reset the encoding table */
178private void
179lzw_reset(register lzw_encoder _ss *pe)
180{ int index;
181 for ( index = 0; index < TABLE_SIZE; index++ )
182 pe->table[index].code_value = -1;
183 pe->next_code = pe->Clear_code + 2;
184}
185
186/* Put out (data) of length (bits) to GIF buffer */
187private void
188lzw_putc(register lzw_encoder _ss *pe, uint data)
189{ int bits = pe->bits + 1; /* output width */
190 ulong buffer = pe->output_bit_buffer | ((ulong)data << pe->output_bit_count);
191 pe->output_bit_count += bits;
192 while ( pe->output_bit_count >= 8 )
193 { /* putc(output_bit_buffer >> 24, file); */
194 pe->gif_buffer[pe->byte_count] = (byte)buffer; /* low byte */
195 buffer >>= 8;
196 pe->output_bit_count -= 8;
197 pe->byte_count++;
198 if ( pe->byte_count == 256 )
199 { pe->byte_count = 1;
200 pe->gif_buffer[0] = 255; /* byte count for block */
201 fwrite(pe->gif_buffer, 1, 256, pe->file);
202 }
203 }
204 pe->output_bit_buffer = (byte)buffer;
205}
206
207/* Finish encoding, and flush the buffers. */
208private void
209lzw_finish(register lzw_encoder _ss *pe)
210{ lzw_putc(pe, pe->string_code); /* output last code */
211 lzw_putc(pe, pe->Clear_code+1); /* output eof code */
212 lzw_putc(pe, 0); /* force out last code */
213 if ( pe->byte_count != 1 )
214 { pe->gif_buffer[0] = pe->byte_count;
215 fwrite(pe->gif_buffer, 1, pe->byte_count+1, pe->file);
216 }
217}
218
219/* Terminate LZW encoder. */
220private void
221lzw_exit(register lzw_encoder _ss *pe)
222{ gs_free((char *)pe->table, TABLE_SIZE, sizeof(code_entry), "GIF code table");
223}
224
225/* Get the next (depth) bits from the pixel buffer. */
226private uint
227lzw_getc(pixel_reader _ss *pr, uint depth)
228{ if ( pr->bits_left < depth )
229 { pr->bit_buffer <<= 8;
230 pr->bit_buffer |= *(pr->next++);
231 pr->bits_left += 8;
232 }
233 pr->bits_left -= depth;
234 return (pr->bit_buffer >> pr->bits_left) & ((1 << depth) - 1);
235}
236
237/* Output 1 row of data in GIF (LZW) format. */
238private void
239lzw(byte *from, byte *end, register lzw_encoder _ss *pe, byte depth)
240{ pixel_reader reader;
241 /* Set up the reader. */
242 reader.next = from;
243 reader.bits_left = 0;
244
245 if ( pe->next_code == (pe->Clear_code + 2)) /* first time through */
246 { pe->string_code = lzw_getc(&reader, depth);
247 }
248
249 while ( reader.next < end || reader.bits_left >= depth )
250 { uint data = lzw_getc(&reader, depth); /* actually only a byte */
251
252 /* Hash to find a match for the prefix+char */
253 /* string in the string table */
254
255 ushort hash_prefix = pe->string_code;
256 int index = (data << 4) ^ hash_prefix;
257 int hash_offset;
258 register code_entry *pce;
259
260 if ( index == 0 )
261 hash_offset = 1;
262 else
263 hash_offset = TABLE_SIZE - index;
264
265 while ( 1 )
266 { pce = &pe->table[index];
267 if ( pce->code_value == -1 )
268 break;
269 if ( pce->prefix_code == hash_prefix &&
270 pce->append_character == data )
271 break;
272 index -= hash_offset;
273 if ( index < 0 )
274 index += TABLE_SIZE;
275 }
276 if ( pce->code_value != -1 )
277 pe->string_code = pce->code_value;
278 else
279 { /* Make a new entry */
280 pce->code_value = pe->next_code++;
281 pce->prefix_code = pe->string_code;
282 pce->append_character = data;
283
284 lzw_putc(pe, pe->string_code);
285
286 if ( pe->next_code > (pe->Max_Code + 1) )
287 { /* Increment the width of the code */
288 if ( pe->bits+1 >= MAX_BITS )
289 { /* output clear code first*/
290 lzw_putc(pe, pe->Clear_code);
291 pe->bits = (depth == 1 ? 2 : depth);
292 lzw_reset(pe);
293 }
294 else
295 pe->bits++;
296 lzw_set_bits(pe, pe->bits);
297 }
298 pe->string_code = data;
299 }
300 }
301}
302
303
304/* Write a page to a file in GIF format. */
305int
306gif_print_page(gx_device_printer *pdev, FILE *file)
307{ int raster = gdev_prn_bytes_per_scan_line(pdev);
308 int height = pdev->height;
309 int depth = pdev->color_info.depth;
310 byte *row = (byte *)gs_malloc(raster * 2, 1, "gif file buffer");
311 byte *end = row + raster;
312 gif_header header;
313 image_descriptor header_desc;
314 lzw_encoder encoder;
315 int y;
316 int code = 0; /* return code */
317
318 if ( row == 0 ) /* can't allocate row buffer */
319 return gs_error_VMerror;
320 code = lzw_init(&encoder, (depth == 1 ? 2 : depth), file);
321 if ( code < 0 )
322 return code;
323
324 /* Set up the header. */
325
326 memcpy(header.signature, "GIF", 3);
327 memcpy(header.version, "87a", 3);
328 header.width = raster * (8/depth); /*pdev->width;*/
329 /* for most decoders the */
330 /* width must be on byte */
331 /* boundry */
332 header.height = height;
333/* header.flags.globalcolor = TRUE; */
334/* header.flags.colorres = depth-1; */
335/* header.flags.sort = FALSE; */
336/* header.flags.colorsize = depth-1; */
337 header.flags =
338 (1 << globalcolor_shift) +
339 ((depth - 1) << colorres_shift) +
340 (0 << sort_shift) +
341 ((depth - 1) << colorsize_shift);
342 header.background = 0;
343 header.aspect = 0;
344
345 /* Write the header. */
346
347 if ( fwrite(&header, 1, 13, file) < 13 )
348 { code = gs_error_ioerror;
349 goto gif_done;
350 }
351
352 /* Write the header global color palette. */
353
354 if ( pc_write_palette((gx_device *)pdev, 1 << depth, file) < 0 )
355 { code = gs_error_ioerror;
356 goto gif_done;
357 }
358
359 header_desc.left_pos = 0;
360 header_desc.top_pos = 0;
361 header_desc.width = raster * (8/depth); /*pdev->width;*/
362 header_desc.height = height;
363/* header_desc.flags.localcolor = FALSE; */
364/* header_desc.flags.interlace = FALSE; */
365/* header_desc.flags.sort = FALSE; */
366/* header_desc.flags.localsize = 0; */
367
368 header_desc.flags = 0;
369
370 /* Write the header image descriptor. */
371
372 fputc(0x2c,file); /* start with separator */
373 if ( fwrite(&header_desc, 1, 9, file) < 9 )
374 { code = gs_error_ioerror;
375 goto gif_done;
376 }
377
378 fflush(file);
379
380 printf("The Graphics Interchange Format(c) is \n");
381 printf("the Copyright Property of CompuServe Incorporated.\n");
382 printf("GIF(sm) is a Service Mark property of CompuServe Incorporated\n");
383
384 fputc(encoder.bits, file); /* start with code size */
385
386 lzw_putc(&encoder, encoder.Clear_code); /* output clear code first*/
387
388 /* Dump the contents of the image. */
389 for ( y = 0; y < height; y++ )
390 { gdev_prn_copy_scan_lines(pdev, y, row, raster);
391 lzw(row, end, &encoder, depth);
392 }
393
394 lzw_finish(&encoder);
395 fputc(0, file);
396 fputc(0x3b, file); /* EOF indicator */
397
398gif_done:
399 lzw_exit(&encoder);
400 gs_free((char *)row, raster * 2, 1, "gif file buffer");
401 return code;
402}