summaryrefslogtreecommitdiff
path: root/src/cairo-lzw.c
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2006-03-15 10:44:09 -0800
committerCarl Worth <cworth@cworth.org>2006-03-15 10:47:10 -0800
commita7f58f886253f4ae66c9b0ed4472f575eaf2ec1d (patch)
tree067b1779a64e3432521eafa6d86fc662428441a7 /src/cairo-lzw.c
parentd6f8df7417bdab75dbcfe9658616dd3367276ed0 (diff)
Add LZW image compression and ASCII85 encoding for PostScript image output.
* src/Makefile.am: Add cairo-lzw.c to sources Remove erronous space at start of line * src/cairo-lzw.c: New file implementing _cairo_compress_lzw which does Lempel-Ziv & Welch compression as used by postscript. Based on code from libtiff. * src/cairo-output-stream.c: Add _cairo_output_stream_write_base85_string which implements an encoder for the ASCII85Decode postscript filter. * src/cairoint.h: Add _cairo_compress_lzw and _cairo_output_stream_write_base85_string. * src/cairo-ps-surface.c: Write Images LZW + Base85 encoded. Change ps level to 2 (we only needed level 3 for FlateDecode). Change DocumentData to Clean7Bit as we don't include binary data anymore.
Diffstat (limited to 'src/cairo-lzw.c')
-rw-r--r--src/cairo-lzw.c499
1 files changed, 499 insertions, 0 deletions
diff --git a/src/cairo-lzw.c b/src/cairo-lzw.c
new file mode 100644
index 00000000..bb764f3a
--- /dev/null
+++ b/src/cairo-lzw.c
@@ -0,0 +1,499 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ * Alexander Larsson <alexl@redhat.com>
+ *
+ * This code is derived from tif_lzw.c in libtiff 3.8.0.
+ * The original copyright notice appears below in its entirety.
+ */
+
+#include "cairoint.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * TIFF Library.
+ * Rev 5.0 Lempel-Ziv & Welch Compression Support
+ *
+ * This code is derived from the compress program whose code is
+ * derived from software contributed to Berkeley by James A. Woods,
+ * derived from original work by Spencer Thomas and Joseph Orost.
+ *
+ * The original Berkeley copyright notice appears below in its entirety.
+ */
+
+#define MAXCODE(n) ((1L<<(n))-1)
+/*
+ * The TIFF spec specifies that encoded bit
+ * strings range from 9 to 12 bits.
+ */
+#define BITS_MIN 9 /* start with 9 bits */
+#define BITS_MAX 12 /* max of 12 bit strings */
+/* predefined codes */
+#define CODE_CLEAR 256 /* code to clear string table */
+#define CODE_EOI 257 /* end-of-information code */
+#define CODE_FIRST 258 /* first free code entry */
+#define CODE_MAX MAXCODE(BITS_MAX)
+#define HSIZE 9001L /* 91% occupancy */
+#define HSHIFT (13-8)
+#ifdef LZW_COMPAT
+/* NB: +1024 is for compatibility with old files */
+#define CSIZE (MAXCODE(BITS_MAX)+1024L)
+#else
+#define CSIZE (MAXCODE(BITS_MAX)+1L)
+#endif
+
+typedef uint16_t hcode_t; /* codes fit in 16 bits */
+typedef struct {
+ long hash;
+ hcode_t code;
+} hash_t;
+
+typedef struct {
+ /* Out buffer */
+ unsigned char *out_buffer; /* compressed out buffer */
+ size_t out_buffer_size; /* # of allocated bytes in out buffer */
+ unsigned char *out_buffer_pos; /* current spot in out buffer */
+ size_t out_buffer_bytes; /* # of data bytes in out buffer */
+ unsigned char *out_buffer_end; /* bound on out_buffer */
+
+ unsigned short nbits; /* # of bits/code */
+ unsigned short maxcode; /* maximum code for lzw_nbits */
+ unsigned short free_ent; /* next free entry in hash table */
+ long nextdata; /* next bits of i/o */
+ long nextbits; /* # of valid bits in lzw_nextdata */
+
+ int enc_oldcode; /* last code encountered */
+ long enc_checkpoint; /* point at which to clear table */
+#define CHECK_GAP 10000 /* enc_ratio check interval */
+ long enc_ratio; /* current compression ratio */
+ long enc_incount; /* (input) data bytes encoded */
+ long enc_outcount; /* encoded (output) bytes */
+ hash_t* enc_hashtab; /* kept separate for small machines */
+} LZWCodecState;
+
+static void cl_hash(LZWCodecState*);
+
+/*
+ * LZW Encoding.
+ */
+
+static unsigned char *
+grow_out_buffer (LZWCodecState *sp, unsigned char *op)
+{
+ size_t cc;
+
+ cc = (size_t)(op - sp->out_buffer);
+
+ sp->out_buffer_size = sp->out_buffer_size * 2;
+ sp->out_buffer = realloc (sp->out_buffer, sp->out_buffer_size);
+ /*
+ * The 4 here insures there is space for 2 max-sized
+ * codes in LZWEncode and LZWPostDecode.
+ */
+ sp->out_buffer_end = sp->out_buffer + sp->out_buffer_size-1 - 4;
+
+ return sp->out_buffer + cc;
+}
+
+static int
+LZWSetupEncode (LZWCodecState* sp)
+{
+ memset (sp, 0, sizeof (LZWCodecState));
+ sp->enc_hashtab = (hash_t*) malloc (HSIZE * sizeof (hash_t));
+ if (sp->enc_hashtab == NULL)
+ return 0;
+ return 1;
+}
+
+static void
+LZWFreeEncode (LZWCodecState* sp)
+{
+ if (sp->enc_hashtab)
+ free (sp->enc_hashtab);
+}
+
+
+/*
+ * Reset encoding state at the start of a strip.
+ */
+static void
+LZWPreEncode (LZWCodecState *sp)
+{
+ sp->nbits = BITS_MIN;
+ sp->maxcode = MAXCODE(BITS_MIN);
+ sp->free_ent = CODE_FIRST;
+ sp->nextbits = 0;
+ sp->nextdata = 0;
+ sp->enc_checkpoint = CHECK_GAP;
+ sp->enc_ratio = 0;
+ sp->enc_incount = 0;
+ sp->enc_outcount = 0;
+ /*
+ * The 4 here insures there is space for 2 max-sized
+ * codes in LZWEncode and LZWPostDecode.
+ */
+ sp->out_buffer_end = sp->out_buffer + sp->out_buffer_size-1 - 4;
+ cl_hash(sp); /* clear hash table */
+ sp->enc_oldcode = (hcode_t) -1; /* generates CODE_CLEAR in LZWEncode */
+}
+
+#define CALCRATIO(sp, rat) { \
+ if (incount > 0x007fffff) { /* NB: shift will overflow */ \
+ rat = outcount >> 8; \
+ rat = (rat == 0 ? 0x7fffffff : incount/rat); \
+ } else \
+ rat = (incount << 8) / outcount; \
+}
+#define PutNextCode(op, c) { \
+ nextdata = (nextdata << nbits) | c; \
+ nextbits += nbits; \
+ *op++ = (unsigned char)(nextdata >> (nextbits-8)); \
+ nextbits -= 8; \
+ if (nextbits >= 8) { \
+ *op++ = (unsigned char)(nextdata >> (nextbits-8)); \
+ nextbits -= 8; \
+ } \
+ outcount += nbits; \
+}
+
+/*
+ * Encode a chunk of pixels.
+ *
+ * Uses an open addressing double hashing (no chaining) on the
+ * prefix code/next character combination. We do a variant of
+ * Knuth's algorithm D (vol. 3, sec. 6.4) along with G. Knott's
+ * relatively-prime secondary probe. Here, the modular division
+ * first probe is gives way to a faster exclusive-or manipulation.
+ * Also do block compression with an adaptive reset, whereby the
+ * code table is cleared when the compression ratio decreases,
+ * but after the table fills. The variable-length output codes
+ * are re-sized at this point, and a CODE_CLEAR is generated
+ * for the decoder.
+ */
+static int
+LZWEncode (LZWCodecState *sp,
+ unsigned char *bp,
+ size_t cc)
+{
+ register long fcode;
+ register hash_t *hp;
+ register int h, c;
+ hcode_t ent;
+ long disp;
+ long incount, outcount, checkpoint;
+ long nextdata, nextbits;
+ int free_ent, maxcode, nbits;
+ unsigned char *op;
+
+ /*
+ * Load local state.
+ */
+ incount = sp->enc_incount;
+ outcount = sp->enc_outcount;
+ checkpoint = sp->enc_checkpoint;
+ nextdata = sp->nextdata;
+ nextbits = sp->nextbits;
+ free_ent = sp->free_ent;
+ maxcode = sp->maxcode;
+ nbits = sp->nbits;
+ op = sp->out_buffer_pos;
+ ent = sp->enc_oldcode;
+
+ if (ent == (hcode_t) -1 && cc > 0) {
+ /*
+ * NB: This is safe because it can only happen
+ * at the start of a strip where we know there
+ * is space in the data buffer.
+ */
+ PutNextCode(op, CODE_CLEAR);
+ ent = *bp++; cc--; incount++;
+ }
+ while (cc > 0) {
+ c = *bp++; cc--; incount++;
+ fcode = ((long)c << BITS_MAX) + ent;
+ h = (c << HSHIFT) ^ ent; /* xor hashing */
+#ifdef _WINDOWS
+ /*
+ * Check hash index for an overflow.
+ */
+ if (h >= HSIZE)
+ h -= HSIZE;
+#endif
+ hp = &sp->enc_hashtab[h];
+ if (hp->hash == fcode) {
+ ent = hp->code;
+ continue;
+ }
+ if (hp->hash >= 0) {
+ /*
+ * Primary hash failed, check secondary hash.
+ */
+ disp = HSIZE - h;
+ if (h == 0)
+ disp = 1;
+ do {
+ /*
+ * Avoid pointer arithmetic 'cuz of
+ * wraparound problems with segments.
+ */
+ if ((h -= disp) < 0)
+ h += HSIZE;
+ hp = &sp->enc_hashtab[h];
+ if (hp->hash == fcode) {
+ ent = hp->code;
+ goto hit;
+ }
+ } while (hp->hash >= 0);
+ }
+ /*
+ * New entry, emit code and add to table.
+ */
+ /*
+ * Verify there is space in the buffer for the code
+ * and any potential Clear code that might be emitted
+ * below. The value of limit is setup so that there
+ * are at least 4 bytes free--room for 2 codes.
+ */
+ if (op > sp->out_buffer_end) {
+ op = grow_out_buffer (sp, op);
+ if (sp->out_buffer == NULL) {
+ return 0;
+ }
+ }
+ PutNextCode(op, ent);
+ ent = c;
+ hp->code = free_ent++;
+ hp->hash = fcode;
+ if (free_ent == CODE_MAX-1) {
+ /* table is full, emit clear code and reset */
+ cl_hash(sp);
+ sp->enc_ratio = 0;
+ incount = 0;
+ outcount = 0;
+ free_ent = CODE_FIRST;
+ PutNextCode(op, CODE_CLEAR);
+ nbits = BITS_MIN;
+ maxcode = MAXCODE(BITS_MIN);
+ } else {
+ /*
+ * If the next entry is going to be too big for
+ * the code size, then increase it, if possible.
+ */
+ if (free_ent > maxcode) {
+ nbits++;
+ assert(nbits <= BITS_MAX);
+ maxcode = (int) MAXCODE(nbits);
+ } else if (incount >= checkpoint) {
+ long rat;
+ /*
+ * Check compression ratio and, if things seem
+ * to be slipping, clear the hash table and
+ * reset state. The compression ratio is a
+ * 24+8-bit fractional number.
+ */
+ checkpoint = incount+CHECK_GAP;
+ CALCRATIO(sp, rat);
+ if (rat <= sp->enc_ratio) {
+ cl_hash(sp);
+ sp->enc_ratio = 0;
+ incount = 0;
+ outcount = 0;
+ free_ent = CODE_FIRST;
+ PutNextCode(op, CODE_CLEAR);
+ nbits = BITS_MIN;
+ maxcode = MAXCODE(BITS_MIN);
+ } else
+ sp->enc_ratio = rat;
+ }
+ }
+ hit:
+ ;
+ }
+
+ /*
+ * Restore global state.
+ */
+ sp->enc_incount = incount;
+ sp->enc_outcount = outcount;
+ sp->enc_checkpoint = checkpoint;
+ sp->enc_oldcode = ent;
+ sp->nextdata = nextdata;
+ sp->nextbits = nextbits;
+ sp->free_ent = free_ent;
+ sp->maxcode = maxcode;
+ sp->nbits = nbits;
+ sp->out_buffer_pos = op;
+ return 1;
+}
+
+/*
+ * Finish off an encoded strip by flushing the last
+ * string and tacking on an End Of Information code.
+ */
+static int
+LZWPostEncode (LZWCodecState *sp)
+{
+ unsigned char *op = sp->out_buffer_pos;
+ long nextbits = sp->nextbits;
+ long nextdata = sp->nextdata;
+ long outcount = sp->enc_outcount;
+ int nbits = sp->nbits;
+
+ if (op > sp->out_buffer_end) {
+ op = grow_out_buffer (sp, op);
+ if (sp->out_buffer == NULL) {
+ return 0;
+ }
+ }
+ if (sp->enc_oldcode != (hcode_t) -1) {
+ PutNextCode(op, sp->enc_oldcode);
+ sp->enc_oldcode = (hcode_t) -1;
+ }
+ PutNextCode(op, CODE_EOI);
+ if (nextbits > 0)
+ *op++ = (unsigned char)(nextdata << (8-nextbits));
+ sp->out_buffer_bytes = (size_t)(op - sp->out_buffer);
+ return 1;
+}
+
+/*
+ * Reset encoding hash table.
+ */
+static void
+cl_hash (LZWCodecState* sp)
+{
+ register hash_t *hp = &sp->enc_hashtab[HSIZE-1];
+ register long i = HSIZE-8;
+
+ do {
+ i -= 8;
+ hp[-7].hash = -1;
+ hp[-6].hash = -1;
+ hp[-5].hash = -1;
+ hp[-4].hash = -1;
+ hp[-3].hash = -1;
+ hp[-2].hash = -1;
+ hp[-1].hash = -1;
+ hp[ 0].hash = -1;
+ hp -= 8;
+ } while (i >= 0);
+ for (i += 8; i > 0; i--, hp--)
+ hp->hash = -1;
+}
+
+/*
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+void *
+_cairo_compress_lzw (void *data, unsigned long data_size, unsigned long *compressed_size)
+{
+ LZWCodecState state;
+
+ if (!LZWSetupEncode (&state))
+ goto bail0;
+
+ state.out_buffer_size = data_size/4;
+ /* We need *some* space at least */
+ if (state.out_buffer_size < 256)
+ state.out_buffer_size = 256;
+ state.out_buffer = malloc (state.out_buffer_size);
+ if (state.out_buffer == NULL)
+ goto bail1;
+
+ state.out_buffer_pos = state.out_buffer;
+ state.out_buffer_bytes = 0;
+
+ LZWPreEncode (&state);
+ if (!LZWEncode (&state, data, data_size))
+ goto bail2;
+ if (!LZWPostEncode(&state))
+ goto bail2;
+
+ LZWFreeEncode(&state);
+
+ *compressed_size = state.out_buffer_bytes;
+ return state.out_buffer;
+
+ bail2:
+ if (state.out_buffer)
+ free (state.out_buffer);
+ bail1:
+ LZWFreeEncode(&state);
+ bail0:
+ return NULL;
+}