home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1992, 1993 Aladdin Enterprises. All rights reserved.
-
- This file is part of Ghostscript.
-
- Ghostscript is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
- to anyone for the consequences of using it or for whether it serves any
- particular purpose or works at all, unless he says so in writing. Refer
- to the Ghostscript General Public License for full details.
-
- Everyone is granted permission to copy, modify and redistribute
- Ghostscript, but only under the conditions described in the Ghostscript
- General Public License. A copy of this license is supposed to have been
- given to you along with Ghostscript so you can know your rights and
- responsibilities. It should be in a file named COPYING. Among other
- things, the copyright notice and this notice must be preserved on all
- copies. */
-
- /* gdevgif.c */
- /* GIF output device for Ghostscript. */
- #include "gdevprn.h"
- #include "gserrors.h"
- #include "gdevpccm.h"
-
- /* Thanks to Phil Conrad for donating the original version */
- /* of these drivers to Aladdin Enterprises. */
-
- /* ------ The device descriptors ------ */
-
- /*
- * Default X and Y resolution.
- */
- #define X_DPI 72
- #define Y_DPI 72
-
- /* The same print_page routine currently serves for */
- /* both monochrome and color. */
- private dev_proc_open_device(gif_open);
- private dev_proc_print_page(gif_print_page);
- private dev_proc_close_device(gif_close);
-
- /* Monochrome. */
-
- private gx_device_procs gifmono_procs =
- prn_procs(gif_open, gdev_prn_output_page, gif_close);
- gx_device_printer far_data gs_gifmono_device =
- prn_device(gifmono_procs, "gifmono",
- DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
- X_DPI, Y_DPI,
- 0,0,0,0, /* margins */
- 1, gif_print_page);
-
- /* Chunky 8-bit (SuperVGA-style) color. */
- /* (Uses a fixed palette of 3,3,2 bits.) */
-
- private gx_device_procs gif8_procs =
- prn_color_procs(gif_open, gdev_prn_output_page, gif_close,
- pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
- gx_device_printer far_data gs_gif8_device =
- prn_device(gif8_procs, "gif8",
- DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
- X_DPI, Y_DPI,
- 0,0,0,0, /* margins */
- 8, gif_print_page);
-
- /* ------ Private definitions ------ */
-
- /* All two-byte quantities are stored LSB-first! */
- #if arch_is_big_endian
- # define assign_ushort(a,v) a = ((v) >> 8) + ((v) << 8)
- #else
- # define assign_ushort(a,v) a = (v)
- #endif
-
- typedef struct gif_header_s {
- byte signature[3]; /* magic number == 'GIF' */
- byte version[3]; /* version # '87a' or '89a' */
- ushort width; /* screen width */
- ushort height; /* screen height */
-
- /* struct { /* bit structure of flags */
- /* unsigned globalcolor:1; /* global color table flag - MSB*/
- #define globalcolor_shift 7
- /* unsigned colorres:3; /* bits/color */
- #define colorres_shift 4
- /* unsigned sort:1; /* color table sorted */
- #define sort_shift 3
- /* unsigned colorsize:3; /* 2^colorsize bytes in color table -LSB */
- #define colorsize_shift 0
- /* } flags; */
-
- byte flags;
- byte background; /* background color index */
- byte aspect; /* pixel aspect ratio */
- /* ratio = (aspect + 15) / 64 */
- } gif_header;
-
- typedef struct image_descriptor_s {
- /* byte separator; /* image separator == 0x2c */
- ushort left_pos; /* image left pos (pixels) */
- ushort top_pos; /* image top pos (pixels) */
- ushort width; /* image width (pixels) */
- ushort height; /* image height (pixels) */
-
- /* struct { */
- /* unsigned localcolor:1; /* local color table flag */
- /* unsigned interlace:1; /* image interlaced 0=no */
- /* unsigned sort:1; /* color table sorted 0=no*/
- /* unsigned resv:2; */
- /* unsigned localsize:3; /* 2^localsize+1 = color table size */
- /* } flags; */
-
- byte flags;
- } image_descriptor;
-
- /********************************************************/
- /* LZW routines are based on: */
- /* Dr. Dobbs Journal --- Oct. 1989. */
- /* Article on LZW Data Compression by Mark R. Nelson */
- /********************************************************/
-
- #define MAX_BITS 12 /* this is max for GIF. */
-
- #define TABLE_SIZE 5123 /* this is max for 12-bit codes */
- #define TABLE_HASH_SHIFT 2 /* size < 4095 + (4095 >> shift) */
-
- /* State of LZW encoder */
- typedef struct code_entry_s {
- int code_value;
- ushort prefix_code;
- byte append_character;
- } code_entry;
- typedef struct lzw_encoder_s {
- int bits;
- ushort Max_Code;
- ushort Clear_code;
- ushort next_code;
- FILE *file;
- code_entry *table;
- ushort string_code;
- /* State of output buffer */
- byte output_bit_buffer;
- int output_bit_count; /* # of valid low-order bits */
- /* (between 0 and 7) in buffer */
- uint byte_count;
- byte gif_buffer[260];
- } lzw_encoder;
-
- /* Initialize LZW encoder */
- private void lzw_set_bits(P2(register lzw_encoder _ss *, int));
- private void lzw_reset(P1(register lzw_encoder _ss *));
- private int
- lzw_init(register lzw_encoder _ss *pe, int bits, FILE *file)
- { lzw_set_bits(pe, bits);
- pe->Clear_code = (1 << bits);
- pe->file = file;
- pe->byte_count = 1;
- pe->output_bit_count = 0;
- pe->output_bit_buffer = 0;
- pe->table = (code_entry *)gs_malloc(TABLE_SIZE, sizeof(code_entry), "GIF code table");
-
- if ( pe->table == 0 )
- return_error(gs_error_VMerror); /* can't allocate buffers */
-
- lzw_reset(pe);
- pe->string_code = 0;
- return 0;
- }
- /* Establish the width of the code in bits */
- private void
- lzw_set_bits(register lzw_encoder _ss *pe, int bits)
- { pe->bits = bits;
- pe->Max_Code = (1 << (bits+1)) - 1;
- }
- /* Reset the encoding table */
- private void
- lzw_reset(register lzw_encoder _ss *pe)
- { int index;
- for ( index = 0; index < TABLE_SIZE; index++ )
- pe->table[index].code_value = -1;
- pe->next_code = pe->Clear_code + 2;
- }
-
- /* Put out (data) of length (bits) to GIF buffer */
- private void
- lzw_putc(register lzw_encoder _ss *pe, uint data)
- { int bits = pe->bits + 1; /* output width */
- ulong buffer = pe->output_bit_buffer | ((ulong)data << pe->output_bit_count);
- pe->output_bit_count += bits;
- while ( pe->output_bit_count >= 8 )
- { /* putc(output_bit_buffer >> 24, file); */
- pe->gif_buffer[pe->byte_count] = (byte)buffer; /* low byte */
- buffer >>= 8;
- pe->output_bit_count -= 8;
- pe->byte_count++;
- if ( pe->byte_count == 256 )
- { pe->byte_count = 1;
- pe->gif_buffer[0] = 255; /* byte count for block */
- fwrite(pe->gif_buffer, 1, 256, pe->file);
- }
- }
- pe->output_bit_buffer = (byte)buffer;
- }
-
- /* Finish encoding, and flush the buffers. */
- private void
- lzw_finish(register lzw_encoder _ss *pe)
- { lzw_putc(pe, pe->string_code); /* output last code */
- lzw_putc(pe, pe->Clear_code+1); /* output eof code */
- lzw_putc(pe, 0); /* force out last code */
- if ( pe->byte_count != 1 )
- { pe->gif_buffer[0] = pe->byte_count;
- fwrite(pe->gif_buffer, 1, pe->byte_count+1, pe->file);
- }
- }
-
- /* Terminate LZW encoder. */
- private void
- lzw_exit(register lzw_encoder _ss *pe)
- { gs_free((char *)pe->table, TABLE_SIZE, sizeof(code_entry), "GIF code table");
- }
-
- /* Get the next (depth) bits from the pixel buffer. */
- /* Note that 8 % depth == 0. */
- /* Free variables: bits_left, bit_buffer, next, depth, depth_mask. */
- #define lzw_getc()\
- (bits_left =\
- (bits_left == 0 ?\
- (bit_buffer = *(next++), 8 - (depth)) :\
- bits_left - (depth)),\
- (bit_buffer >> bits_left) & (depth_mask))
-
- /* Output 1 row of data in GIF (LZW) format. */
- private void
- lzw(byte *from, byte *end, register lzw_encoder _ss *pe, int depth)
- { int bits_left = 0;
- uint bit_buffer;
- byte *next = from;
- uint depth_mask = (1 << depth) - 1;
-
- if ( pe->next_code == (pe->Clear_code + 2)) /* first time through */
- { pe->string_code = lzw_getc();
- }
-
- while ( next < end || bits_left >= depth )
- { uint data = lzw_getc(); /* actually only a byte */
-
- /* Hash to find a match for the prefix+char */
- /* string in the string table */
-
- ushort hash_prefix = pe->string_code;
- int index = (data << 4) ^ hash_prefix;
- int hash_offset;
- register code_entry *pce;
-
- index += index >> TABLE_HASH_SHIFT;
- if ( index == 0 )
- hash_offset = 1;
- else
- hash_offset = TABLE_SIZE - index;
-
- while ( 1 )
- { pce = &pe->table[index];
- if ( pce->code_value == -1 )
- break;
- if ( pce->prefix_code == hash_prefix &&
- pce->append_character == data )
- break;
- index -= hash_offset;
- if ( index < 0 )
- index += TABLE_SIZE;
- }
- if ( pce->code_value != -1 )
- pe->string_code = pce->code_value;
- else
- { /* Make a new entry */
- pce->code_value = pe->next_code++;
- pce->prefix_code = pe->string_code;
- pce->append_character = data;
-
- lzw_putc(pe, pe->string_code);
-
- if ( pe->next_code > (pe->Max_Code + 1) )
- { /* Increment the width of the code */
- if ( pe->bits+1 >= MAX_BITS )
- { /* output clear code first*/
- lzw_putc(pe, pe->Clear_code);
- pe->bits = (depth == 1 ? 2 : depth);
- lzw_reset(pe);
- }
- else
- pe->bits++;
- lzw_set_bits(pe, pe->bits);
- }
- pe->string_code = data;
- }
- }
- }
-
-
- /* Open the device. The only reason for this routine is */
- /* to print the obnoxious copyright notice. */
- private int
- gif_open(gx_device *pdev)
- { int code = gdev_prn_open(pdev);
- if ( code < 0 ) return code;
-
- /* Put the message on stderr so it doesn't interfere with */
- /* possible piped output. */
- fprintf(stderr, "The Graphics Interchange Format(c) is\n");
- fprintf(stderr, "the Copyright Property of CompuServe Incorporated.\n");
- fprintf(stderr, "GIF(sm) is a Service Mark property of CompuServe Incorporated.\n");
-
- return 0;
- }
-
-
- /* Write a page to a file in GIF format. */
- private int
- gif_print_page(gx_device_printer *pdev, FILE *file)
- { int raster = gdev_prn_raster(pdev);
- ushort height = pdev->height;
- int depth = pdev->color_info.depth;
- ushort gif_width = raster * (8 / depth); /* decoders want the width */
- /* on a byte boundary */
- byte *row = (byte *)gs_malloc(raster * 2, 1, "gif file buffer");
- byte *end = row + raster;
- gif_header header;
- image_descriptor header_desc;
- lzw_encoder encoder;
- int y;
- int code = 0; /* return code */
-
- if ( row == 0 ) /* can't allocate row buffer */
- return_error(gs_error_VMerror);
- code = lzw_init(&encoder, (depth == 1 ? 2 : depth), file);
- if ( code < 0 )
- return code;
-
- /* Set up the header. */
-
- memcpy(header.signature, "GIF", 3);
- memcpy(header.version, "87a", 3);
- assign_ushort(header.width, gif_width);
- assign_ushort(header.height, height);
- /* header.flags.globalcolor = TRUE; */
- /* header.flags.colorres = depth-1; */
- /* header.flags.sort = FALSE; */
- /* header.flags.colorsize = depth-1; */
- header.flags =
- (1 << globalcolor_shift) +
- ((depth - 1) << colorres_shift) +
- (0 << sort_shift) +
- ((depth - 1) << colorsize_shift);
- header.background = 0;
- header.aspect = 0;
-
- /* Write the header, on the first page only. */
-
- if ( gdev_prn_file_is_new(pdev) )
- {
- if ( fwrite(&header, 1, 13, file) < 13 )
- { code = gs_error_ioerror;
- goto gif_done;
- }
-
- /* Write the header global color palette. */
-
- if ( pc_write_palette((gx_device *)pdev, 1 << depth, file) < 0 )
- { code = gs_error_ioerror;
- goto gif_done;
- }
- }
-
- header_desc.left_pos = 0;
- header_desc.top_pos = 0;
- assign_ushort(header_desc.width, gif_width);
- assign_ushort(header_desc.height, height);
- /* header_desc.flags.localcolor = TRUE; */
- /* header_desc.flags.interlace = FALSE; */
- /* header_desc.flags.sort = FALSE; */
- /* header_desc.flags.localsize = depth - 1;*/
-
- header_desc.flags =
- (1 << globalcolor_shift) +
- ((depth - 1) << colorsize_shift);
-
- /* Write the header image descriptor. */
-
- fputc(0x2c,file); /* start with separator */
- if ( fwrite(&header_desc, 1, 9, file) < 9 )
- { code = gs_error_ioerror;
- goto gif_done;
- }
-
- /* Write the local color palette. */
-
- if ( pc_write_palette((gx_device *)pdev, 1 << depth, file) < 0 )
- { code = gs_error_ioerror;
- goto gif_done;
- }
-
- fputc(encoder.bits, file); /* start with code size */
-
- lzw_putc(&encoder, encoder.Clear_code); /* output clear code first*/
-
- /* Dump the contents of the image. */
- for ( y = 0; y < height; y++ )
- { gdev_prn_copy_scan_lines(pdev, y, row, raster);
- lzw(row, end, &encoder, depth);
- }
-
- lzw_finish(&encoder);
- fputc(0, file);
-
- gif_done:
- lzw_exit(&encoder);
- gs_free((char *)row, raster * 2, 1, "gif file buffer");
- return code;
- }
-
- /* Close the device, writing an end-of-file mark. */
- private int
- gif_close(gx_device *pdev)
- {
- FILE *file = ((gx_device_printer *)pdev)->file;
-
- if ( file != NULL )
- { fputc(0x3b, file); /* EOF indicator */
- }
-
- return gdev_prn_close(pdev);
- }
-