blob: 9c560106b48ef440ae41a422b5a7c70d42155fc1 [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
Constantin Kaplinsky4292b842005-09-09 20:05:22 +000061 // Flags can include: hextileAnySubrects, hextileSubrectsColoured
62 int getFlags() const { return m_flags; }
63
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000064 int getBackground() const { return m_background; }
65 int getForeground() const { return m_foreground; }
66
67 protected:
68
69 const PIXEL_T *m_tile;
70 int m_width;
71 int m_height;
72 int m_numSubrects;
Constantin Kaplinsky4292b842005-09-09 20:05:22 +000073 int m_flags;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000074 PIXEL_T m_background;
75 PIXEL_T m_foreground;
76
77 // FIXME: Comment data structures.
78 rdr::U8 m_coords[256 * 2];
79 PIXEL_T m_colors[256];
80
81 private:
82
83 /* DEBUG: Check performance for: (1) U8[] and (2) dyn.allocated. */
84 bool m_processed[16][16];
85
86 /* FIXME: Use array for (BPP == 8)? */
87 /* DEBUG: Use own hashing like in ZRLE? */
Constantin Kaplinsky2a90a452005-09-09 19:57:49 +000088 std::map<PIXEL_T,short> m_counts;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000089};
90
91HEXTILE_SUBRECTS_TABLE::HEXTILE_SUBRECTS_TABLE()
Constantin Kaplinsky4292b842005-09-09 20:05:22 +000092 : m_tile(NULL), m_width(0), m_height(0), m_numSubrects(0),
93 m_flags(0), m_background(0), m_foreground(0)
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000094{
95}
96
97void HEXTILE_SUBRECTS_TABLE::newTile(const PIXEL_T *src, int w, int h)
98{
99 m_tile = src;
100 m_width = w;
101 m_height = h;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000102}
103
104/*
105 * Returns estimated encoded data size.
106 */
107
108int HEXTILE_SUBRECTS_TABLE::buildTables()
109{
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000110 assert(m_tile && m_width && m_height);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000111
112 m_numSubrects = 0;
113 memset(m_processed, 0, 16 * 16 * sizeof(bool));
114 m_counts.clear();
115
116 int x, y, sx, sy, sw, sh, max_x;
117 PIXEL_T color;
118 PIXEL_T *colorsPtr = &m_colors[0];
119 rdr::U8 *coordsPtr = &m_coords[0];
120
121 for (y = 0; y < m_height; y++) {
122 for (x = 0; x < m_width; x++) {
123 /* Skip pixels that were processed earlier */
124 if (m_processed[y][x]) {
125 continue;
126 }
127 /* Determine dimensions of the horizontal subrect */
128 color = m_tile[y * m_width + x];
129 for (sx = x + 1; sx < m_width; sx++) {
130 if (m_tile[y * m_width + sx] != color)
131 break;
132 }
133 sw = sx - x;
134 max_x = sx;
135 for (sy = y + 1; sy < m_height; sy++) {
136 for (sx = x; sx < max_x; sx++) {
137 if (m_tile[sy * m_width + sx] != color)
138 goto done;
139 }
140 }
141 done:
142 sh = sy - y;
143
144 /* Save properties of this subrect */
145 *colorsPtr++ = color;
146 *coordsPtr++ = (rdr::U8)((x << 4) | (y & 0x0F));
147 *coordsPtr++ = (rdr::U8)(((sw - 1) << 4) | ((sh - 1) & 0x0F));
148 m_counts[color] += 1;
149
150 m_numSubrects++;
151
152 /* Mark pixels of this subrect as processed, below this row */
153 for (sy = y + 1; sy < y + sh; sy++) {
154 for (sx = x; sx < x + sw; sx++)
155 m_processed[sy][sx] = true;
156 }
157
158 /* Skip processed pixels of this row */
159 x += (sw - 1);
160 }
161 }
162
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000163 // Save the number of colors
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000164 int numColors = m_counts.size();
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000165
166 // Handle solid tile
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000167 if (numColors == 1) {
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000168 m_background = m_tile[0];
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000169 m_flags = 0;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000170 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
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000176 if (numColors == 2) {
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000177 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
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000189 m_flags = hextileAnySubrects;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000190 return 1 + 2 * (m_numSubrects - bgCount);
191 }
192
193 // Handle colored tile - choose the best background color
194 int bgCount = 0, count;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000195 for (i = m_counts.begin(); i != m_counts.end(); i++) {
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000196 color = i->first;
197 count = i->second;
198 if (count > bgCount) {
199 bgCount = count;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000200 m_background = color;
201 }
202 }
203
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000204 m_flags = hextileAnySubrects | hextileSubrectsColoured;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000205 return 1 + (2 + (BPP/8)) * (m_numSubrects - bgCount);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000206}
207
208/*
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000209 * Call this function only if hextileAnySubrects bit is set in flags.
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000210 * The buffer size should be enough to store at least that number of
211 * bytes returned by buildTables() method.
212 * Returns encoded data size, or zero if something is wrong.
213 */
214
215int HEXTILE_SUBRECTS_TABLE::encode(rdr::U8 *dst)
216{
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000217 assert(m_numSubrects && (m_flags & hextileAnySubrects));
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000218
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
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000227 if (m_flags & hextileSubrectsColoured) {
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000228#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
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000279 if (encodedLen >= t.width() * t.height() * (BPP/8)) {
280 os->writeU8(hextileRaw);
281 os->writeBytes(buf, t.width() * t.height() * (BPP/8));
282 oldBgValid = oldFgValid = false;
283 continue;
284 }
285
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000286 int tileType = subrects.getFlags();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000287 PIXEL_T bg = subrects.getBackground();
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000288 PIXEL_T fg = 0;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000289
290 if (!oldBgValid || oldBg != bg) {
291 tileType |= hextileBgSpecified;
292 oldBg = bg;
293 oldBgValid = true;
294 }
295
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000296 if (tileType & hextileAnySubrects) {
297 if (tileType & hextileSubrectsColoured) {
298 oldFgValid = false;
299 } else {
300 fg = subrects.getForeground();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000301 if (!oldFgValid || oldFg != fg) {
302 tileType |= hextileFgSpecified;
303 oldFg = fg;
304 oldFgValid = true;
305 }
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000306 }
307 int finalEncodedLen = subrects.encode(encoded);
308 assert(finalEncodedLen == encodedLen);
309 assert(finalEncodedLen <= 256*(BPP/8));
310 }
311
312 os->writeU8(tileType);
313 if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg);
314 if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg);
315 if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
316 }
317 }
318}
319
320#undef PIXEL_T
321#undef WRITE_PIXEL
322#undef HEXTILE_ENCODE
323
324#undef HEXTILE_SUBRECTS_TABLE
325}