blob: c63ee14328ae492b3ec898b70c65639d92480677 [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? */
86 std::map<PIXEL_T,int> m_counts;
87};
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
164 // Choose the best background color
165 int maxCount = 0, count;
166 std::map<PIXEL_T,int>::iterator i;
167 for (i = m_counts.begin(); i != m_counts.end(); i++) {
168 color = (*i).first;
169 count = (*i).second;
170 if (count > maxCount) {
171 maxCount = count;
172 m_background = color;
173 }
174 }
175
176 // Save the number of colors
177 m_numColors = m_counts.size();
178
179 // Set foreground color if it's a monochrome tile
180 if (m_numColors == 2) {
181 i = m_counts.begin();
182 m_foreground = (*i).first;
183 if (m_foreground == m_background) {
184 i++;
185 m_foreground = (*i).first;
186 }
187 // Calculate and return encoded data size
188 return 1 + 2 * (m_numSubrects - maxCount);
189 }
190
191 // Calculate and return encoded data size (colored subrects)
192 return 1 + (2 + (BPP/8)) * (m_numSubrects - maxCount);
193}
194
195/*
196 * Call this function only if there are any subrects in the tile.
197 * The buffer size should be enough to store at least that number of
198 * bytes returned by buildTables() method.
199 * Returns encoded data size, or zero if something is wrong.
200 */
201
202int HEXTILE_SUBRECTS_TABLE::encode(rdr::U8 *dst)
203{
204 if (m_numSubrects == -1 || m_numColors == 0)
205 return 0;
206
207 // Zero subrects counter.
208 rdr::U8 *numSubrectsPtr = dst;
209 *dst++ = 0;
210
211 for (int i = 0; i < m_numSubrects; i++) {
212 if (m_colors[i] == m_background)
213 continue;
214
215 if (m_numColors > 2) { /* FIXME: Duplicate */
216#if (BPP == 8)
217 *dst++ = m_colors[i];
218#elif (BPP == 16)
219 *dst++ = ((rdr::U8*)&m_colors[i])[0];
220 *dst++ = ((rdr::U8*)&m_colors[i])[1];
221#elif (BPP == 32)
222 *dst++ = ((rdr::U8*)&m_colors[i])[0];
223 *dst++ = ((rdr::U8*)&m_colors[i])[1];
224 *dst++ = ((rdr::U8*)&m_colors[i])[2];
225 *dst++ = ((rdr::U8*)&m_colors[i])[3];
226#endif
227 }
228 *dst++ = m_coords[i * 2];
229 *dst++ = m_coords[i * 2 + 1];
230
231 (*numSubrectsPtr)++;
232 }
233
234 return (dst - numSubrectsPtr);
235}
236
237/*------------------------------------------------------------------*/
238
239void HEXTILE_ENCODE(const Rect& r, rdr::OutStream* os
240#ifdef EXTRA_ARGS
241 , EXTRA_ARGS
242#endif
243 )
244{
245 Rect t;
246 PIXEL_T buf[256];
247 PIXEL_T oldBg = 0, oldFg = 0;
248 bool oldBgValid = false;
249 bool oldFgValid = false;
250 rdr::U8 encoded[256*(BPP/8)];
251
252 HEXTILE_SUBRECTS_TABLE subrects;
253
254 for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
255
256 t.br.y = vncmin(r.br.y, t.tl.y + 16);
257
258 for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
259
260 t.br.x = vncmin(r.br.x, t.tl.x + 16);
261
262 GET_IMAGE_INTO_BUF(t,buf);
263
264 subrects.newTile(buf, t.width(), t.height());
265 int encodedLen = subrects.buildTables();
266
267 // FIXME: Adjust encodedLen comparison!
268 if (encodedLen >= t.width() * t.height() * (BPP/8)) {
269 os->writeU8(hextileRaw);
270 os->writeBytes(buf, t.width() * t.height() * (BPP/8));
271 oldBgValid = oldFgValid = false;
272 continue;
273 }
274
275 int numColors = subrects.getNumColors();
276 PIXEL_T bg = subrects.getBackground();
277 PIXEL_T fg = subrects.getForeground();
278
279 int tileType = 0;
280
281 if (!oldBgValid || oldBg != bg) {
282 tileType |= hextileBgSpecified;
283 oldBg = bg;
284 oldBgValid = true;
285 }
286
287 if (numColors >= 2) {
288 tileType |= hextileAnySubrects;
289 if (numColors == 2) {
290 if (!oldFgValid || oldFg != fg) {
291 tileType |= hextileFgSpecified;
292 oldFg = fg;
293 oldFgValid = true;
294 }
295 } else {
296 tileType |= hextileSubrectsColoured;
297 oldFgValid = false;
298 }
299 int finalEncodedLen = subrects.encode(encoded);
300 assert(finalEncodedLen == encodedLen);
301 assert(finalEncodedLen <= 256*(BPP/8));
302 }
303
304 os->writeU8(tileType);
305 if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg);
306 if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg);
307 if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
308 }
309 }
310}
311
312#undef PIXEL_T
313#undef WRITE_PIXEL
314#undef HEXTILE_ENCODE
315
316#undef HEXTILE_SUBRECTS_TABLE
317}