summaryrefslogtreecommitdiff
path: root/filter/rle.c
diff options
context:
space:
mode:
Diffstat (limited to 'filter/rle.c')
-rw-r--r--filter/rle.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/filter/rle.c b/filter/rle.c
new file mode 100644
index 00000000..0e611918
--- /dev/null
+++ b/filter/rle.c
@@ -0,0 +1,238 @@
+#include <fitz.h>
+
+/* TODO: rewrite!
+ * make it non-optimal or something,
+ * just not this horrid mess...
+ */
+
+#define noDEBUG
+
+typedef struct fz_rle_s fz_rle;
+
+struct fz_rle_s
+{
+ fz_filter super;
+ int reclen;
+ int curlen;
+ int state;
+ int run;
+ unsigned char buf[128];
+};
+
+enum {
+ ZERO,
+ ONE,
+ DIFF,
+ SAME,
+ END
+};
+
+fz_error *
+fz_newrle(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_rle, enc, rle);
+
+ if (params)
+ enc->reclen = fz_toint(params);
+ else
+ enc->reclen = 0;
+
+ enc->curlen = 0;
+ enc->state = ZERO;
+ enc->run = 0;
+
+ return nil;
+}
+
+void
+fz_freerle(fz_filter *enc)
+{
+ fz_free(enc);
+}
+
+static fz_error *
+putone(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ if (out->wp + 2 >= out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+fprintf(stderr, "one '%c'\n", enc->buf[0]);
+#endif
+
+ *out->wp++ = 0;
+ *out->wp++ = enc->buf[0];
+
+ return nil;
+}
+
+static fz_error *
+putsame(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ if (out->wp + enc->run >= out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+fprintf(stderr, "same %d x '%c'\n", enc->run, enc->buf[0]);
+#endif
+
+ *out->wp++ = 257 - enc->run;
+ *out->wp++ = enc->buf[0];
+ return nil;
+}
+
+static fz_error *
+putdiff(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ int i;
+ if (out->wp + enc->run >= out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+fprintf(stderr, "diff %d\n", enc->run);
+#endif
+
+ *out->wp++ = enc->run - 1;
+ for (i = 0; i < enc->run; i++)
+ *out->wp++ = enc->buf[i];
+ return nil;
+}
+
+static fz_error *
+puteod(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ if (out->wp + 1 >= out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+fprintf(stderr, "eod\n");
+#endif
+
+ *out->wp++ = 128;
+ return nil;
+}
+
+static fz_error *
+savebuf(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ switch (enc->state)
+ {
+ case ZERO: return nil;
+ case ONE: return putone(enc, in, out);
+ case SAME: return putsame(enc, in, out);
+ case DIFF: return putdiff(enc, in, out);
+ case END: return puteod(enc, in, out);
+ default: assert(!"invalid state in rle"); return nil;
+ }
+}
+
+fz_error *
+fz_processrle(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_rle *enc = (fz_rle*)filter;
+ fz_error *err;
+ unsigned char c;
+
+ while (1)
+ {
+
+ if (enc->reclen && enc->curlen == enc->reclen) {
+ err = savebuf(enc, in, out);
+ if (err) return err;
+#ifdef DEBUG
+fprintf(stderr, "--record--\n");
+#endif
+ enc->state = ZERO;
+ enc->curlen = 0;
+ }
+
+ if (in->rp == in->wp) {
+ if (in->eof) {
+ if (enc->state != END) {
+ err = savebuf(enc, in, out);
+ if (err) return err;
+ }
+ enc->state = END;
+ }
+ else
+ return fz_ioneedin;
+ }
+
+ c = *in->rp;
+
+ switch (enc->state)
+ {
+ case ZERO:
+ enc->state = ONE;
+ enc->run = 1;
+ enc->buf[0] = c;
+ break;
+
+ case ONE:
+ enc->state = DIFF;
+ enc->run = 2;
+ enc->buf[1] = c;
+ break;
+
+ case DIFF:
+ /* out of space */
+ if (enc->run == 128) {
+ err = putdiff(enc, in, out);
+ if (err) return err;
+
+ enc->state = ONE;
+ enc->run = 1;
+ enc->buf[0] = c;
+ }
+
+ /* run of three that are the same */
+ else if ((enc->run > 1) &&
+ (c == enc->buf[enc->run - 1]) &&
+ (c == enc->buf[enc->run - 2]))
+ {
+ if (enc->run >= 3) {
+ enc->run -= 2; /* skip prev two for diff run */
+ err = putdiff(enc, in, out);
+ if (err) return err;
+ }
+
+ enc->state = SAME;
+ enc->run = 3;
+ enc->buf[0] = c;
+ }
+
+ /* keep on collecting */
+ else {
+ enc->buf[enc->run++] = c;
+ }
+ break;
+
+ case SAME:
+ if (enc->run == 128 || c != enc->buf[0]) {
+ err = putsame(enc, in, out);
+ if (err) return err;
+
+ enc->state = ONE;
+ enc->run = 1;
+ enc->buf[0] = c;
+ }
+ else {
+ enc->run ++;
+ }
+ break;
+
+ case END:
+ err = puteod(enc, in, out);
+ if (err) return err;
+
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ in->rp ++;
+
+ enc->curlen ++;
+
+ }
+}
+