blob: bc9dcacac4d4b3d74d1e0af87f3057e77e90c4ac [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//
Pierre Ossmanbcc295e2014-02-12 13:16:43 +010022// This file is #included after having set the following macro:
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000023// BPP - 8, 16 or 32
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000024
25#include <rdr/OutStream.h>
26#include <rfb/hextileConstants.h>
Pierre Ossman65ad3222014-03-07 13:48:29 +010027#include <rfb/Palette.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000028
29#include <assert.h>
30
31namespace rfb {
32
33// CONCAT2E concatenates its arguments, expanding them if they are macros
34
35#ifndef CONCAT2E
36#define CONCAT2(a,b) a##b
37#define CONCAT2E(a,b) CONCAT2(a,b)
38#endif
39
40#define PIXEL_T rdr::CONCAT2E(U,BPP)
41#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
42#define HEXTILE_TILE CONCAT2E(HextileTile,BPP)
43#define HEXTILE_ENCODE CONCAT2E(hextileEncodeBetter,BPP)
44
45//
46// This class analyzes a separate tile and encodes its subrectangles.
47//
48
49class HEXTILE_TILE {
50
51 public:
52
53 HEXTILE_TILE ();
54
55 //
56 // Initialize existing object instance with new tile data.
57 //
58 void newTile(const PIXEL_T *src, int w, int h);
59
60 //
61 // Flags can include: hextileRaw, hextileAnySubrects and
62 // hextileSubrectsColoured. Note that if hextileRaw is set, other
63 // flags make no sense. Also, hextileSubrectsColoured is meaningful
64 // only when hextileAnySubrects is set as well.
65 //
66 int getFlags() const { return m_flags; }
67
68 //
69 // Returns the size of encoded subrects data, including subrect count.
70 // The size is zero if flags do not include hextileAnySubrects.
71 //
72 int getSize() const { return m_size; }
73
74 //
75 // Return optimal background.
76 //
77 int getBackground() const { return m_background; }
78
79 //
80 // Return foreground if flags include hextileSubrectsColoured.
81 //
82 int getForeground() const { return m_foreground; }
83
84 //
85 // Encode subrects. This function may be called only if
86 // hextileAnySubrects bit is set in flags. The buffer size should be
87 // big enough to store at least the number of bytes returned by the
88 // getSize() method.
89 //
90 void encode(rdr::U8* dst) const;
91
92 protected:
93
94 //
95 // Analyze the tile pixels, fill in all the data fields.
96 //
97 void analyze();
98
99 const PIXEL_T *m_tile;
100 int m_width;
101 int m_height;
102
103 int m_size;
104 int m_flags;
105 PIXEL_T m_background;
106 PIXEL_T m_foreground;
107
108 int m_numSubrects;
109 rdr::U8 m_coords[256 * 2];
110 PIXEL_T m_colors[256];
111
112 private:
113
114 bool m_processed[16][16];
Pierre Ossman65ad3222014-03-07 13:48:29 +0100115 Palette m_pal;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000116};
117
118HEXTILE_TILE::HEXTILE_TILE()
119 : m_tile(NULL), m_width(0), m_height(0),
120 m_size(0), m_flags(0), m_background(0), m_foreground(0),
Pierre Ossman65ad3222014-03-07 13:48:29 +0100121 m_numSubrects(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000122{
123}
124
125void HEXTILE_TILE::newTile(const PIXEL_T *src, int w, int h)
126{
127 m_tile = src;
128 m_width = w;
129 m_height = h;
130
131 analyze();
132}
133
134void HEXTILE_TILE::analyze()
135{
136 assert(m_tile && m_width && m_height);
137
138 const PIXEL_T *ptr = m_tile;
139 const PIXEL_T *end = &m_tile[m_width * m_height];
140 PIXEL_T color = *ptr++;
141 while (ptr != end && *ptr == color)
142 ptr++;
143
144 // Handle solid tile
145 if (ptr == end) {
146 m_background = m_tile[0];
147 m_flags = 0;
148 m_size = 0;
149 return;
150 }
151
152 // Compute number of complete rows of the same color, at the top
153 int y = (ptr - m_tile) / m_width;
154
155 PIXEL_T *colorsPtr = m_colors;
156 rdr::U8 *coordsPtr = m_coords;
Pierre Ossman65ad3222014-03-07 13:48:29 +0100157 m_pal.clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000158 m_numSubrects = 0;
159
160 // Have we found the first subrect already?
161 if (y > 0) {
162 *colorsPtr++ = color;
163 *coordsPtr++ = 0;
164 *coordsPtr++ = (rdr::U8)(((m_width - 1) << 4) | ((y - 1) & 0x0F));
165 m_pal.insert(color, 1);
166 m_numSubrects++;
167 }
168
169 memset(m_processed, 0, 16 * 16 * sizeof(bool));
170
171 int x, sx, sy, sw, sh, max_x;
172
173 for (; y < m_height; y++) {
174 for (x = 0; x < m_width; x++) {
175 // Skip pixels that were processed earlier
176 if (m_processed[y][x]) {
177 continue;
178 }
179 // Determine dimensions of the horizontal subrect
180 color = m_tile[y * m_width + x];
181 for (sx = x + 1; sx < m_width; sx++) {
182 if (m_tile[y * m_width + sx] != color)
183 break;
184 }
185 sw = sx - x;
186 max_x = sx;
187 for (sy = y + 1; sy < m_height; sy++) {
188 for (sx = x; sx < max_x; sx++) {
189 if (m_tile[sy * m_width + sx] != color)
190 goto done;
191 }
192 }
193 done:
194 sh = sy - y;
195
196 // Save properties of this subrect
197 *colorsPtr++ = color;
198 *coordsPtr++ = (rdr::U8)((x << 4) | (y & 0x0F));
199 *coordsPtr++ = (rdr::U8)(((sw - 1) << 4) | ((sh - 1) & 0x0F));
200
Pierre Ossman65ad3222014-03-07 13:48:29 +0100201 if (!m_pal.insert(color, 1) || (m_pal.size() > (48 + 2 * BPP))) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000202 // Handle palette overflow
203 m_flags = hextileRaw;
204 m_size = 0;
205 return;
206 }
207
208 m_numSubrects++;
209
210 // Mark pixels of this subrect as processed, below this row
211 for (sy = y + 1; sy < y + sh; sy++) {
212 for (sx = x; sx < x + sw; sx++)
213 m_processed[sy][sx] = true;
214 }
215
216 // Skip processed pixels of this row
217 x += (sw - 1);
218 }
219 }
220
221 // Save number of colors in this tile (should be no less than 2)
Pierre Ossman65ad3222014-03-07 13:48:29 +0100222 int numColors = m_pal.size();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000223 assert(numColors >= 2);
224
Pierre Ossman65ad3222014-03-07 13:48:29 +0100225 m_background = (PIXEL_T)m_pal.getColour(0);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000226 m_flags = hextileAnySubrects;
227 int numSubrects = m_numSubrects - m_pal.getCount(0);
228
229 if (numColors == 2) {
230 // Monochrome tile
Pierre Ossman65ad3222014-03-07 13:48:29 +0100231 m_foreground = (PIXEL_T)m_pal.getColour(1);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000232 m_size = 1 + 2 * numSubrects;
233 } else {
234 // Colored tile
235 m_flags |= hextileSubrectsColoured;
236 m_size = 1 + (2 + (BPP/8)) * numSubrects;
237 }
238}
239
240void HEXTILE_TILE::encode(rdr::U8 *dst) const
241{
242 assert(m_numSubrects && (m_flags & hextileAnySubrects));
243
244 // Zero subrects counter
245 rdr::U8 *numSubrectsPtr = dst;
246 *dst++ = 0;
247
248 for (int i = 0; i < m_numSubrects; i++) {
249 if (m_colors[i] == m_background)
250 continue;
251
252 if (m_flags & hextileSubrectsColoured) {
253#if (BPP == 8)
254 *dst++ = m_colors[i];
255#elif (BPP == 16)
256 *dst++ = ((rdr::U8*)&m_colors[i])[0];
257 *dst++ = ((rdr::U8*)&m_colors[i])[1];
258#elif (BPP == 32)
259 *dst++ = ((rdr::U8*)&m_colors[i])[0];
260 *dst++ = ((rdr::U8*)&m_colors[i])[1];
261 *dst++ = ((rdr::U8*)&m_colors[i])[2];
262 *dst++ = ((rdr::U8*)&m_colors[i])[3];
263#endif
264 }
265 *dst++ = m_coords[i * 2];
266 *dst++ = m_coords[i * 2 + 1];
267
268 (*numSubrectsPtr)++;
269 }
270
271 assert(dst - numSubrectsPtr == m_size);
272}
273
274//
275// Main encoding function.
276//
277
Pierre Ossmanc0397262014-03-14 15:59:46 +0100278void HEXTILE_ENCODE(rdr::OutStream* os, const PixelBuffer* pb)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000279{
280 Rect t;
281 PIXEL_T buf[256];
282 PIXEL_T oldBg = 0, oldFg = 0;
283 bool oldBgValid = false;
284 bool oldFgValid = false;
285 rdr::U8 encoded[256*(BPP/8)];
286
287 HEXTILE_TILE tile;
288
Pierre Ossmanc0397262014-03-14 15:59:46 +0100289 for (t.tl.y = 0; t.tl.y < pb->height(); t.tl.y += 16) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000290
Pierre Ossmanc0397262014-03-14 15:59:46 +0100291 t.br.y = __rfbmin(pb->height(), t.tl.y + 16);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000292
Pierre Ossmanc0397262014-03-14 15:59:46 +0100293 for (t.tl.x = 0; t.tl.x < pb->width(); t.tl.x += 16) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000294
Pierre Ossmanc0397262014-03-14 15:59:46 +0100295 t.br.x = __rfbmin(pb->width(), t.tl.x + 16);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000296
Pierre Ossmanc0397262014-03-14 15:59:46 +0100297 pb->getImage(buf, t);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000298
299 tile.newTile(buf, t.width(), t.height());
300 int tileType = tile.getFlags();
301 int encodedLen = tile.getSize();
302
303 if ( (tileType & hextileRaw) != 0 ||
304 encodedLen >= t.width() * t.height() * (BPP/8)) {
305 os->writeU8(hextileRaw);
306 os->writeBytes(buf, t.width() * t.height() * (BPP/8));
307 oldBgValid = oldFgValid = false;
308 continue;
309 }
310
311 PIXEL_T bg = tile.getBackground();
312 PIXEL_T fg = 0;
313
314 if (!oldBgValid || oldBg != bg) {
315 tileType |= hextileBgSpecified;
316 oldBg = bg;
317 oldBgValid = true;
318 }
319
320 if (tileType & hextileAnySubrects) {
321 if (tileType & hextileSubrectsColoured) {
322 oldFgValid = false;
323 } else {
324 fg = tile.getForeground();
325 if (!oldFgValid || oldFg != fg) {
326 tileType |= hextileFgSpecified;
327 oldFg = fg;
328 oldFgValid = true;
329 }
330 }
331 tile.encode(encoded);
332 }
333
334 os->writeU8(tileType);
335 if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg);
336 if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg);
337 if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
338 }
339 }
340}
341
342#undef PIXEL_T
343#undef WRITE_PIXEL
344#undef HEXTILE_TILE
345#undef HEXTILE_ENCODE
346}