blob: 9e037927bf6a65f921b2be0788ebe9c44ee78f4f [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 Kaplinskyd5d85902005-09-16 13:59:29 +000045#define HEXTILE_TILE CONCAT2E(HextileTile,BPP)
Constantin Kaplinskyd6551172005-09-16 07:46:16 +000046#define HEXTILE_ENCODE CONCAT2E(hextileEncodeBetter,BPP)
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000047
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000048//
49// This class analyzes a separate tile and encodes its subrectangles.
50//
51
52class HEXTILE_TILE {
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000053
54 public:
55
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000056 HEXTILE_TILE ();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000057
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000058 //
59 // Initialize the object with new tile data.
60 //
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000061 void newTile(const PIXEL_T *src, int w, int h);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000062
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000063 //
64 // Returns the size of encoded subrects data, including subrects count.
65 //
66 int getSize() const { return m_size; }
67
68 //
69 // Flags can include: hextileAnySubrects, hextileSubrectsColoured.
70 //
Constantin Kaplinsky4292b842005-09-09 20:05:22 +000071 int getFlags() const { return m_flags; }
72
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000073 //
74 // Return optimal background.
75 //
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000076 int getBackground() const { return m_background; }
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000077
78 //
79 // Return foreground if flags include hextileSubrectsColoured.
80 //
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000081 int getForeground() const { return m_foreground; }
82
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000083 //
84 // Encode subrects. This function may be called only if
85 // hextileAnySubrects bit is set in flags. The buffer size should be
86 // big enough to store at least the number of bytes returned by the
87 // getSize() method.
88 //
89 void encode(rdr::U8* dst) const;
90
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000091 protected:
92
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000093 //
94 // Fill in m_coords[], m_colors[], m_counts[] and set m_numSubrects.
95 //
96 void buildTables();
97
98 //
99 // Fill in m_size, m_flags, m_background and m_foreground.
100 // Must be called after buildTables(), before encode().
101 //
102 void computeSize();
103
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000104 const PIXEL_T *m_tile;
105 int m_width;
106 int m_height;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000107
108 int m_size;
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000109 int m_flags;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000110 PIXEL_T m_background;
111 PIXEL_T m_foreground;
112
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000113 int m_numSubrects;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000114 rdr::U8 m_coords[256 * 2];
115 PIXEL_T m_colors[256];
116
117 private:
118
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000119 // DEBUG: Check performance for: (1) U8[] and (2) dyn.allocated.
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000120 bool m_processed[16][16];
121
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000122 // FIXME: Use array for (BPP == 8)?
123 // DEBUG: Use own hashing like in ZRLE?
Constantin Kaplinsky2a90a452005-09-09 19:57:49 +0000124 std::map<PIXEL_T,short> m_counts;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000125};
126
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000127HEXTILE_TILE::HEXTILE_TILE()
128 : m_tile(NULL), m_width(0), m_height(0),
129 m_size(0), m_flags(0), m_background(0), m_foreground(0),
130 m_numSubrects(0)
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000131{
132}
133
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000134void HEXTILE_TILE::newTile(const PIXEL_T *src, int w, int h)
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000135{
136 m_tile = src;
137 m_width = w;
138 m_height = h;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000139
140 buildTables();
141 computeSize();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000142}
143
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000144void HEXTILE_TILE::buildTables()
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000145{
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000146 assert(m_tile && m_width && m_height);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000147
148 m_numSubrects = 0;
149 memset(m_processed, 0, 16 * 16 * sizeof(bool));
150 m_counts.clear();
151
152 int x, y, sx, sy, sw, sh, max_x;
153 PIXEL_T color;
154 PIXEL_T *colorsPtr = &m_colors[0];
155 rdr::U8 *coordsPtr = &m_coords[0];
156
157 for (y = 0; y < m_height; y++) {
158 for (x = 0; x < m_width; x++) {
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000159 // Skip pixels that were processed earlier
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000160 if (m_processed[y][x]) {
161 continue;
162 }
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000163 // Determine dimensions of the horizontal subrect
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000164 color = m_tile[y * m_width + x];
165 for (sx = x + 1; sx < m_width; sx++) {
166 if (m_tile[y * m_width + sx] != color)
167 break;
168 }
169 sw = sx - x;
170 max_x = sx;
171 for (sy = y + 1; sy < m_height; sy++) {
172 for (sx = x; sx < max_x; sx++) {
173 if (m_tile[sy * m_width + sx] != color)
174 goto done;
175 }
176 }
177 done:
178 sh = sy - y;
179
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000180 // Save properties of this subrect
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000181 *colorsPtr++ = color;
182 *coordsPtr++ = (rdr::U8)((x << 4) | (y & 0x0F));
183 *coordsPtr++ = (rdr::U8)(((sw - 1) << 4) | ((sh - 1) & 0x0F));
184 m_counts[color] += 1;
185
186 m_numSubrects++;
187
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000188 // Mark pixels of this subrect as processed, below this row
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000189 for (sy = y + 1; sy < y + sh; sy++) {
190 for (sx = x; sx < x + sw; sx++)
191 m_processed[sy][sx] = true;
192 }
193
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000194 // Skip processed pixels of this row
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000195 x += (sw - 1);
196 }
197 }
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000198}
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000199
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000200void HEXTILE_TILE::computeSize()
201{
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000202 // Save the number of colors
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000203 int numColors = m_counts.size();
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000204
205 // Handle solid tile
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000206 if (numColors == 1) {
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000207 m_background = m_tile[0];
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000208 m_flags = 0;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000209 m_size = 0;
210 return;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000211 }
212
Constantin Kaplinsky2a90a452005-09-09 19:57:49 +0000213 std::map<PIXEL_T,short>::iterator i;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000214
215 // Handle monochrome tile - choose background and foreground
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000216 if (numColors == 2) {
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000217 i = m_counts.begin();
218 m_background = i->first;
219 int bgCount = i->second;
220 i++;
221 if (i->second <= bgCount) {
222 m_foreground = i->first;
223 } else {
224 m_foreground = m_background;
225 m_background = i->first;
226 bgCount = i->second;;
227 }
228
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000229 m_flags = hextileAnySubrects;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000230 m_size = 1 + 2 * (m_numSubrects - bgCount);
231 return;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000232 }
233
234 // Handle colored tile - choose the best background color
235 int bgCount = 0, count;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000236 PIXEL_T color;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000237 for (i = m_counts.begin(); i != m_counts.end(); i++) {
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000238 color = i->first;
239 count = i->second;
240 if (count > bgCount) {
241 bgCount = count;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000242 m_background = color;
243 }
244 }
245
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000246 m_flags = hextileAnySubrects | hextileSubrectsColoured;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000247 m_size = 1 + (2 + (BPP/8)) * (m_numSubrects - bgCount);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000248}
249
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000250void HEXTILE_TILE::encode(rdr::U8 *dst) const
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000251{
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000252 assert(m_numSubrects && (m_flags & hextileAnySubrects));
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000253
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000254 // Zero subrects counter
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000255 rdr::U8 *numSubrectsPtr = dst;
256 *dst++ = 0;
257
258 for (int i = 0; i < m_numSubrects; i++) {
259 if (m_colors[i] == m_background)
260 continue;
261
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000262 if (m_flags & hextileSubrectsColoured) {
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000263#if (BPP == 8)
264 *dst++ = m_colors[i];
265#elif (BPP == 16)
266 *dst++ = ((rdr::U8*)&m_colors[i])[0];
267 *dst++ = ((rdr::U8*)&m_colors[i])[1];
268#elif (BPP == 32)
269 *dst++ = ((rdr::U8*)&m_colors[i])[0];
270 *dst++ = ((rdr::U8*)&m_colors[i])[1];
271 *dst++ = ((rdr::U8*)&m_colors[i])[2];
272 *dst++ = ((rdr::U8*)&m_colors[i])[3];
273#endif
274 }
275 *dst++ = m_coords[i * 2];
276 *dst++ = m_coords[i * 2 + 1];
277
278 (*numSubrectsPtr)++;
279 }
280
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000281 assert(dst - numSubrectsPtr == m_size);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000282}
283
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000284//
285// Main encoding function.
286//
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000287
288void HEXTILE_ENCODE(const Rect& r, rdr::OutStream* os
289#ifdef EXTRA_ARGS
290 , EXTRA_ARGS
291#endif
292 )
293{
294 Rect t;
295 PIXEL_T buf[256];
296 PIXEL_T oldBg = 0, oldFg = 0;
297 bool oldBgValid = false;
298 bool oldFgValid = false;
299 rdr::U8 encoded[256*(BPP/8)];
300
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000301 HEXTILE_TILE tile;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000302
303 for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
304
305 t.br.y = vncmin(r.br.y, t.tl.y + 16);
306
307 for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
308
309 t.br.x = vncmin(r.br.x, t.tl.x + 16);
310
311 GET_IMAGE_INTO_BUF(t,buf);
312
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000313 tile.newTile(buf, t.width(), t.height());
314 int encodedLen = tile.getSize();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000315
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000316 if (encodedLen >= t.width() * t.height() * (BPP/8)) {
317 os->writeU8(hextileRaw);
318 os->writeBytes(buf, t.width() * t.height() * (BPP/8));
319 oldBgValid = oldFgValid = false;
320 continue;
321 }
322
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000323 int tileType = tile.getFlags();
324 PIXEL_T bg = tile.getBackground();
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000325 PIXEL_T fg = 0;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000326
327 if (!oldBgValid || oldBg != bg) {
328 tileType |= hextileBgSpecified;
329 oldBg = bg;
330 oldBgValid = true;
331 }
332
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000333 if (tileType & hextileAnySubrects) {
334 if (tileType & hextileSubrectsColoured) {
335 oldFgValid = false;
336 } else {
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000337 fg = tile.getForeground();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000338 if (!oldFgValid || oldFg != fg) {
339 tileType |= hextileFgSpecified;
340 oldFg = fg;
341 oldFgValid = true;
342 }
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000343 }
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000344 tile.encode(encoded);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000345 }
346
347 os->writeU8(tileType);
348 if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg);
349 if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg);
350 if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
351 }
352 }
353}
354
355#undef PIXEL_T
356#undef WRITE_PIXEL
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000357#undef HEXTILE_TILE
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000358#undef HEXTILE_ENCODE
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000359}