blob: 42505a3137d654275f4a43d27559b8985f2887f6 [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19//
20// zrleEncode.h - zrle encoding function.
21//
22// This file is #included after having set the following macros:
23// BPP - 8, 16 or 32
24// EXTRA_ARGS - optional extra arguments
25// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer
26//
27// Note that the buf argument to ZRLE_ENCODE needs to be at least one pixel
28// bigger than the largest tile of pixel data, since the ZRLE encoding
29// algorithm writes to the position one past the end of the pixel data.
30//
31
32#include <rdr/OutStream.h>
33#include <rdr/ZlibOutStream.h>
34#include <assert.h>
35
36namespace rfb {
37
38// CONCAT2E concatenates its arguments, expanding them if they are macros
39
40#ifndef CONCAT2E
41#define CONCAT2(a,b) a##b
42#define CONCAT2E(a,b) CONCAT2(a,b)
43#endif
44
45#ifdef CPIXEL
46#define PIXEL_T rdr::CONCAT2E(U,BPP)
47#define WRITE_PIXEL CONCAT2E(writeOpaque,CPIXEL)
48#define ZRLE_ENCODE CONCAT2E(zrleEncode,CPIXEL)
49#define ZRLE_ENCODE_TILE CONCAT2E(zrleEncodeTile,CPIXEL)
50#define BPPOUT 24
51#else
52#define PIXEL_T rdr::CONCAT2E(U,BPP)
53#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
54#define ZRLE_ENCODE CONCAT2E(zrleEncode,BPP)
55#define ZRLE_ENCODE_TILE CONCAT2E(zrleEncodeTile,BPP)
56#define BPPOUT BPP
57#endif
58
59#ifndef ZRLE_ONCE
60#define ZRLE_ONCE
61static const int bitsPerPackedPixel[] = {
62 0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
63};
64
65// The PaletteHelper class helps us build up the palette from pixel data by
66// storing a reverse index using a simple hash-table
67
68class PaletteHelper {
69public:
70 enum { MAX_SIZE = 127 };
71
72 PaletteHelper()
73 {
74 memset(index, 255, sizeof(index));
75 size = 0;
76 }
77
78 inline int hash(rdr::U32 pix)
79 {
80 return (pix ^ (pix >> 17)) & 4095;
81 }
82
83 inline void insert(rdr::U32 pix)
84 {
85 if (size < MAX_SIZE) {
86 int i = hash(pix);
87 while (index[i] != 255 && key[i] != pix)
88 i++;
89 if (index[i] != 255) return;
90
91 index[i] = size;
92 key[i] = pix;
93 palette[size] = pix;
94 }
95 size++;
96 }
97
98 inline int lookup(rdr::U32 pix)
99 {
100 assert(size <= MAX_SIZE);
101 int i = hash(pix);
102 while (index[i] != 255 && key[i] != pix)
103 i++;
104 if (index[i] != 255) return index[i];
105 return -1;
106 }
107
108 rdr::U32 palette[MAX_SIZE];
109 rdr::U8 index[4096+MAX_SIZE];
110 rdr::U32 key[4096+MAX_SIZE];
111 int size;
112};
113#endif
114
115void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os);
116
117bool ZRLE_ENCODE (const Rect& r, rdr::OutStream* os,
118 rdr::ZlibOutStream* zos, void* buf, int maxLen, Rect* actual
119#ifdef EXTRA_ARGS
120 , EXTRA_ARGS
121#endif
122 )
123{
124 zos->setUnderlying(os);
125 // RLE overhead is at worst 1 byte per 64x64 (4Kpixel) block
126 int worstCaseLine = r.width() * 64 * (BPPOUT/8) + 1 + r.width() / 64;
127 // Zlib overhead is at worst 6 bytes plus 5 bytes per 32Kbyte block.
128 worstCaseLine += 11 + 5 * (worstCaseLine >> 15);
129 Rect t;
130
131 for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) {
132
Peter Åstrand0f49e222005-01-13 22:11:30 +0000133 t.br.y = vncmin(r.br.y, t.tl.y + 64);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000134
135 if (os->length() + worstCaseLine > maxLen) {
136 if (t.tl.y == r.tl.y)
137 throw Exception("ZRLE: not enough space for first line?");
138 actual->tl = r.tl;
139 actual->br.x = r.br.x;
140 actual->br.y = t.tl.y;
141 return false;
142 }
143
144 for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) {
145
Peter Åstrand0f49e222005-01-13 22:11:30 +0000146 t.br.x = vncmin(r.br.x, t.tl.x + 64);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000147
148 GET_IMAGE_INTO_BUF(t,buf);
149
150 ZRLE_ENCODE_TILE((PIXEL_T*)buf, t.width(), t.height(), zos);
151 }
152
153 zos->flush();
154 }
155 return true;
156}
157
158
159void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os)
160{
161 // First find the palette and the number of runs
162
163 PaletteHelper ph;
164
165 int runs = 0;
166 int singlePixels = 0;
167
168 PIXEL_T* ptr = data;
169 PIXEL_T* end = ptr + h * w;
170 *end = ~*(end-1); // one past the end is different so the while loop ends
171
172 while (ptr < end) {
173 PIXEL_T pix = *ptr;
174 if (*++ptr != pix) {
175 singlePixels++;
176 } else {
177 while (*++ptr == pix) ;
178 runs++;
179 }
180 ph.insert(pix);
181 }
182
183 //fprintf(stderr,"runs %d, single pixels %d, paletteSize %d\n",
184 // runs, singlePixels, ph.size);
185
186 // Solid tile is a special case
187
188 if (ph.size == 1) {
189 os->writeU8(1);
190 os->WRITE_PIXEL(ph.palette[0]);
191 return;
192 }
193
194 // Try to work out whether to use RLE and/or a palette. We do this by
195 // estimating the number of bytes which will be generated and picking the
196 // method which results in the fewest bytes. Of course this may not result
197 // in the fewest bytes after compression...
198
199 bool useRle = false;
200 bool usePalette = false;
201
202 int estimatedBytes = w * h * (BPPOUT/8); // start assuming raw
203
204 int plainRleBytes = ((BPPOUT/8)+1) * (runs + singlePixels);
205
206 if (plainRleBytes < estimatedBytes) {
207 useRle = true;
208 estimatedBytes = plainRleBytes;
209 }
210
211 if (ph.size < 128) {
212 int paletteRleBytes = (BPPOUT/8) * ph.size + 2 * runs + singlePixels;
213
214 if (paletteRleBytes < estimatedBytes) {
215 useRle = true;
216 usePalette = true;
217 estimatedBytes = paletteRleBytes;
218 }
219
220 if (ph.size < 17) {
221 int packedBytes = ((BPPOUT/8) * ph.size +
222 w * h * bitsPerPackedPixel[ph.size-1] / 8);
223
224 if (packedBytes < estimatedBytes) {
225 useRle = false;
226 usePalette = true;
227 estimatedBytes = packedBytes;
228 }
229 }
230 }
231
232 if (!usePalette) ph.size = 0;
233
234 os->writeU8((useRle ? 128 : 0) | ph.size);
235
236 for (int i = 0; i < ph.size; i++) {
237 os->WRITE_PIXEL(ph.palette[i]);
238 }
239
240 if (useRle) {
241
242 PIXEL_T* ptr = data;
243 PIXEL_T* end = ptr + w * h;
244 PIXEL_T* runStart;
245 PIXEL_T pix;
246 while (ptr < end) {
247 runStart = ptr;
248 pix = *ptr++;
249 while (*ptr == pix && ptr < end)
250 ptr++;
251 int len = ptr - runStart;
252 if (len <= 2 && usePalette) {
253 int index = ph.lookup(pix);
254 if (len == 2)
255 os->writeU8(index);
256 os->writeU8(index);
257 continue;
258 }
259 if (usePalette) {
260 int index = ph.lookup(pix);
261 os->writeU8(index | 128);
262 } else {
263 os->WRITE_PIXEL(pix);
264 }
265 len -= 1;
266 while (len >= 255) {
267 os->writeU8(255);
268 len -= 255;
269 }
270 os->writeU8(len);
271 }
272
273 } else {
274
275 // no RLE
276
277 if (usePalette) {
278
279 // packed pixels
280
281 assert (ph.size < 17);
282
283 int bppp = bitsPerPackedPixel[ph.size-1];
284
285 PIXEL_T* ptr = data;
286
287 for (int i = 0; i < h; i++) {
288 rdr::U8 nbits = 0;
289 rdr::U8 byte = 0;
290
291 PIXEL_T* eol = ptr + w;
292
293 while (ptr < eol) {
294 PIXEL_T pix = *ptr++;
295 rdr::U8 index = ph.lookup(pix);
296 byte = (byte << bppp) | index;
297 nbits += bppp;
298 if (nbits >= 8) {
299 os->writeU8(byte);
300 nbits = 0;
301 }
302 }
303 if (nbits > 0) {
304 byte <<= 8 - nbits;
305 os->writeU8(byte);
306 }
307 }
308 } else {
309
310 // raw
311
312#ifdef CPIXEL
313 for (PIXEL_T* ptr = data; ptr < data+w*h; ptr++) {
314 os->WRITE_PIXEL(*ptr);
315 }
316#else
317 os->writeBytes(data, w*h*(BPP/8));
318#endif
319 }
320 }
321}
322
323#undef PIXEL_T
324#undef WRITE_PIXEL
325#undef ZRLE_ENCODE
326#undef ZRLE_ENCODE_TILE
327#undef BPPOUT
328}