blob: 59df102a5132c8a8b6c2d0fdace87cc51c579ac8 [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 Kaplinsky681fd672005-09-22 04:10:07 +000097 // Analyze the tile pixels, fill in all the data fields.
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +000098 //
Constantin Kaplinsky681fd672005-09-22 04:10:07 +000099 void analyze();
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000100
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000101 const PIXEL_T *m_tile;
102 int m_width;
103 int m_height;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000104
105 int m_size;
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000106 int m_flags;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000107 PIXEL_T m_background;
108 PIXEL_T m_foreground;
109
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000110 int m_numSubrects;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000111 rdr::U8 m_coords[256 * 2];
112 PIXEL_T m_colors[256];
113
114 private:
115
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000116 bool m_processed[16][16];
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000117 TightPalette m_pal;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000118};
119
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000120HEXTILE_TILE::HEXTILE_TILE()
121 : m_tile(NULL), m_width(0), m_height(0),
122 m_size(0), m_flags(0), m_background(0), m_foreground(0),
Constantin Kaplinsky2aa8cd42005-09-22 01:27:08 +0000123 m_numSubrects(0), m_pal(48 + 2 * BPP)
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000124{
125}
126
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000127void HEXTILE_TILE::newTile(const PIXEL_T *src, int w, int h)
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000128{
129 m_tile = src;
130 m_width = w;
131 m_height = h;
Constantin Kaplinskyd5d85902005-09-16 13:59:29 +0000132
Constantin Kaplinsky681fd672005-09-22 04:10:07 +0000133 analyze();
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000134}
135
Constantin Kaplinsky681fd672005-09-22 04:10:07 +0000136void HEXTILE_TILE::analyze()
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000137{
Constantin Kaplinsky4292b842005-09-09 20:05:22 +0000138 assert(m_tile && m_width && m_height);
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000139
Constantin Kaplinsky681fd672005-09-22 04:10:07 +0000140 const PIXEL_T *ptr = m_tile;
141 const PIXEL_T *end = &m_tile[m_width * m_height];
142 PIXEL_T color = *ptr++;
143 while (ptr != end && *ptr == color)
144 ptr++;
145
146 // Handle solid tile
147 if (ptr == end) {
148 m_background = m_tile[0];
149 m_flags = 0;
150 m_size = 0;
151 return;
152 }
153
154 // Compute number of complete rows of the same color, at the top
155 int y = (ptr - m_tile) / m_width;
156
157 PIXEL_T *colorsPtr = m_colors;
158 rdr::U8 *coordsPtr = m_coords;
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000159 m_pal.reset();
Constantin Kaplinsky681fd672005-09-22 04:10:07 +0000160 m_numSubrects = 0;
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000161
Constantin Kaplinsky681fd672005-09-22 04:10:07 +0000162 // Have we found the first subrect already?
163 if (y > 0) {
164 *colorsPtr++ = color;
165 *coordsPtr++ = 0;
166 *coordsPtr++ = (rdr::U8)(((m_width - 1) << 4) | ((y - 1) & 0x0F));
Constantin Kaplinsky51cc9762005-09-22 05:38:56 +0000167 m_pal.insert(color, 1);
Constantin Kaplinsky681fd672005-09-22 04:10:07 +0000168 m_numSubrects++;
169 }
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000170
Constantin Kaplinsky681fd672005-09-22 04:10:07 +0000171 memset(m_processed, 0, 16 * 16 * sizeof(bool));
172
173 int x, sx, sy, sw, sh, max_x;
174
175 for (; y < m_height; y++) {
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000176 for (x = 0; x < m_width; x++) {
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000177 // Skip pixels that were processed earlier
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000178 if (m_processed[y][x]) {
179 continue;
180 }
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000181 // Determine dimensions of the horizontal subrect
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000182 color = m_tile[y * m_width + x];
183 for (sx = x + 1; sx < m_width; sx++) {
184 if (m_tile[y * m_width + sx] != color)
185 break;
186 }
187 sw = sx - x;
188 max_x = sx;
189 for (sy = y + 1; sy < m_height; sy++) {
190 for (sx = x; sx < max_x; sx++) {
191 if (m_tile[sy * m_width + sx] != color)
192 goto done;
193 }
194 }
195 done:
196 sh = sy - y;
197
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000198 // Save properties of this subrect
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000199 *colorsPtr++ = color;
200 *coordsPtr++ = (rdr::U8)((x << 4) | (y & 0x0F));
201 *coordsPtr++ = (rdr::U8)(((sw - 1) << 4) | ((sh - 1) & 0x0F));
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000202
Constantin Kaplinsky51cc9762005-09-22 05:38:56 +0000203 if (m_pal.insert(color, 1) == 0) {
Constantin Kaplinsky681fd672005-09-22 04:10:07 +0000204 // Handle palette overflow
205 m_flags = hextileRaw;
206 m_size = 0;
207 return;
208 }
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000209
210 m_numSubrects++;
211
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000212 // Mark pixels of this subrect as processed, below this row
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000213 for (sy = y + 1; sy < y + sh; sy++) {
214 for (sx = x; sx < x + sw; sx++)
215 m_processed[sy][sx] = true;
216 }
217
Constantin Kaplinskyd6551172005-09-16 07:46:16 +0000218 // Skip processed pixels of this row
Constantin Kaplinskyd10721b2005-09-09 19:52:17 +0000219 x += (sw - 1);
220 }
221 }
222
Constantin Kaplinsky681fd672005-09-22 04:10:07 +0000223 // Save number of colors in this tile (should be no less than 2)
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000224 int numColors = m_pal.getNumColors();
Constantin Kaplinsky681fd672005-09-22 04:10:07 +0000225 assert(numColors >= 2);
Constantin Kaplinskydada02d2005-09-09 20:01:05 +0000226
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000227 m_background = (PIXEL_T)m_pal.getEntry(0);
Constantin Kaplinsky681fd672005-09-22 04:10:07 +0000228 m_flags = hextileAnySubrects;
229 int numSubrects = m_numSubrects - m_pal.getCount(0);
Constantin Kaplinsky059aa632005-09-22 00:59:47 +0000230
Constantin Kaplinsky681fd672005-09-22 04:10:07 +0000231 if (numColors == 2) {
232 // Monochrome tile
233 m_foreground = (PIXEL_T)m_pal.getEntry(1);
234 m_size = 1 + 2 * numSubrects;
235 } else {
236 // Colored tile
237 m_flags |= hextileSubrectsColoured;
238 m_size = 1 + (2 + (BPP/8)) * numSubrects;
239 }
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}