xref: /plan9-contrib/sys/src/cmd/gs/src/srle.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
17dd7cddfSDavid du Colombier /* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises.  All rights reserved.
27dd7cddfSDavid du Colombier 
3*593dc095SDavid du Colombier   This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier   implied.
57dd7cddfSDavid du Colombier 
6*593dc095SDavid du Colombier   This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier   modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier   of the license contained in the file LICENSE in this distribution.
97dd7cddfSDavid du Colombier 
10*593dc095SDavid du Colombier   For more information about licensing, please refer to
11*593dc095SDavid du Colombier   http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier   commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
157dd7cddfSDavid du Colombier */
167dd7cddfSDavid du Colombier 
17*593dc095SDavid du Colombier /* $Id: srle.c,v 1.4 2002/02/21 22:24:54 giles Exp $ */
187dd7cddfSDavid du Colombier /* RunLengthEncode filter */
197dd7cddfSDavid du Colombier #include "stdio_.h"		/* includes std.h */
207dd7cddfSDavid du Colombier #include "memory_.h"
217dd7cddfSDavid du Colombier #include "strimpl.h"
227dd7cddfSDavid du Colombier #include "srlx.h"
237dd7cddfSDavid du Colombier 
247dd7cddfSDavid du Colombier /* ------ RunLengthEncode ------ */
257dd7cddfSDavid du Colombier 
267dd7cddfSDavid du Colombier private_st_RLE_state();
277dd7cddfSDavid du Colombier 
287dd7cddfSDavid du Colombier /* Set defaults */
297dd7cddfSDavid du Colombier private void
s_RLE_set_defaults(stream_state * st)307dd7cddfSDavid du Colombier s_RLE_set_defaults(stream_state * st)
317dd7cddfSDavid du Colombier {
327dd7cddfSDavid du Colombier     stream_RLE_state *const ss = (stream_RLE_state *) st;
337dd7cddfSDavid du Colombier 
347dd7cddfSDavid du Colombier     s_RLE_set_defaults_inline(ss);
357dd7cddfSDavid du Colombier }
367dd7cddfSDavid du Colombier 
377dd7cddfSDavid du Colombier /* Initialize */
387dd7cddfSDavid du Colombier private int
s_RLE_init(stream_state * st)397dd7cddfSDavid du Colombier s_RLE_init(stream_state * st)
407dd7cddfSDavid du Colombier {
417dd7cddfSDavid du Colombier     stream_RLE_state *const ss = (stream_RLE_state *) st;
427dd7cddfSDavid du Colombier 
437dd7cddfSDavid du Colombier     return s_RLE_init_inline(ss);
447dd7cddfSDavid du Colombier }
457dd7cddfSDavid du Colombier 
467dd7cddfSDavid du Colombier /* Process a buffer */
477dd7cddfSDavid du Colombier private int
s_RLE_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)487dd7cddfSDavid du Colombier s_RLE_process(stream_state * st, stream_cursor_read * pr,
497dd7cddfSDavid du Colombier 	      stream_cursor_write * pw, bool last)
507dd7cddfSDavid du Colombier {
517dd7cddfSDavid du Colombier     stream_RLE_state *const ss = (stream_RLE_state *) st;
527dd7cddfSDavid du Colombier     register const byte *p = pr->ptr;
537dd7cddfSDavid du Colombier     register byte *q = pw->ptr;
547dd7cddfSDavid du Colombier     const byte *rlimit = pr->limit;
557dd7cddfSDavid du Colombier     byte *wlimit = pw->limit;
567dd7cddfSDavid du Colombier     int status = 0;
577dd7cddfSDavid du Colombier     ulong rleft = ss->record_left;
587dd7cddfSDavid du Colombier 
597dd7cddfSDavid du Colombier     /*
607dd7cddfSDavid du Colombier      * We thought that the Genoa CET demands that the output from this
617dd7cddfSDavid du Colombier      * filter be not just legal, but optimal, so we went to the trouble
627dd7cddfSDavid du Colombier      * of ensuring this.  It turned out that this wasn't actually
637dd7cddfSDavid du Colombier      * necessary, but we didn't want to change the code back.
647dd7cddfSDavid du Colombier      *
657dd7cddfSDavid du Colombier      * For optimal output, we can't just break runs at buffer
667dd7cddfSDavid du Colombier      * boundaries: unless we hit a record boundary or the end of the
677dd7cddfSDavid du Colombier      * input, we have to look ahead far enough to know that we aren't
687dd7cddfSDavid du Colombier      * breaking a run prematurely.
697dd7cddfSDavid du Colombier      */
707dd7cddfSDavid du Colombier 
717dd7cddfSDavid du Colombier     /* Check for leftover output. */
727dd7cddfSDavid du Colombier copy:
737dd7cddfSDavid du Colombier     if (ss->copy_left) {
747dd7cddfSDavid du Colombier 	uint rcount = rlimit - p;
757dd7cddfSDavid du Colombier 	uint wcount = wlimit - q;
767dd7cddfSDavid du Colombier 	uint count = ss->copy_left;
777dd7cddfSDavid du Colombier 
787dd7cddfSDavid du Colombier 	if (rcount < count)
797dd7cddfSDavid du Colombier 	    count = rcount;
807dd7cddfSDavid du Colombier 	if (wcount < count)
817dd7cddfSDavid du Colombier 	    count = wcount;
827dd7cddfSDavid du Colombier 	if (rleft < count)
837dd7cddfSDavid du Colombier 	    count = rleft;
847dd7cddfSDavid du Colombier 	memcpy(q + 1, p + 1, count);
857dd7cddfSDavid du Colombier 	pr->ptr = p += count;
867dd7cddfSDavid du Colombier 	pw->ptr = q += count;
877dd7cddfSDavid du Colombier 	if ((ss->record_left = rleft -= count) == 0)
887dd7cddfSDavid du Colombier 	    ss->record_left = rleft = ss->record_size;
897dd7cddfSDavid du Colombier 	if ((ss->copy_left -= count) != 0)
907dd7cddfSDavid du Colombier 	    return (rcount == 0 ? 0 : 1);
917dd7cddfSDavid du Colombier     }
927dd7cddfSDavid du Colombier     while (p < rlimit) {
937dd7cddfSDavid du Colombier 	const byte *beg = p;
947dd7cddfSDavid du Colombier 	const byte *p1;
957dd7cddfSDavid du Colombier 	uint count = rlimit - p;
967dd7cddfSDavid du Colombier 	bool end = last;
977dd7cddfSDavid du Colombier 	byte next;
987dd7cddfSDavid du Colombier 
997dd7cddfSDavid du Colombier 	if (count > rleft)
1007dd7cddfSDavid du Colombier 	    count = rleft, end = true;
1017dd7cddfSDavid du Colombier 	if (count > 128)
1027dd7cddfSDavid du Colombier 	    count = 128, end = true;
1037dd7cddfSDavid du Colombier 	p1 = p + count - 1;
1047dd7cddfSDavid du Colombier 	if (count < 3) {
1057dd7cddfSDavid du Colombier 	    if (!end || count == 0)
1067dd7cddfSDavid du Colombier 		break;		/* can't look ahead far enough */
1077dd7cddfSDavid du Colombier 	    if (count == 1) {
1087dd7cddfSDavid du Colombier 		if (wlimit - q < 2) {
1097dd7cddfSDavid du Colombier 		    status = 1;
1107dd7cddfSDavid du Colombier 		    break;
1117dd7cddfSDavid du Colombier 		}
1127dd7cddfSDavid du Colombier 		*++q = 0;
1137dd7cddfSDavid du Colombier 	    } else {		/* count == 2 */
1147dd7cddfSDavid du Colombier 		if (p[1] == p[2]) {
1157dd7cddfSDavid du Colombier 		    if (wlimit - q < 2) {
1167dd7cddfSDavid du Colombier 			status = 1;
1177dd7cddfSDavid du Colombier 			break;
1187dd7cddfSDavid du Colombier 		    }
1197dd7cddfSDavid du Colombier 		    *++q = 255;
1207dd7cddfSDavid du Colombier 		} else {
1217dd7cddfSDavid du Colombier 		    if (wlimit - q < 3) {
1227dd7cddfSDavid du Colombier 			status = 1;
1237dd7cddfSDavid du Colombier 			break;
1247dd7cddfSDavid du Colombier 		    }
1257dd7cddfSDavid du Colombier 		    *++q = 1;
1267dd7cddfSDavid du Colombier 		    *++q = p[1];
1277dd7cddfSDavid du Colombier 		}
1287dd7cddfSDavid du Colombier 	    }
1297dd7cddfSDavid du Colombier 	    *++q = p1[1];
1307dd7cddfSDavid du Colombier 	    p = p1 + 1;
1317dd7cddfSDavid du Colombier 	} else if ((next = p[1]) == p[2] && next == p[3]) {
1327dd7cddfSDavid du Colombier 	    if (wlimit - q < 2) {
1337dd7cddfSDavid du Colombier 		status = 1;
1347dd7cddfSDavid du Colombier 		break;
1357dd7cddfSDavid du Colombier 	    }
1367dd7cddfSDavid du Colombier 	    /* Recognize leading repeated byte */
1377dd7cddfSDavid du Colombier 	    do {
1387dd7cddfSDavid du Colombier 		p++;
1397dd7cddfSDavid du Colombier 	    }
1407dd7cddfSDavid du Colombier 	    while (p < p1 && p[2] == next);
1417dd7cddfSDavid du Colombier 	    if (p == p1 && !end) {
1427dd7cddfSDavid du Colombier 		p = beg;	/* need to look ahead further */
1437dd7cddfSDavid du Colombier 		break;
1447dd7cddfSDavid du Colombier 	    }
1457dd7cddfSDavid du Colombier 	    p++;
1467dd7cddfSDavid du Colombier 	    *++q = (byte) (257 - (p - beg));
1477dd7cddfSDavid du Colombier 	    *++q = next;
1487dd7cddfSDavid du Colombier 	} else {
1497dd7cddfSDavid du Colombier 	    p1--;
1507dd7cddfSDavid du Colombier 	    while (p < p1 && (p[2] != p[1] || p[3] != p[1]))
1517dd7cddfSDavid du Colombier 		p++;
1527dd7cddfSDavid du Colombier 	    if (p == p1) {
1537dd7cddfSDavid du Colombier 		if (!end) {
1547dd7cddfSDavid du Colombier 		    p = beg;	/* need to look ahead further */
1557dd7cddfSDavid du Colombier 		    break;
1567dd7cddfSDavid du Colombier 		}
1577dd7cddfSDavid du Colombier 		p += 2;
1587dd7cddfSDavid du Colombier 	    }
1597dd7cddfSDavid du Colombier 	    count = p - beg;
1607dd7cddfSDavid du Colombier 	    if (wlimit - q < count + 1) {
1617dd7cddfSDavid du Colombier 		p = beg;
1627dd7cddfSDavid du Colombier 		if (q >= wlimit) {
1637dd7cddfSDavid du Colombier 		    status = 1;
1647dd7cddfSDavid du Colombier 		    break;
1657dd7cddfSDavid du Colombier 		}
1667dd7cddfSDavid du Colombier 		/* Copy some now and some later. */
1677dd7cddfSDavid du Colombier 		*++q = count - 1;
1687dd7cddfSDavid du Colombier 		ss->copy_left = count;
1697dd7cddfSDavid du Colombier 		goto copy;
1707dd7cddfSDavid du Colombier 	    }
1717dd7cddfSDavid du Colombier 	    *++q = count - 1;
1727dd7cddfSDavid du Colombier 	    memcpy(q + 1, beg + 1, count);
1737dd7cddfSDavid du Colombier 	    q += count;
1747dd7cddfSDavid du Colombier 	}
1757dd7cddfSDavid du Colombier 	rleft -= p - beg;
1767dd7cddfSDavid du Colombier 	if (rleft == 0)
1777dd7cddfSDavid du Colombier 	    rleft = ss->record_size;
1787dd7cddfSDavid du Colombier     }
1797dd7cddfSDavid du Colombier     if (last && status == 0 && ss->EndOfData) {
1807dd7cddfSDavid du Colombier 	if (q < wlimit)
1817dd7cddfSDavid du Colombier 	    *++q = 128;
1827dd7cddfSDavid du Colombier 	else
1837dd7cddfSDavid du Colombier 	    status = 1;
1847dd7cddfSDavid du Colombier     }
1857dd7cddfSDavid du Colombier     pr->ptr = p;
1867dd7cddfSDavid du Colombier     pw->ptr = q;
1877dd7cddfSDavid du Colombier     ss->record_left = rleft;
1887dd7cddfSDavid du Colombier     return status;
1897dd7cddfSDavid du Colombier }
1907dd7cddfSDavid du Colombier 
1917dd7cddfSDavid du Colombier /* Stream template */
1927dd7cddfSDavid du Colombier const stream_template s_RLE_template = {
1937dd7cddfSDavid du Colombier     &st_RLE_state, s_RLE_init, s_RLE_process, 129, 2, NULL,
1947dd7cddfSDavid du Colombier     s_RLE_set_defaults, s_RLE_init
1957dd7cddfSDavid du Colombier };
196