summaryrefslogtreecommitdiff
path: root/filter/lzwd.c
diff options
context:
space:
mode:
Diffstat (limited to 'filter/lzwd.c')
-rw-r--r--filter/lzwd.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/filter/lzwd.c b/filter/lzwd.c
new file mode 100644
index 00000000..1ab47357
--- /dev/null
+++ b/filter/lzwd.c
@@ -0,0 +1,263 @@
+#include <fitz.h>
+
+/* TODO: error checking */
+
+#define noDEBUG 1
+
+#ifdef DEBUG
+#define DPRINT(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DPRINT(...)
+#endif
+
+enum
+{
+ MINBITS = 9,
+ MAXBITS = 12,
+ NUMCODES = (1 << MAXBITS),
+ LZW_CLEAR = 256,
+ LZW_EOD = 257,
+ LZW_FIRST = 258,
+};
+
+typedef struct lzw_code_s lzw_code;
+
+struct lzw_code_s
+{
+ int prev; /* prev code (in string) */
+ unsigned short length; /* string len, including this token */
+ unsigned char value; /* data value */
+ unsigned char firstchar; /* first token of string */
+};
+
+typedef struct fz_lzwd_s fz_lzwd;
+
+struct fz_lzwd_s
+{
+ fz_filter super;
+
+ int earlychange;
+
+ unsigned int word; /* bits loaded from data */
+ int bidx;
+
+ int resume; /* resume output of code from needout */
+ int codebits; /* num bits/code */
+ int code; /* current code */
+ int oldcode; /* previously recognized code */
+ int nextcode; /* next free entry */
+ lzw_code table[NUMCODES];
+};
+
+fz_error *
+fz_newlzwd(fz_filter **fp, fz_obj *params)
+{
+ int i;
+
+ FZ_NEWFILTER(fz_lzwd, lzw, lzwd);
+
+ lzw->earlychange = 0;
+
+ if (params) {
+ fz_obj *obj;
+ obj = fz_dictgets(params, "EarlyChange");
+ if (obj) lzw->earlychange = fz_toint(obj) != 0;
+ }
+
+ lzw->bidx = 32;
+ lzw->word = 0;
+
+ for (i = 0; i < 256; i++) {
+ lzw->table[i].value = i;
+ lzw->table[i].firstchar = i;
+ lzw->table[i].length = 1;
+ lzw->table[i].prev = -1;
+ }
+
+ for (i = LZW_FIRST; i < NUMCODES; i++) {
+ lzw->table[i].value = 0;
+ lzw->table[i].firstchar = 0;
+ lzw->table[i].length = 0;
+ lzw->table[i].prev = -1;
+ }
+
+ lzw->codebits = MINBITS;
+ lzw->code = -1;
+ lzw->nextcode = LZW_FIRST;
+ lzw->oldcode = -1;
+ lzw->resume = 0;
+
+ return nil;
+}
+
+void
+fz_freelzwd(fz_filter *filter)
+{
+ fz_free(filter);
+}
+
+static inline void eatbits(fz_lzwd *lzw, int nbits)
+{
+ lzw->word <<= nbits;
+ lzw->bidx += nbits;
+}
+
+static inline fz_error * fillbits(fz_lzwd *lzw, fz_buffer *in)
+{
+ while (lzw->bidx >= 8)
+ {
+ if (in->rp + 1 > in->wp)
+ return fz_ioneedin;
+ lzw->bidx -= 8;
+ lzw->word |= *in->rp << lzw->bidx;
+ in->rp ++;
+ }
+ return nil;
+}
+
+fz_error *
+fz_processlzwd(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_lzwd *lzw = (fz_lzwd*)filter;
+ unsigned char *s;
+ int len;
+
+ if (lzw->resume) {
+ lzw->resume = 0;
+ goto output;
+ }
+
+ while (1)
+ {
+ if (fillbits(lzw, in)) {
+ DPRINT("FILL[eof=%d] bidx=%d word=%08x\n", in->eof, lzw->bidx, lzw->word);
+ if (in->eof) {
+ if (lzw->bidx > 32 - lzw->codebits) {
+ out->eof = 1;
+ return fz_iodone;
+ }
+ }
+ else {
+ return fz_ioneedin;
+ }
+ }
+
+ lzw->code = lzw->word >> (32 - lzw->codebits);
+
+ DPRINT("CODE[%d]: %03x (%d)\n", lzw->codebits, lzw->code, lzw->code);
+
+ if (lzw->code == LZW_EOD)
+ {
+ DPRINT("-> LZW_EOD\n");
+
+ eatbits(lzw, lzw->codebits);
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ if (lzw->code == LZW_CLEAR)
+ {
+ DPRINT("-> LZW_CLEAR\n");
+
+ int oldcodebits = lzw->codebits;
+
+ lzw->codebits = MINBITS;
+ lzw->nextcode = LZW_FIRST;
+
+ lzw->code = lzw->word >> (32 - oldcodebits - MINBITS) & ((1 << MINBITS) - 1);
+
+ DPRINT("CODE[%d]: %03x (%d) [after CLEAR]\n", lzw->codebits, lzw->code, lzw->code);
+
+ if (lzw->code == LZW_EOD) {
+ DPRINT("-> LZW_EOD\n");
+
+ eatbits(lzw, oldcodebits + MINBITS);
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ DPRINT("-> %d\n", lzw->code);
+
+ if (out->wp + 1 > out->ep)
+ return fz_ioneedout;
+
+ *out->wp++ = lzw->code;
+
+ lzw->oldcode = lzw->code;
+
+ eatbits(lzw, oldcodebits + MINBITS);
+
+ continue;
+ }
+
+ eatbits(lzw, lzw->codebits);
+
+ /* if stream starts without a clear code, oldcode is undefined... */
+ if (lzw->oldcode == -1) {
+ lzw->oldcode = lzw->code;
+ goto output;
+ }
+
+ /* add new entry to the code table */
+ lzw->table[lzw->nextcode].prev = lzw->oldcode;
+ lzw->table[lzw->nextcode].firstchar = lzw->table[lzw->oldcode].firstchar;
+ lzw->table[lzw->nextcode].length = lzw->table[lzw->oldcode].length + 1;
+ if (lzw->code < lzw->nextcode)
+ lzw->table[lzw->nextcode].value = lzw->table[lzw->code].firstchar;
+ else
+ lzw->table[lzw->nextcode].value = lzw->table[lzw->nextcode].firstchar;
+
+ DPRINT("NEW[%d] prev=%d, f=%d, l=%d, v=%d\n",
+ lzw->nextcode,
+ lzw->table[lzw->nextcode].prev,
+ lzw->table[lzw->nextcode].firstchar,
+ lzw->table[lzw->nextcode].length,
+ lzw->table[lzw->nextcode].value);
+
+ lzw->nextcode ++;
+
+ if (lzw->nextcode >= (1 << lzw->codebits) - lzw->earlychange - 1)
+ {
+ lzw->codebits ++;
+ if (lzw->codebits > MAXBITS)
+ lzw->codebits = MAXBITS; /* FIXME */
+ }
+
+ lzw->oldcode = lzw->code;
+
+output:
+ /* code maps to a string, copy to output (in reverse...) */
+ if (lzw->code > 255)
+ {
+ if (out->wp + lzw->table[lzw->code].length > out->ep) {
+ lzw->resume = 1;
+ return fz_ioneedout;
+ }
+
+ len = lzw->table[lzw->code].length;
+ s = out->wp + len;
+
+ DPRINT("OUT code=%d len=%d\n", lzw->code, len);
+ do {
+ DPRINT("-> %d\n", lzw->table[lzw->code].value);
+ *(--s) = lzw->table[lzw->code].value;
+ lzw->code = lzw->table[lzw->code].prev;
+ } while (lzw->code >= 0 && s > out->wp);
+ out->wp += len;
+ }
+
+ /* ... or just a single character */
+ else
+ {
+ if (out->wp + 1 > out->ep) {
+ lzw->resume = 1;
+ return fz_ioneedout;
+ }
+
+ DPRINT("OUT code=%d\n-> %d\n", lzw->code, lzw->code);
+
+ *out->wp++ = lzw->code;
+ }
+ }
+}
+