Commit | Line | Data |
---|---|---|
264eb8c7 WJ |
1 | /* Copyright (C) 1992 Aladdin Enterprises. All rights reserved. |
2 | Distributed by Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of Ghostscript. | |
5 | ||
6 | Ghostscript is distributed in the hope that it will be useful, but | |
7 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility | |
8 | to anyone for the consequences of using it or for whether it serves any | |
9 | particular purpose or works at all, unless he says so in writing. Refer | |
10 | to the Ghostscript General Public License for full details. | |
11 | ||
12 | Everyone is granted permission to copy, modify and redistribute | |
13 | Ghostscript, but only under the conditions described in the Ghostscript | |
14 | General Public License. A copy of this license is supposed to have been | |
15 | given to you along with Ghostscript so you can know your rights and | |
16 | responsibilities. It should be in a file named COPYING. Among other | |
17 | things, the copyright notice and this notice must be preserved on all | |
18 | copies. */ | |
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. */ | |
45 | private dev_proc_print_page(gif_print_page); | |
46 | ||
47 | /* Monochrome. */ | |
48 | ||
49 | gx_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 | ||
59 | private 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); | |
62 | gx_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 | ||
71 | typedef 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 | ||
94 | typedef 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. */ | |
113 | typedef 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 */ | |
130 | typedef struct code_entry_s { | |
131 | int code_value; | |
132 | ushort prefix_code; | |
133 | byte append_character; | |
134 | } code_entry; | |
135 | typedef 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 */ | |
152 | private void lzw_set_bits(P2(register lzw_encoder _ss *, int)); | |
153 | private void lzw_reset(P1(register lzw_encoder _ss *)); | |
154 | private int | |
155 | lzw_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 */ | |
172 | private void | |
173 | lzw_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 */ | |
178 | private void | |
179 | lzw_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 */ | |
187 | private void | |
188 | lzw_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. */ | |
208 | private void | |
209 | lzw_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. */ | |
220 | private void | |
221 | lzw_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. */ | |
226 | private uint | |
227 | lzw_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. */ | |
238 | private void | |
239 | lzw(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. */ | |
305 | int | |
306 | gif_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 | ||
398 | gif_done: | |
399 | lzw_exit(&encoder); | |
400 | gs_free((char *)row, raster * 2, 1, "gif file buffer"); | |
401 | return code; | |
402 | } |