blob: e1730ba540f5e4cac9642534200e6e109cb9cd1f [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +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 <rdr/OutStream.h>
28#include <rfb/hextileConstants.h>
Pierre Ossman65ad3222014-03-07 13:48:29 +010029#include <rfb/Palette.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +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)
44#define HEXTILE_TILE CONCAT2E(HextileTile,BPP)
45#define HEXTILE_ENCODE CONCAT2E(hextileEncodeBetter,BPP)
46
47//
48// This class analyzes a separate tile and encodes its subrectangles.
49//
50
51class HEXTILE_TILE {
52
53 public:
54
55 HEXTILE_TILE ();
56
57 //
58 // Initialize existing object instance with new tile data.
59 //
60 void newTile(const PIXEL_T *src, int w, int h);
61
62 //
63 // 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.
67 //
68 int getFlags() const { return m_flags; }
69
70 //
71 // 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 //
77 // Return optimal background.
78 //
79 int getBackground() const { return m_background; }
80
81 //
82 // Return foreground if flags include hextileSubrectsColoured.
83 //
84 int getForeground() const { return m_foreground; }
85
86 //
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
94 protected:
95
96 //
97 // Analyze the tile pixels, fill in all the data fields.
98 //
99 void analyze();
100
101 const PIXEL_T *m_tile;
102 int m_width;
103 int m_height;
104
105 int m_size;
106 int m_flags;
107 PIXEL_T m_background;
108 PIXEL_T m_foreground;
109
110 int m_numSubrects;
111 rdr::U8 m_coords[256 * 2];
112 PIXEL_T m_colors[256];
113
114 private:
115
116 bool m_processed[16][16];
Pierre Ossman65ad3222014-03-07 13:48:29 +0100117 Palette m_pal;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000118};
119
120HEXTILE_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),
Pierre Ossman65ad3222014-03-07 13:48:29 +0100123 m_numSubrects(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000124{
125}
126
127void HEXTILE_TILE::newTile(const PIXEL_T *src, int w, int h)
128{
129 m_tile = src;
130 m_width = w;
131 m_height = h;
132
133 analyze();
134}
135
136void HEXTILE_TILE::analyze()
137{
138 assert(m_tile && m_width && m_height);
139
140 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;
Pierre Ossman65ad3222014-03-07 13:48:29 +0100159 m_pal.clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000160 m_numSubrects = 0;
161
162 // 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));
167 m_pal.insert(color, 1);
168 m_numSubrects++;
169 }
170
171 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++) {
176 for (x = 0; x < m_width; x++) {
177 // Skip pixels that were processed earlier
178 if (m_processed[y][x]) {
179 continue;
180 }
181 // Determine dimensions of the horizontal subrect
182 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
198 // Save properties of this subrect
199 *colorsPtr++ = color;
200 *coordsPtr++ = (rdr::U8)((x << 4) | (y & 0x0F));
201 *coordsPtr++ = (rdr::U8)(((sw - 1) << 4) | ((sh - 1) & 0x0F));
202
Pierre Ossman65ad3222014-03-07 13:48:29 +0100203 if (!m_pal.insert(color, 1) || (m_pal.size() > (48 + 2 * BPP))) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000204 // Handle palette overflow
205 m_flags = hextileRaw;
206 m_size = 0;
207 return;
208 }
209
210 m_numSubrects++;
211
212 // Mark pixels of this subrect as processed, below this row
213 for (sy = y + 1; sy < y + sh; sy++) {
214 for (sx = x; sx < x + sw; sx++)
215 m_processed[sy][sx] = true;
216 }
217
218 // Skip processed pixels of this row
219 x += (sw - 1);
220 }
221 }
222
223 // Save number of colors in this tile (should be no less than 2)
Pierre Ossman65ad3222014-03-07 13:48:29 +0100224 int numColors = m_pal.size();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000225 assert(numColors >= 2);
226
Pierre Ossman65ad3222014-03-07 13:48:29 +0100227 m_background = (PIXEL_T)m_pal.getColour(0);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000228 m_flags = hextileAnySubrects;
229 int numSubrects = m_numSubrects - m_pal.getCount(0);
230
231 if (numColors == 2) {
232 // Monochrome tile
Pierre Ossman65ad3222014-03-07 13:48:29 +0100233 m_foreground = (PIXEL_T)m_pal.getColour(1);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000234 m_size = 1 + 2 * numSubrects;
235 } else {
236 // Colored tile
237 m_flags |= hextileSubrectsColoured;
238 m_size = 1 + (2 + (BPP/8)) * numSubrects;
239 }
240}
241
242void HEXTILE_TILE::encode(rdr::U8 *dst) const
243{
244 assert(m_numSubrects && (m_flags & hextileAnySubrects));
245
246 // Zero subrects counter
247 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
254 if (m_flags & hextileSubrectsColoured) {
255#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
273 assert(dst - numSubrectsPtr == m_size);
274}
275
276//
277// Main encoding function.
278//
279
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
293 HEXTILE_TILE tile;
294
295 for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
296
297 t.br.y = __rfbmin(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 = __rfbmin(r.br.x, t.tl.x + 16);
302
303 GET_IMAGE_INTO_BUF(t,buf);
304
305 tile.newTile(buf, t.width(), t.height());
306 int tileType = tile.getFlags();
307 int encodedLen = tile.getSize();
308
309 if ( (tileType & hextileRaw) != 0 ||
310 encodedLen >= t.width() * t.height() * (BPP/8)) {
311 os->writeU8(hextileRaw);
312 os->writeBytes(buf, t.width() * t.height() * (BPP/8));
313 oldBgValid = oldFgValid = false;
314 continue;
315 }
316
317 PIXEL_T bg = tile.getBackground();
318 PIXEL_T fg = 0;
319
320 if (!oldBgValid || oldBg != bg) {
321 tileType |= hextileBgSpecified;
322 oldBg = bg;
323 oldBgValid = true;
324 }
325
326 if (tileType & hextileAnySubrects) {
327 if (tileType & hextileSubrectsColoured) {
328 oldFgValid = false;
329 } else {
330 fg = tile.getForeground();
331 if (!oldFgValid || oldFg != fg) {
332 tileType |= hextileFgSpecified;
333 oldFg = fg;
334 oldFgValid = true;
335 }
336 }
337 tile.encode(encoded);
338 }
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
350#undef HEXTILE_TILE
351#undef HEXTILE_ENCODE
352}