blob: c5b99f5ebb8b9cacc46dc12671035642a6400640 [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)
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000045#define HEXTILE_SUBRECTS_TABLE CONCAT2E(HextileSubrectsTable,BPP)
Constantin Kaplinskyd6551172005-09-16 07:46:16 +000046#define HEXTILE_ENCODE CONCAT2E(hextileEncodeBetter,BPP)
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000047
48class HEXTILE_SUBRECTS_TABLE {
49
50 public:
51
52 HEXTILE_SUBRECTS_TABLE ();
53
54 void newTile(const PIXEL_T *src, int w, int h);
55 int buildTables();
56 int encode(rdr::U8* dst);
57
Constantin Kaplinsky4292b842005-09-09 20:05:22 +000058 // Flags can include: hextileAnySubrects, hextileSubrectsColoured
59 int getFlags() const { return m_flags; }
60
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000061 int getBackground() const { return m_background; }
62 int getForeground() const { return m_foreground; }
63
64 protected:
65
66 const PIXEL_T *m_tile;
67 int m_width;
68 int m_height;
69 int m_numSubrects;
Constantin Kaplinsky4292b842005-09-09 20:05:22 +000070 int m_flags;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000071 PIXEL_T m_background;
72 PIXEL_T m_foreground;
73
74 // FIXME: Comment data structures.
75 rdr::U8 m_coords[256 * 2];
76 PIXEL_T m_colors[256];
77
78 private:
79
Constantin Kaplinskyd6551172005-09-16 07:46:16 +000080 // DEBUG: Check performance for: (1) U8[] and (2) dyn.allocated.
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000081 bool m_processed[16][16];
82
Constantin Kaplinskyd6551172005-09-16 07:46:16 +000083 // FIXME: Use array for (BPP == 8)?
84 // DEBUG: Use own hashing like in ZRLE?
Constantin Kaplinsky2a90a452005-09-09 19:57:49 +000085 std::map<PIXEL_T,short> m_counts;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000086};
87
88HEXTILE_SUBRECTS_TABLE::HEXTILE_SUBRECTS_TABLE()
Constantin Kaplinsky4292b842005-09-09 20:05:22 +000089 : m_tile(NULL), m_width(0), m_height(0), m_numSubrects(0),
90 m_flags(0), m_background(0), m_foreground(0)
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000091{
92}
93
94void HEXTILE_SUBRECTS_TABLE::newTile(const PIXEL_T *src, int w, int h)
95{
96 m_tile = src;
97 m_width = w;
98 m_height = h;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000099}
100
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000101//
102// Returns estimated encoded data size.
103//
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000104
105int HEXTILE_SUBRECTS_TABLE::buildTables()
106{
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000107 assert(m_tile && m_width && m_height);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000108
109 m_numSubrects = 0;
110 memset(m_processed, 0, 16 * 16 * sizeof(bool));
111 m_counts.clear();
112
113 int x, y, sx, sy, sw, sh, max_x;
114 PIXEL_T color;
115 PIXEL_T *colorsPtr = &m_colors[0];
116 rdr::U8 *coordsPtr = &m_coords[0];
117
118 for (y = 0; y < m_height; y++) {
119 for (x = 0; x < m_width; x++) {
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000120 // Skip pixels that were processed earlier
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000121 if (m_processed[y][x]) {
122 continue;
123 }
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000124 // Determine dimensions of the horizontal subrect
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000125 color = m_tile[y * m_width + x];
126 for (sx = x + 1; sx < m_width; sx++) {
127 if (m_tile[y * m_width + sx] != color)
128 break;
129 }
130 sw = sx - x;
131 max_x = sx;
132 for (sy = y + 1; sy < m_height; sy++) {
133 for (sx = x; sx < max_x; sx++) {
134 if (m_tile[sy * m_width + sx] != color)
135 goto done;
136 }
137 }
138 done:
139 sh = sy - y;
140
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000141 // Save properties of this subrect
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000142 *colorsPtr++ = color;
143 *coordsPtr++ = (rdr::U8)((x << 4) | (y & 0x0F));
144 *coordsPtr++ = (rdr::U8)(((sw - 1) << 4) | ((sh - 1) & 0x0F));
145 m_counts[color] += 1;
146
147 m_numSubrects++;
148
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000149 // Mark pixels of this subrect as processed, below this row
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000150 for (sy = y + 1; sy < y + sh; sy++) {
151 for (sx = x; sx < x + sw; sx++)
152 m_processed[sy][sx] = true;
153 }
154
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000155 // Skip processed pixels of this row
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000156 x += (sw - 1);
157 }
158 }
159
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000160 // Save the number of colors
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000161 int numColors = m_counts.size();
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000162
163 // Handle solid tile
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000164 if (numColors == 1) {
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000165 m_background = m_tile[0];
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000166 m_flags = 0;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000167 return 0;
168 }
169
Constantin Kaplinsky2a90a452005-09-09 19:57:49 +0000170 std::map<PIXEL_T,short>::iterator i;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000171
172 // Handle monochrome tile - choose background and foreground
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000173 if (numColors == 2) {
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000174 i = m_counts.begin();
175 m_background = i->first;
176 int bgCount = i->second;
177 i++;
178 if (i->second <= bgCount) {
179 m_foreground = i->first;
180 } else {
181 m_foreground = m_background;
182 m_background = i->first;
183 bgCount = i->second;;
184 }
185
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000186 m_flags = hextileAnySubrects;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000187 return 1 + 2 * (m_numSubrects - bgCount);
188 }
189
190 // Handle colored tile - choose the best background color
191 int bgCount = 0, count;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000192 for (i = m_counts.begin(); i != m_counts.end(); i++) {
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000193 color = i->first;
194 count = i->second;
195 if (count > bgCount) {
196 bgCount = count;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000197 m_background = color;
198 }
199 }
200
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000201 m_flags = hextileAnySubrects | hextileSubrectsColoured;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000202 return 1 + (2 + (BPP/8)) * (m_numSubrects - bgCount);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000203}
204
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000205//
206// Call this function only if hextileAnySubrects bit is set in flags.
207// The buffer size should be enough to store at least that number of
208// bytes returned by buildTables() method.
209// Returns encoded data size, or zero if something is wrong.
210//
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000211
212int HEXTILE_SUBRECTS_TABLE::encode(rdr::U8 *dst)
213{
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000214 assert(m_numSubrects && (m_flags & hextileAnySubrects));
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000215
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000216 // Zero subrects counter
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000217 rdr::U8 *numSubrectsPtr = dst;
218 *dst++ = 0;
219
220 for (int i = 0; i < m_numSubrects; i++) {
221 if (m_colors[i] == m_background)
222 continue;
223
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000224 if (m_flags & hextileSubrectsColoured) {
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000225#if (BPP == 8)
226 *dst++ = m_colors[i];
227#elif (BPP == 16)
228 *dst++ = ((rdr::U8*)&m_colors[i])[0];
229 *dst++ = ((rdr::U8*)&m_colors[i])[1];
230#elif (BPP == 32)
231 *dst++ = ((rdr::U8*)&m_colors[i])[0];
232 *dst++ = ((rdr::U8*)&m_colors[i])[1];
233 *dst++ = ((rdr::U8*)&m_colors[i])[2];
234 *dst++ = ((rdr::U8*)&m_colors[i])[3];
235#endif
236 }
237 *dst++ = m_coords[i * 2];
238 *dst++ = m_coords[i * 2 + 1];
239
240 (*numSubrectsPtr)++;
241 }
242
243 return (dst - numSubrectsPtr);
244}
245
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000246//
247// Main encoding function.
248//
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000249
250void HEXTILE_ENCODE(const Rect& r, rdr::OutStream* os
251#ifdef EXTRA_ARGS
252 , EXTRA_ARGS
253#endif
254 )
255{
256 Rect t;
257 PIXEL_T buf[256];
258 PIXEL_T oldBg = 0, oldFg = 0;
259 bool oldBgValid = false;
260 bool oldFgValid = false;
261 rdr::U8 encoded[256*(BPP/8)];
262
263 HEXTILE_SUBRECTS_TABLE subrects;
264
265 for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
266
267 t.br.y = vncmin(r.br.y, t.tl.y + 16);
268
269 for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
270
271 t.br.x = vncmin(r.br.x, t.tl.x + 16);
272
273 GET_IMAGE_INTO_BUF(t,buf);
274
275 subrects.newTile(buf, t.width(), t.height());
276 int encodedLen = subrects.buildTables();
277
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000278 if (encodedLen >= t.width() * t.height() * (BPP/8)) {
279 os->writeU8(hextileRaw);
280 os->writeBytes(buf, t.width() * t.height() * (BPP/8));
281 oldBgValid = oldFgValid = false;
282 continue;
283 }
284
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000285 int tileType = subrects.getFlags();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000286 PIXEL_T bg = subrects.getBackground();
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000287 PIXEL_T fg = 0;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000288
289 if (!oldBgValid || oldBg != bg) {
290 tileType |= hextileBgSpecified;
291 oldBg = bg;
292 oldBgValid = true;
293 }
294
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000295 if (tileType & hextileAnySubrects) {
296 if (tileType & hextileSubrectsColoured) {
297 oldFgValid = false;
298 } else {
299 fg = subrects.getForeground();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000300 if (!oldFgValid || oldFg != fg) {
301 tileType |= hextileFgSpecified;
302 oldFg = fg;
303 oldFgValid = true;
304 }
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000305 }
306 int finalEncodedLen = subrects.encode(encoded);
307 assert(finalEncodedLen == encodedLen);
308 assert(finalEncodedLen <= 256*(BPP/8));
309 }
310
311 os->writeU8(tileType);
312 if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg);
313 if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg);
314 if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
315 }
316 }
317}
318
319#undef PIXEL_T
320#undef WRITE_PIXEL
321#undef HEXTILE_ENCODE
322
323#undef HEXTILE_SUBRECTS_TABLE
324}