blob: eab40cbaef945422cdb4423a040a06237a017bf0 [file] [log] [blame]
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +00001/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
2 * Copyright (C) 2005 Constantin Kaplinsky. All Rights Reserved.
3 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19//
20// Hextile 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#include <map>
28
29#include <rdr/OutStream.h>
30#include <rfb/hextileConstants.h>
31
32#include <assert.h>
33
34namespace rfb {
35
36// CONCAT2E concatenates its arguments, expanding them if they are macros
37
38#ifndef CONCAT2E
39#define CONCAT2(a,b) a##b
40#define CONCAT2E(a,b) CONCAT2(a,b)
41#endif
42
43#define PIXEL_T rdr::CONCAT2E(U,BPP)
44#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
45#define HEXTILE_ENCODE CONCAT2E(hextileEncode,BPP)
46
47/********************************************************************/
48
49#define HEXTILE_SUBRECTS_TABLE CONCAT2E(HextileSubrectsTable,BPP)
50
51class HEXTILE_SUBRECTS_TABLE {
52
53 public:
54
55 HEXTILE_SUBRECTS_TABLE ();
56
57 void newTile(const PIXEL_T *src, int w, int h);
58 int buildTables();
59 int encode(rdr::U8* dst);
60
61 int getNumColors() const { return m_numColors; }
62 int getBackground() const { return m_background; }
63 int getForeground() const { return m_foreground; }
64
65 protected:
66
67 const PIXEL_T *m_tile;
68 int m_width;
69 int m_height;
70 int m_numSubrects;
71 int m_numColors;
72 PIXEL_T m_background;
73 PIXEL_T m_foreground;
74
75 // FIXME: Comment data structures.
76 rdr::U8 m_coords[256 * 2];
77 PIXEL_T m_colors[256];
78
79 private:
80
81 /* DEBUG: Check performance for: (1) U8[] and (2) dyn.allocated. */
82 bool m_processed[16][16];
83
84 /* FIXME: Use array for (BPP == 8)? */
85 /* DEBUG: Use own hashing like in ZRLE? */
Constantin Kaplinsky2a90a452005-09-09 19:57:49 +000086 std::map<PIXEL_T,short> m_counts;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000087};
88
89HEXTILE_SUBRECTS_TABLE::HEXTILE_SUBRECTS_TABLE()
90 : m_tile(NULL), m_width(0), m_height(0), m_numSubrects(-1), m_numColors(0),
91 m_background(0), m_foreground(0)
92{
93}
94
95void HEXTILE_SUBRECTS_TABLE::newTile(const PIXEL_T *src, int w, int h)
96{
97 m_tile = src;
98 m_width = w;
99 m_height = h;
100 m_numSubrects = -1;
101 m_numColors = 0;
102}
103
104/*
105 * Returns estimated encoded data size.
106 */
107
108int HEXTILE_SUBRECTS_TABLE::buildTables()
109{
110 if (m_tile == NULL || m_width == 0 || m_height == 0)
111 return -1;
112
113 m_numSubrects = 0;
114 memset(m_processed, 0, 16 * 16 * sizeof(bool));
115 m_counts.clear();
116
117 int x, y, sx, sy, sw, sh, max_x;
118 PIXEL_T color;
119 PIXEL_T *colorsPtr = &m_colors[0];
120 rdr::U8 *coordsPtr = &m_coords[0];
121
122 for (y = 0; y < m_height; y++) {
123 for (x = 0; x < m_width; x++) {
124 /* Skip pixels that were processed earlier */
125 if (m_processed[y][x]) {
126 continue;
127 }
128 /* Determine dimensions of the horizontal subrect */
129 color = m_tile[y * m_width + x];
130 for (sx = x + 1; sx < m_width; sx++) {
131 if (m_tile[y * m_width + sx] != color)
132 break;
133 }
134 sw = sx - x;
135 max_x = sx;
136 for (sy = y + 1; sy < m_height; sy++) {
137 for (sx = x; sx < max_x; sx++) {
138 if (m_tile[sy * m_width + sx] != color)
139 goto done;
140 }
141 }
142 done:
143 sh = sy - y;
144
145 /* Save properties of this subrect */
146 *colorsPtr++ = color;
147 *coordsPtr++ = (rdr::U8)((x << 4) | (y & 0x0F));
148 *coordsPtr++ = (rdr::U8)(((sw - 1) << 4) | ((sh - 1) & 0x0F));
149 m_counts[color] += 1;
150
151 m_numSubrects++;
152
153 /* Mark pixels of this subrect as processed, below this row */
154 for (sy = y + 1; sy < y + sh; sy++) {
155 for (sx = x; sx < x + sw; sx++)
156 m_processed[sy][sx] = true;
157 }
158
159 /* Skip processed pixels of this row */
160 x += (sw - 1);
161 }
162 }
163
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000164 // Save the number of colors
165 m_numColors = m_counts.size();
166
167 // Handle solid tile
168 if (m_numColors == 1) {
169 m_background = m_tile[0];
170 return 0;
171 }
172
Constantin Kaplinsky2a90a452005-09-09 19:57:49 +0000173 std::map<PIXEL_T,short>::iterator i;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000174
175 // Handle monochrome tile - choose background and foreground
176 if (m_numColors == 2) {
177 i = m_counts.begin();
178 m_background = i->first;
179 int bgCount = i->second;
180 i++;
181 if (i->second <= bgCount) {
182 m_foreground = i->first;
183 } else {
184 m_foreground = m_background;
185 m_background = i->first;
186 bgCount = i->second;;
187 }
188
189 return 1 + 2 * (m_numSubrects - bgCount);
190 }
191
192 // Handle colored tile - choose the best background color
193 int bgCount = 0, count;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000194 for (i = m_counts.begin(); i != m_counts.end(); i++) {
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000195 color = i->first;
196 count = i->second;
197 if (count > bgCount) {
198 bgCount = count;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000199 m_background = color;
200 }
201 }
202
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000203 // Calculate and return encoded data size (colored subrects)
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000204 return 1 + (2 + (BPP/8)) * (m_numSubrects - bgCount);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000205}
206
207/*
208 * Call this function only if there are any subrects in the tile.
209 * The buffer size should be enough to store at least that number of
210 * bytes returned by buildTables() method.
211 * Returns encoded data size, or zero if something is wrong.
212 */
213
214int HEXTILE_SUBRECTS_TABLE::encode(rdr::U8 *dst)
215{
216 if (m_numSubrects == -1 || m_numColors == 0)
217 return 0;
218
219 // Zero subrects counter.
220 rdr::U8 *numSubrectsPtr = dst;
221 *dst++ = 0;
222
223 for (int i = 0; i < m_numSubrects; i++) {
224 if (m_colors[i] == m_background)
225 continue;
226
227 if (m_numColors > 2) { /* FIXME: Duplicate */
228#if (BPP == 8)
229 *dst++ = m_colors[i];
230#elif (BPP == 16)
231 *dst++ = ((rdr::U8*)&m_colors[i])[0];
232 *dst++ = ((rdr::U8*)&m_colors[i])[1];
233#elif (BPP == 32)
234 *dst++ = ((rdr::U8*)&m_colors[i])[0];
235 *dst++ = ((rdr::U8*)&m_colors[i])[1];
236 *dst++ = ((rdr::U8*)&m_colors[i])[2];
237 *dst++ = ((rdr::U8*)&m_colors[i])[3];
238#endif
239 }
240 *dst++ = m_coords[i * 2];
241 *dst++ = m_coords[i * 2 + 1];
242
243 (*numSubrectsPtr)++;
244 }
245
246 return (dst - numSubrectsPtr);
247}
248
249/*------------------------------------------------------------------*/
250
251void HEXTILE_ENCODE(const Rect& r, rdr::OutStream* os
252#ifdef EXTRA_ARGS
253 , EXTRA_ARGS
254#endif
255 )
256{
257 Rect t;
258 PIXEL_T buf[256];
259 PIXEL_T oldBg = 0, oldFg = 0;
260 bool oldBgValid = false;
261 bool oldFgValid = false;
262 rdr::U8 encoded[256*(BPP/8)];
263
264 HEXTILE_SUBRECTS_TABLE subrects;
265
266 for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
267
268 t.br.y = vncmin(r.br.y, t.tl.y + 16);
269
270 for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
271
272 t.br.x = vncmin(r.br.x, t.tl.x + 16);
273
274 GET_IMAGE_INTO_BUF(t,buf);
275
276 subrects.newTile(buf, t.width(), t.height());
277 int encodedLen = subrects.buildTables();
278
279 // FIXME: Adjust encodedLen comparison!
280 if (encodedLen >= t.width() * t.height() * (BPP/8)) {
281 os->writeU8(hextileRaw);
282 os->writeBytes(buf, t.width() * t.height() * (BPP/8));
283 oldBgValid = oldFgValid = false;
284 continue;
285 }
286
287 int numColors = subrects.getNumColors();
288 PIXEL_T bg = subrects.getBackground();
289 PIXEL_T fg = subrects.getForeground();
290
291 int tileType = 0;
292
293 if (!oldBgValid || oldBg != bg) {
294 tileType |= hextileBgSpecified;
295 oldBg = bg;
296 oldBgValid = true;
297 }
298
299 if (numColors >= 2) {
300 tileType |= hextileAnySubrects;
301 if (numColors == 2) {
302 if (!oldFgValid || oldFg != fg) {
303 tileType |= hextileFgSpecified;
304 oldFg = fg;
305 oldFgValid = true;
306 }
307 } else {
308 tileType |= hextileSubrectsColoured;
309 oldFgValid = false;
310 }
311 int finalEncodedLen = subrects.encode(encoded);
312 assert(finalEncodedLen == encodedLen);
313 assert(finalEncodedLen <= 256*(BPP/8));
314 }
315
316 os->writeU8(tileType);
317 if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg);
318 if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg);
319 if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
320 }
321 }
322}
323
324#undef PIXEL_T
325#undef WRITE_PIXEL
326#undef HEXTILE_ENCODE
327
328#undef HEXTILE_SUBRECTS_TABLE
329}