summaryrefslogtreecommitdiffstats
path: root/common/rfb/hextileEncodeBetter.h
diff options
context:
space:
mode:
Diffstat (limited to 'common/rfb/hextileEncodeBetter.h')
-rw-r--r--common/rfb/hextileEncodeBetter.h352
1 files changed, 352 insertions, 0 deletions
diff --git a/common/rfb/hextileEncodeBetter.h b/common/rfb/hextileEncodeBetter.h
new file mode 100644
index 00000000..2b6b160a
--- /dev/null
+++ b/common/rfb/hextileEncodeBetter.h
@@ -0,0 +1,352 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
+ * Copyright (C) 2005 Constantin Kaplinsky. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// Hextile encoding function.
+//
+// This file is #included after having set the following macros:
+// BPP - 8, 16 or 32
+// EXTRA_ARGS - optional extra arguments
+// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer
+
+#include <rdr/OutStream.h>
+#include <rfb/hextileConstants.h>
+#include <rfb/TightPalette.h>
+
+#include <assert.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
+#define HEXTILE_TILE CONCAT2E(HextileTile,BPP)
+#define HEXTILE_ENCODE CONCAT2E(hextileEncodeBetter,BPP)
+
+//
+// This class analyzes a separate tile and encodes its subrectangles.
+//
+
+class HEXTILE_TILE {
+
+ public:
+
+ HEXTILE_TILE ();
+
+ //
+ // Initialize existing object instance with new tile data.
+ //
+ void newTile(const PIXEL_T *src, int w, int h);
+
+ //
+ // Flags can include: hextileRaw, hextileAnySubrects and
+ // hextileSubrectsColoured. Note that if hextileRaw is set, other
+ // flags make no sense. Also, hextileSubrectsColoured is meaningful
+ // only when hextileAnySubrects is set as well.
+ //
+ int getFlags() const { return m_flags; }
+
+ //
+ // Returns the size of encoded subrects data, including subrect count.
+ // The size is zero if flags do not include hextileAnySubrects.
+ //
+ int getSize() const { return m_size; }
+
+ //
+ // Return optimal background.
+ //
+ int getBackground() const { return m_background; }
+
+ //
+ // Return foreground if flags include hextileSubrectsColoured.
+ //
+ int getForeground() const { return m_foreground; }
+
+ //
+ // Encode subrects. This function may be called only if
+ // hextileAnySubrects bit is set in flags. The buffer size should be
+ // big enough to store at least the number of bytes returned by the
+ // getSize() method.
+ //
+ void encode(rdr::U8* dst) const;
+
+ protected:
+
+ //
+ // Analyze the tile pixels, fill in all the data fields.
+ //
+ void analyze();
+
+ const PIXEL_T *m_tile;
+ int m_width;
+ int m_height;
+
+ int m_size;
+ int m_flags;
+ PIXEL_T m_background;
+ PIXEL_T m_foreground;
+
+ int m_numSubrects;
+ rdr::U8 m_coords[256 * 2];
+ PIXEL_T m_colors[256];
+
+ private:
+
+ bool m_processed[16][16];
+ TightPalette m_pal;
+};
+
+HEXTILE_TILE::HEXTILE_TILE()
+ : m_tile(NULL), m_width(0), m_height(0),
+ m_size(0), m_flags(0), m_background(0), m_foreground(0),
+ m_numSubrects(0), m_pal(48 + 2 * BPP)
+{
+}
+
+void HEXTILE_TILE::newTile(const PIXEL_T *src, int w, int h)
+{
+ m_tile = src;
+ m_width = w;
+ m_height = h;
+
+ analyze();
+}
+
+void HEXTILE_TILE::analyze()
+{
+ assert(m_tile && m_width && m_height);
+
+ const PIXEL_T *ptr = m_tile;
+ const PIXEL_T *end = &m_tile[m_width * m_height];
+ PIXEL_T color = *ptr++;
+ while (ptr != end && *ptr == color)
+ ptr++;
+
+ // Handle solid tile
+ if (ptr == end) {
+ m_background = m_tile[0];
+ m_flags = 0;
+ m_size = 0;
+ return;
+ }
+
+ // Compute number of complete rows of the same color, at the top
+ int y = (ptr - m_tile) / m_width;
+
+ PIXEL_T *colorsPtr = m_colors;
+ rdr::U8 *coordsPtr = m_coords;
+ m_pal.reset();
+ m_numSubrects = 0;
+
+ // Have we found the first subrect already?
+ if (y > 0) {
+ *colorsPtr++ = color;
+ *coordsPtr++ = 0;
+ *coordsPtr++ = (rdr::U8)(((m_width - 1) << 4) | ((y - 1) & 0x0F));
+ m_pal.insert(color, 1);
+ m_numSubrects++;
+ }
+
+ memset(m_processed, 0, 16 * 16 * sizeof(bool));
+
+ int x, sx, sy, sw, sh, max_x;
+
+ for (; y < m_height; y++) {
+ for (x = 0; x < m_width; x++) {
+ // Skip pixels that were processed earlier
+ if (m_processed[y][x]) {
+ continue;
+ }
+ // Determine dimensions of the horizontal subrect
+ color = m_tile[y * m_width + x];
+ for (sx = x + 1; sx < m_width; sx++) {
+ if (m_tile[y * m_width + sx] != color)
+ break;
+ }
+ sw = sx - x;
+ max_x = sx;
+ for (sy = y + 1; sy < m_height; sy++) {
+ for (sx = x; sx < max_x; sx++) {
+ if (m_tile[sy * m_width + sx] != color)
+ goto done;
+ }
+ }
+ done:
+ sh = sy - y;
+
+ // Save properties of this subrect
+ *colorsPtr++ = color;
+ *coordsPtr++ = (rdr::U8)((x << 4) | (y & 0x0F));
+ *coordsPtr++ = (rdr::U8)(((sw - 1) << 4) | ((sh - 1) & 0x0F));
+
+ if (m_pal.insert(color, 1) == 0) {
+ // Handle palette overflow
+ m_flags = hextileRaw;
+ m_size = 0;
+ return;
+ }
+
+ m_numSubrects++;
+
+ // Mark pixels of this subrect as processed, below this row
+ for (sy = y + 1; sy < y + sh; sy++) {
+ for (sx = x; sx < x + sw; sx++)
+ m_processed[sy][sx] = true;
+ }
+
+ // Skip processed pixels of this row
+ x += (sw - 1);
+ }
+ }
+
+ // Save number of colors in this tile (should be no less than 2)
+ int numColors = m_pal.getNumColors();
+ assert(numColors >= 2);
+
+ m_background = (PIXEL_T)m_pal.getEntry(0);
+ m_flags = hextileAnySubrects;
+ int numSubrects = m_numSubrects - m_pal.getCount(0);
+
+ if (numColors == 2) {
+ // Monochrome tile
+ m_foreground = (PIXEL_T)m_pal.getEntry(1);
+ m_size = 1 + 2 * numSubrects;
+ } else {
+ // Colored tile
+ m_flags |= hextileSubrectsColoured;
+ m_size = 1 + (2 + (BPP/8)) * numSubrects;
+ }
+}
+
+void HEXTILE_TILE::encode(rdr::U8 *dst) const
+{
+ assert(m_numSubrects && (m_flags & hextileAnySubrects));
+
+ // Zero subrects counter
+ rdr::U8 *numSubrectsPtr = dst;
+ *dst++ = 0;
+
+ for (int i = 0; i < m_numSubrects; i++) {
+ if (m_colors[i] == m_background)
+ continue;
+
+ if (m_flags & hextileSubrectsColoured) {
+#if (BPP == 8)
+ *dst++ = m_colors[i];
+#elif (BPP == 16)
+ *dst++ = ((rdr::U8*)&m_colors[i])[0];
+ *dst++ = ((rdr::U8*)&m_colors[i])[1];
+#elif (BPP == 32)
+ *dst++ = ((rdr::U8*)&m_colors[i])[0];
+ *dst++ = ((rdr::U8*)&m_colors[i])[1];
+ *dst++ = ((rdr::U8*)&m_colors[i])[2];
+ *dst++ = ((rdr::U8*)&m_colors[i])[3];
+#endif
+ }
+ *dst++ = m_coords[i * 2];
+ *dst++ = m_coords[i * 2 + 1];
+
+ (*numSubrectsPtr)++;
+ }
+
+ assert(dst - numSubrectsPtr == m_size);
+}
+
+//
+// Main encoding function.
+//
+
+void HEXTILE_ENCODE(const Rect& r, rdr::OutStream* os
+#ifdef EXTRA_ARGS
+ , EXTRA_ARGS
+#endif
+ )
+{
+ Rect t;
+ PIXEL_T buf[256];
+ PIXEL_T oldBg = 0, oldFg = 0;
+ bool oldBgValid = false;
+ bool oldFgValid = false;
+ rdr::U8 encoded[256*(BPP/8)];
+
+ HEXTILE_TILE tile;
+
+ for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
+
+ t.br.y = __rfbmin(r.br.y, t.tl.y + 16);
+
+ for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
+
+ t.br.x = __rfbmin(r.br.x, t.tl.x + 16);
+
+ GET_IMAGE_INTO_BUF(t,buf);
+
+ tile.newTile(buf, t.width(), t.height());
+ int tileType = tile.getFlags();
+ int encodedLen = tile.getSize();
+
+ if ( (tileType & hextileRaw) != 0 ||
+ encodedLen >= t.width() * t.height() * (BPP/8)) {
+ os->writeU8(hextileRaw);
+ os->writeBytes(buf, t.width() * t.height() * (BPP/8));
+ oldBgValid = oldFgValid = false;
+ continue;
+ }
+
+ PIXEL_T bg = tile.getBackground();
+ PIXEL_T fg = 0;
+
+ if (!oldBgValid || oldBg != bg) {
+ tileType |= hextileBgSpecified;
+ oldBg = bg;
+ oldBgValid = true;
+ }
+
+ if (tileType & hextileAnySubrects) {
+ if (tileType & hextileSubrectsColoured) {
+ oldFgValid = false;
+ } else {
+ fg = tile.getForeground();
+ if (!oldFgValid || oldFg != fg) {
+ tileType |= hextileFgSpecified;
+ oldFg = fg;
+ oldFgValid = true;
+ }
+ }
+ tile.encode(encoded);
+ }
+
+ os->writeU8(tileType);
+ if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg);
+ if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg);
+ if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
+ }
+ }
+}
+
+#undef PIXEL_T
+#undef WRITE_PIXEL
+#undef HEXTILE_TILE
+#undef HEXTILE_ENCODE
+}