xref: /plan9-contrib/sys/src/cmd/gs/src/srle.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
1 /* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: srle.c,v 1.2 2000/09/19 19:00:51 lpd Exp $ */
20 /* RunLengthEncode filter */
21 #include "stdio_.h"		/* includes std.h */
22 #include "memory_.h"
23 #include "strimpl.h"
24 #include "srlx.h"
25 
26 /* ------ RunLengthEncode ------ */
27 
28 private_st_RLE_state();
29 
30 /* Set defaults */
31 private void
32 s_RLE_set_defaults(stream_state * st)
33 {
34     stream_RLE_state *const ss = (stream_RLE_state *) st;
35 
36     s_RLE_set_defaults_inline(ss);
37 }
38 
39 /* Initialize */
40 private int
41 s_RLE_init(stream_state * st)
42 {
43     stream_RLE_state *const ss = (stream_RLE_state *) st;
44 
45     return s_RLE_init_inline(ss);
46 }
47 
48 /* Process a buffer */
49 private int
50 s_RLE_process(stream_state * st, stream_cursor_read * pr,
51 	      stream_cursor_write * pw, bool last)
52 {
53     stream_RLE_state *const ss = (stream_RLE_state *) st;
54     register const byte *p = pr->ptr;
55     register byte *q = pw->ptr;
56     const byte *rlimit = pr->limit;
57     byte *wlimit = pw->limit;
58     int status = 0;
59     ulong rleft = ss->record_left;
60 
61     /*
62      * We thought that the Genoa CET demands that the output from this
63      * filter be not just legal, but optimal, so we went to the trouble
64      * of ensuring this.  It turned out that this wasn't actually
65      * necessary, but we didn't want to change the code back.
66      *
67      * For optimal output, we can't just break runs at buffer
68      * boundaries: unless we hit a record boundary or the end of the
69      * input, we have to look ahead far enough to know that we aren't
70      * breaking a run prematurely.
71      */
72 
73     /* Check for leftover output. */
74 copy:
75     if (ss->copy_left) {
76 	uint rcount = rlimit - p;
77 	uint wcount = wlimit - q;
78 	uint count = ss->copy_left;
79 
80 	if (rcount < count)
81 	    count = rcount;
82 	if (wcount < count)
83 	    count = wcount;
84 	if (rleft < count)
85 	    count = rleft;
86 	memcpy(q + 1, p + 1, count);
87 	pr->ptr = p += count;
88 	pw->ptr = q += count;
89 	if ((ss->record_left = rleft -= count) == 0)
90 	    ss->record_left = rleft = ss->record_size;
91 	if ((ss->copy_left -= count) != 0)
92 	    return (rcount == 0 ? 0 : 1);
93     }
94     while (p < rlimit) {
95 	const byte *beg = p;
96 	const byte *p1;
97 	uint count = rlimit - p;
98 	bool end = last;
99 	byte next;
100 
101 	if (count > rleft)
102 	    count = rleft, end = true;
103 	if (count > 128)
104 	    count = 128, end = true;
105 	p1 = p + count - 1;
106 	if (count < 3) {
107 	    if (!end || count == 0)
108 		break;		/* can't look ahead far enough */
109 	    if (count == 1) {
110 		if (wlimit - q < 2) {
111 		    status = 1;
112 		    break;
113 		}
114 		*++q = 0;
115 	    } else {		/* count == 2 */
116 		if (p[1] == p[2]) {
117 		    if (wlimit - q < 2) {
118 			status = 1;
119 			break;
120 		    }
121 		    *++q = 255;
122 		} else {
123 		    if (wlimit - q < 3) {
124 			status = 1;
125 			break;
126 		    }
127 		    *++q = 1;
128 		    *++q = p[1];
129 		}
130 	    }
131 	    *++q = p1[1];
132 	    p = p1 + 1;
133 	} else if ((next = p[1]) == p[2] && next == p[3]) {
134 	    if (wlimit - q < 2) {
135 		status = 1;
136 		break;
137 	    }
138 	    /* Recognize leading repeated byte */
139 	    do {
140 		p++;
141 	    }
142 	    while (p < p1 && p[2] == next);
143 	    if (p == p1 && !end) {
144 		p = beg;	/* need to look ahead further */
145 		break;
146 	    }
147 	    p++;
148 	    *++q = (byte) (257 - (p - beg));
149 	    *++q = next;
150 	} else {
151 	    p1--;
152 	    while (p < p1 && (p[2] != p[1] || p[3] != p[1]))
153 		p++;
154 	    if (p == p1) {
155 		if (!end) {
156 		    p = beg;	/* need to look ahead further */
157 		    break;
158 		}
159 		p += 2;
160 	    }
161 	    count = p - beg;
162 	    if (wlimit - q < count + 1) {
163 		p = beg;
164 		if (q >= wlimit) {
165 		    status = 1;
166 		    break;
167 		}
168 		/* Copy some now and some later. */
169 		*++q = count - 1;
170 		ss->copy_left = count;
171 		goto copy;
172 	    }
173 	    *++q = count - 1;
174 	    memcpy(q + 1, beg + 1, count);
175 	    q += count;
176 	}
177 	rleft -= p - beg;
178 	if (rleft == 0)
179 	    rleft = ss->record_size;
180     }
181     if (last && status == 0 && ss->EndOfData) {
182 	if (q < wlimit)
183 	    *++q = 128;
184 	else
185 	    status = 1;
186     }
187     pr->ptr = p;
188     pw->ptr = q;
189     ss->record_left = rleft;
190     return status;
191 }
192 
193 /* Stream template */
194 const stream_template s_RLE_template = {
195     &st_RLE_state, s_RLE_init, s_RLE_process, 129, 2, NULL,
196     s_RLE_set_defaults, s_RLE_init
197 };
198