blob: 7a0e168b40bc56732d83662aee0cf18e5b4223ee [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
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000027#include <rdr/OutStream.h>
28#include <rfb/hextileConstants.h>
Constantin Kaplinsky059aa632005-09-22 00:59:47 +000029#include <rfb/TightPalette.h>
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000030
31#include <assert.h>
32
33namespace rfb {
34
35// CONCAT2E concatenates its arguments, expanding them if they are macros
36
37#ifndef CONCAT2E
38#define CONCAT2(a,b) a##b
39#define CONCAT2E(a,b) CONCAT2(a,b)
40#endif
41
42#define PIXEL_T rdr::CONCAT2E(U,BPP)
43#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000044#define HEXTILE_TILE CONCAT2E(HextileTile,BPP)
Constantin Kaplinskyd6551172005-09-16 07:46:16 +000045#define HEXTILE_ENCODE CONCAT2E(hextileEncodeBetter,BPP)
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000046
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000047//
48// This class analyzes a separate tile and encodes its subrectangles.
49//
50
51class HEXTILE_TILE {
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000052
53 public:
54
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000055 HEXTILE_TILE ();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000056
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000057 //
Constantin Kaplinsky059aa632005-09-22 00:59:47 +000058 // Initialize existing object instance with new tile data.
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000059 //
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000060 void newTile(const PIXEL_T *src, int w, int h);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000061
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000062 //
Constantin Kaplinsky059aa632005-09-22 00:59:47 +000063 // Flags can include: hextileRaw, hextileAnySubrects and
64 // hextileSubrectsColoured. Note that if hextileRaw is set, other
65 // flags make no sense. Also, hextileSubrectsColoured is meaningful
66 // only when hextileAnySubrects is set as well.
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000067 //
Constantin Kaplinsky4292b842005-09-09 20:05:22 +000068 int getFlags() const { return m_flags; }
69
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000070 //
Constantin Kaplinsky059aa632005-09-22 00:59:47 +000071 // Returns the size of encoded subrects data, including subrect count.
72 // The size is zero if flags do not include hextileAnySubrects.
73 //
74 int getSize() const { return m_size; }
75
76 //
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000077 // Return optimal background.
78 //
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000079 int getBackground() const { return m_background; }
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000080
81 //
82 // Return foreground if flags include hextileSubrectsColoured.
83 //
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000084 int getForeground() const { return m_foreground; }
85
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000086 //
87 // Encode subrects. This function may be called only if
88 // hextileAnySubrects bit is set in flags. The buffer size should be
89 // big enough to store at least the number of bytes returned by the
90 // getSize() method.
91 //
92 void encode(rdr::U8* dst) const;
93
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +000094 protected:
95
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000096 //
Constantin Kaplinsky059aa632005-09-22 00:59:47 +000097 // Fill in m_coords[], m_colors[], m_pal and set m_numSubrects.
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000098 //
99 void buildTables();
100
101 //
102 // Fill in m_size, m_flags, m_background and m_foreground.
103 // Must be called after buildTables(), before encode().
104 //
105 void computeSize();
106
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000107 const PIXEL_T *m_tile;
108 int m_width;
109 int m_height;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000110
111 int m_size;
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000112 int m_flags;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000113 PIXEL_T m_background;
114 PIXEL_T m_foreground;
115
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000116 int m_numSubrects;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000117 rdr::U8 m_coords[256 * 2];
118 PIXEL_T m_colors[256];
119
120 private:
121
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000122 bool m_processed[16][16];
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000123 TightPalette m_pal;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000124};
125
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000126HEXTILE_TILE::HEXTILE_TILE()
127 : m_tile(NULL), m_width(0), m_height(0),
128 m_size(0), m_flags(0), m_background(0), m_foreground(0),
129 m_numSubrects(0)
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000130{
131}
132
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000133void HEXTILE_TILE::newTile(const PIXEL_T *src, int w, int h)
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000134{
135 m_tile = src;
136 m_width = w;
137 m_height = h;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000138
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000139 // NOTE: These two methods should always be called from this place
140 // only, and exactly in this order.
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000141 buildTables();
142 computeSize();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000143}
144
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000145void HEXTILE_TILE::buildTables()
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000146{
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000147 assert(m_tile && m_width && m_height);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000148
149 m_numSubrects = 0;
150 memset(m_processed, 0, 16 * 16 * sizeof(bool));
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000151 m_pal.reset();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000152
153 int x, y, sx, sy, sw, sh, max_x;
154 PIXEL_T color;
155 PIXEL_T *colorsPtr = &m_colors[0];
156 rdr::U8 *coordsPtr = &m_coords[0];
157
158 for (y = 0; y < m_height; y++) {
159 for (x = 0; x < m_width; x++) {
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000160 // Skip pixels that were processed earlier
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000161 if (m_processed[y][x]) {
162 continue;
163 }
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000164 // Determine dimensions of the horizontal subrect
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000165 color = m_tile[y * m_width + x];
166 for (sx = x + 1; sx < m_width; sx++) {
167 if (m_tile[y * m_width + sx] != color)
168 break;
169 }
170 sw = sx - x;
171 max_x = sx;
172 for (sy = y + 1; sy < m_height; sy++) {
173 for (sx = x; sx < max_x; sx++) {
174 if (m_tile[sy * m_width + sx] != color)
175 goto done;
176 }
177 }
178 done:
179 sh = sy - y;
180
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000181 // Save properties of this subrect
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000182 *colorsPtr++ = color;
183 *coordsPtr++ = (rdr::U8)((x << 4) | (y & 0x0F));
184 *coordsPtr++ = (rdr::U8)(((sw - 1) << 4) | ((sh - 1) & 0x0F));
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000185
186 if (m_pal.insert(color, 1, BPP) == 0)
187 return; // palette overflow
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000188
189 m_numSubrects++;
190
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000191 // Mark pixels of this subrect as processed, below this row
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000192 for (sy = y + 1; sy < y + sh; sy++) {
193 for (sx = x; sx < x + sw; sx++)
194 m_processed[sy][sx] = true;
195 }
196
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000197 // Skip processed pixels of this row
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000198 x += (sw - 1);
199 }
200 }
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000201}
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000202
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000203void HEXTILE_TILE::computeSize()
204{
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000205 // Save the number of colors
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000206 int numColors = m_pal.getNumColors();
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000207
208 // Handle solid tile
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000209 if (numColors == 1) {
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000210 m_background = m_tile[0];
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000211 m_flags = 0;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000212 m_size = 0;
213 return;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000214 }
215
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000216 // Handle monochrome tile - choose background and foreground
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000217 if (numColors == 2) {
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000218 int bgCount = m_pal.getCount(0);
219 m_background = (PIXEL_T)m_pal.getEntry(0);
220 m_foreground = (PIXEL_T)m_pal.getEntry(1);
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000221
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000222 m_flags = hextileAnySubrects;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000223 m_size = 1 + 2 * (m_numSubrects - bgCount);
224 return;
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000225 }
226
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000227 // Handle raw-encoded tile (there was palette overflow)
228 if (numColors == 0) {
229 m_flags = hextileRaw;
230 m_size = 0;
231 return;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000232 }
233
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000234 // Handle colored tile - choose the best background color
235 int bgCount = m_pal.getCount(0);
236 m_background = (PIXEL_T)m_pal.getEntry(0);
237
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000238 m_flags = hextileAnySubrects | hextileSubrectsColoured;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000239 m_size = 1 + (2 + (BPP/8)) * (m_numSubrects - bgCount);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000240}
241
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000242void HEXTILE_TILE::encode(rdr::U8 *dst) const
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000243{
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000244 assert(m_numSubrects && (m_flags & hextileAnySubrects));
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000245
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000246 // Zero subrects counter
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000247 rdr::U8 *numSubrectsPtr = dst;
248 *dst++ = 0;
249
250 for (int i = 0; i < m_numSubrects; i++) {
251 if (m_colors[i] == m_background)
252 continue;
253
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000254 if (m_flags & hextileSubrectsColoured) {
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000255#if (BPP == 8)
256 *dst++ = m_colors[i];
257#elif (BPP == 16)
258 *dst++ = ((rdr::U8*)&m_colors[i])[0];
259 *dst++ = ((rdr::U8*)&m_colors[i])[1];
260#elif (BPP == 32)
261 *dst++ = ((rdr::U8*)&m_colors[i])[0];
262 *dst++ = ((rdr::U8*)&m_colors[i])[1];
263 *dst++ = ((rdr::U8*)&m_colors[i])[2];
264 *dst++ = ((rdr::U8*)&m_colors[i])[3];
265#endif
266 }
267 *dst++ = m_coords[i * 2];
268 *dst++ = m_coords[i * 2 + 1];
269
270 (*numSubrectsPtr)++;
271 }
272
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000273 assert(dst - numSubrectsPtr == m_size);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000274}
275
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000276//
277// Main encoding function.
278//
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000279
280void HEXTILE_ENCODE(const Rect& r, rdr::OutStream* os
281#ifdef EXTRA_ARGS
282 , EXTRA_ARGS
283#endif
284 )
285{
286 Rect t;
287 PIXEL_T buf[256];
288 PIXEL_T oldBg = 0, oldFg = 0;
289 bool oldBgValid = false;
290 bool oldFgValid = false;
291 rdr::U8 encoded[256*(BPP/8)];
292
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000293 HEXTILE_TILE tile;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000294
295 for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
296
297 t.br.y = vncmin(r.br.y, t.tl.y + 16);
298
299 for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
300
301 t.br.x = vncmin(r.br.x, t.tl.x + 16);
302
303 GET_IMAGE_INTO_BUF(t,buf);
304
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000305 tile.newTile(buf, t.width(), t.height());
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000306 int tileType = tile.getFlags();
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000307 int encodedLen = tile.getSize();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000308
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000309 if ( (tileType & hextileRaw) != 0 ||
310 encodedLen >= t.width() * t.height() * (BPP/8)) {
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000311 os->writeU8(hextileRaw);
312 os->writeBytes(buf, t.width() * t.height() * (BPP/8));
313 oldBgValid = oldFgValid = false;
314 continue;
315 }
316
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000317 PIXEL_T bg = tile.getBackground();
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000318 PIXEL_T fg = 0;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000319
320 if (!oldBgValid || oldBg != bg) {
321 tileType |= hextileBgSpecified;
322 oldBg = bg;
323 oldBgValid = true;
324 }
325
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000326 if (tileType & hextileAnySubrects) {
327 if (tileType & hextileSubrectsColoured) {
328 oldFgValid = false;
329 } else {
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000330 fg = tile.getForeground();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000331 if (!oldFgValid || oldFg != fg) {
332 tileType |= hextileFgSpecified;
333 oldFg = fg;
334 oldFgValid = true;
335 }
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000336 }
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000337 tile.encode(encoded);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000338 }
339
340 os->writeU8(tileType);
341 if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg);
342 if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg);
343 if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
344 }
345 }
346}
347
348#undef PIXEL_T
349#undef WRITE_PIXEL
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000350#undef HEXTILE_TILE
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000351#undef HEXTILE_ENCODE
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000352}