From patchwork Thu Nov 11 16:56:57 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corentin Chary X-Patchwork-Id: 70836 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id E8900B714C for ; Fri, 12 Nov 2010 04:12:20 +1100 (EST) Received: from localhost ([127.0.0.1]:47492 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PGace-0004pp-Ks for incoming@patchwork.ozlabs.org; Thu, 11 Nov 2010 12:07:40 -0500 Received: from [140.186.70.92] (port=52218 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PGaSe-0000AE-Gi for qemu-devel@nongnu.org; Thu, 11 Nov 2010 11:57:26 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PGaSY-0005ag-9S for qemu-devel@nongnu.org; Thu, 11 Nov 2010 11:57:19 -0500 Received: from relay2-v.mail.gandi.net ([217.70.178.76]:52209) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PGaSX-0005a6-In for qemu-devel@nongnu.org; Thu, 11 Nov 2010 11:57:14 -0500 X-Originating-IP: 217.70.178.37 Received: from mfilter3-v.gandi.net (mfilter3-v.gandi.net [217.70.178.37]) by relay2-v.mail.gandi.net (Postfix) with ESMTP id F1D32135D9; Thu, 11 Nov 2010 17:57:12 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at mfilter3-v.gandi.net Received: from relay2-v.mail.gandi.net ([217.70.178.76]) by mfilter3-v.gandi.net (mfilter3-v.gandi.net [217.70.178.37]) (amavisd-new, port 10024) with ESMTP id Zrb-qbE03HWl; Thu, 11 Nov 2010 17:57:11 +0100 (CET) X-Originating-IP: 82.241.209.44 Received: from localhost.localdomain (falgoret.iksaif.net [82.241.209.44]) (Authenticated sender: fake@iksaif.net) by relay2-v.mail.gandi.net (Postfix) with ESMTPSA id B75A0135E8; Thu, 11 Nov 2010 17:57:09 +0100 (CET) From: Corentin Chary To: Qemu-development List Date: Thu, 11 Nov 2010 17:56:57 +0100 Message-Id: <1289494624-12807-9-git-send-email-corentincj@iksaif.net> X-Mailer: git-send-email 1.7.3.2 In-Reply-To: <1289494624-12807-1-git-send-email-corentincj@iksaif.net> References: <1289494624-12807-1-git-send-email-corentincj@iksaif.net> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) Cc: Corentin Chary , Anthony Liguori , Alexander Graf , Andre Przywara Subject: [Qemu-devel] [PATCH v2 08/15] vnc: Add ZRLE and ZYWRLE encodings. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Add ZRLE [1] and ZYWRLE [2] encodings. The code is inspire^W stolen from libvncserver (again), but have been rewriten to match QEMU coding style. [1] http://www.realvnc.com/docs/rfbproto.pdf [2] http://micro-vnc.jp/research/remote_desktop_ng/ZYWRLE/publications/ Signed-off-by: Corentin Chary --- Makefile.objs | 1 + ui/vnc-enc-zrle-template.c | 275 ++++++++++++++++++ ui/vnc-enc-zrle.c | 389 +++++++++++++++++++++++++ ui/vnc-enc-zrle.h | 40 +++ ui/vnc-enc-zywrle-template.c | 170 +++++++++++ ui/vnc-enc-zywrle.h | 652 ++++++++++++++++++++++++++++++++++++++++++ ui/vnc-jobs-async.c | 2 + ui/vnc.c | 15 + ui/vnc.h | 27 ++- 9 files changed, 1570 insertions(+), 1 deletions(-) create mode 100644 ui/vnc-enc-zrle-template.c create mode 100644 ui/vnc-enc-zrle.c create mode 100644 ui/vnc-enc-zrle.h create mode 100644 ui/vnc-enc-zywrle-template.c create mode 100644 ui/vnc-enc-zywrle.h diff --git a/Makefile.objs b/Makefile.objs index 15569af..3da959c 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -121,6 +121,7 @@ ui-obj-$(CONFIG_CURSES) += curses.o ui-obj-y += vnc.o d3des.o ui-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o ui-obj-y += vnc-enc-tight.o vnc-palette.o +ui-obj-y += vnc-enc-zrle.o ui-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o ui-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o ui-obj-$(CONFIG_COCOA) += cocoa.o diff --git a/ui/vnc-enc-zrle-template.c b/ui/vnc-enc-zrle-template.c new file mode 100644 index 0000000..94e92fb --- /dev/null +++ b/ui/vnc-enc-zrle-template.c @@ -0,0 +1,275 @@ +/* + * QEMU VNC display driver: Zlib Run-length Encoding (ZRLE) + * + * From libvncserver/libvncserver/zrleencodetemplate.c + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * Copyright (C) 2010 Corentin Chary + * + * 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. + */ + +/* + * Before including this file, you must define a number of CPP macros. + * + * ZRLE_BPP should be 8, 16 or 32 depending on the bits per pixel. + * + * Note that the buf argument to ZRLE_ENCODE needs to be at least one pixel + * bigger than the largest tile of pixel data, since the ZRLE encoding + * algorithm writes to the position one past the end of the pixel data. + */ + + +#include + +#undef ZRLE_ENDIAN_SUFFIX + +#if ZYWRLE_ENDIAN == ENDIAN_LITTLE +#define ZRLE_ENDIAN_SUFFIX le +#elif ZYWRLE_ENDIAN == ENDIAN_BIG +#define ZRLE_ENDIAN_SUFFIX be +#else +#define ZRLE_ENDIAN_SUFFIX ne +#endif + +#ifndef ZRLE_CONCAT +#define ZRLE_CONCAT_I(a, b) a##b +#define ZRLE_CONCAT2(a, b) ZRLE_CONCAT_I(a, b) +#define ZRLE_CONCAT3(a, b, c) ZRLE_CONCAT2(a, ZRLE_CONCAT2(b, c)) +#endif + +#ifdef ZRLE_COMPACT_PIXEL +#define ZRLE_ENCODE_SUFFIX ZRLE_CONCAT2(ZRLE_COMPACT_PIXEL,ZRLE_ENDIAN_SUFFIX) +#define ZRLE_WRITE_SUFFIX ZRLE_COMPACT_PIXEL +#define ZRLE_PIXEL ZRLE_CONCAT3(uint,ZRLE_BPP,_t) +#define ZRLE_BPP_OUT 24 +#elif ZRLE_BPP == 15 +#define ZRLE_ENCODE_SUFFIX ZRLE_CONCAT2(ZRLE_BPP,ZRLE_ENDIAN_SUFFIX) +#define ZRLE_WRITE_SUFFIX 16 +#define ZRLE_PIXEL uint16_t +#define ZRLE_BPP_OUT 16 +#else +#define ZRLE_ENCODE_SUFFIX ZRLE_CONCAT2(ZRLE_BPP,ZRLE_ENDIAN_SUFFIX) +#define ZRLE_WRITE_SUFFIX ZRLE_BPP +#define ZRLE_BPP_OUT ZRLE_BPP +#define ZRLE_PIXEL ZRLE_CONCAT3(uint,ZRLE_BPP,_t) +#endif + +#define ZRLE_WRITE_PIXEL ZRLE_CONCAT2(zrle_write_u, ZRLE_WRITE_SUFFIX) +#define ZRLE_ENCODE ZRLE_CONCAT2(zrle_encode_, ZRLE_ENCODE_SUFFIX) +#define ZRLE_ENCODE_TILE ZRLE_CONCAT2(zrle_encode_tile, ZRLE_ENCODE_SUFFIX) +#define ZRLE_WRITE_PALETTE ZRLE_CONCAT2(zrle_write_palette,ZRLE_ENCODE_SUFFIX) + +static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, + int zywrle_level); + +#if ZRLE_BPP != 8 +#include "vnc-enc-zywrle-template.c" +#endif + + +static void ZRLE_ENCODE(VncState *vs, int x, int y, int w, int h, + int zywrle_level) +{ + int ty; + + for (ty = y; ty < y + h; ty += VNC_ZRLE_TILE_HEIGHT) { + + int tx, th; + + th = MIN(VNC_ZRLE_TILE_HEIGHT, y + h - ty); + + for (tx = x; tx < x + w; tx += VNC_ZRLE_TILE_WIDTH) { + int tw; + ZRLE_PIXEL *buf; + + tw = MIN(VNC_ZRLE_TILE_WIDTH, x + w - tx); + + buf = zrle_convert_fb(vs, tx, ty, tw, th, ZRLE_BPP); + ZRLE_ENCODE_TILE(vs, buf, tw, th, zywrle_level); + } + } +} + +static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, + int zywrle_level) +{ + VncPalette *palette = &vs->zrle.palette; + + int runs = 0; + int single_pixels = 0; + + bool use_rle; + bool use_palette; + + int i; + + ZRLE_PIXEL *ptr = data; + ZRLE_PIXEL *end = ptr + h * w; + *end = ~*(end-1); /* one past the end is different so the while loop ends */ + + /* Real limit is 127 but we wan't a way to know if there is more than 127 */ + palette_init(palette, 256, ZRLE_BPP); + + while (ptr < end) { + ZRLE_PIXEL pix = *ptr; + if (*++ptr != pix) { /* FIXME */ + single_pixels++; + } else { + while (*++ptr == pix) ; + runs++; + } + palette_put(palette, pix); + } + + /* Solid tile is a special case */ + + if (palette_size(palette) == 1) { + bool found; + + vnc_write_u8(vs, 1); + ZRLE_WRITE_PIXEL(vs, palette_color(palette, 0, &found)); + return; + } + + zrle_choose_palette_rle(vs, w, h, palette, ZRLE_BPP_OUT, + runs, single_pixels, zywrle_level, + &use_rle, &use_palette); + + if (!use_palette) { + vnc_write_u8(vs, (use_rle ? 128 : 0)); + } else { + uint32_t colors[VNC_PALETTE_MAX_SIZE]; + size_t size = palette_size(palette); + + vnc_write_u8(vs, (use_rle ? 128 : 0) | size); + palette_fill(palette, colors); + + for (i = 0; i < size; i++) { + ZRLE_WRITE_PIXEL(vs, colors[i]); + } + } + + if (use_rle) { + ZRLE_PIXEL *ptr = data; + ZRLE_PIXEL *end = ptr + w * h; + ZRLE_PIXEL *run_start; + ZRLE_PIXEL pix; + + while (ptr < end) { + int len; + int index = 0; + + run_start = ptr; + pix = *ptr++; + + while (*ptr == pix && ptr < end) { + ptr++; + } + + len = ptr - run_start; + + if (use_palette) + index = palette_idx(palette, pix); + + if (len <= 2 && use_palette) { + if (len == 2) { + vnc_write_u8(vs, index); + } + vnc_write_u8(vs, index); + continue; + } + if (use_palette) { + vnc_write_u8(vs, index | 128); + } else { + ZRLE_WRITE_PIXEL(vs, pix); + } + + len -= 1; + + while (len >= 255) { + vnc_write_u8(vs, 255); + len -= 255; + } + + vnc_write_u8(vs, len); + } + } else if (use_palette) { /* no RLE */ + int bppp; + ZRLE_PIXEL *ptr = data; + + /* packed pixels */ + + assert (palette_size(palette) < 17); + + bppp = bits_per_packed_pixel[palette_size(palette)-1]; + + for (i = 0; i < h; i++) { + uint8_t nbits = 0; + uint8_t byte = 0; + + ZRLE_PIXEL *eol = ptr + w; + + while (ptr < eol) { + ZRLE_PIXEL pix = *ptr++; + uint8_t index = palette_idx(palette, pix); + + byte = (byte << bppp) | index; + nbits += bppp; + if (nbits >= 8) { + vnc_write_u8(vs, byte); + nbits = 0; + } + } + if (nbits > 0) { + byte <<= 8 - nbits; + vnc_write_u8(vs, byte); + } + } + } else { + + /* raw */ + +#if ZRLE_BPP != 8 + if (zywrle_level > 0 && !(zywrle_level & 0x80)) { + ZYWRLE_ANALYZE(data, data, w, h, w, zywrle_level, vs->zywrle.buf); + ZRLE_ENCODE_TILE(vs, data, w, h, zywrle_level | 0x80); + } + else +#endif + { +#ifdef ZRLE_COMPACT_PIXEL + ZRLE_PIXEL *ptr; + + for (ptr = data; ptr < data + w * h; ptr++) { + ZRLE_WRITE_PIXEL(vs, *ptr); + } +#else + vnc_write(vs, data, w * h * (ZRLE_BPP / 8)); +#endif + } + } +} + +#undef ZRLE_PIXEL +#undef ZRLE_WRITE_PIXEL +#undef ZRLE_ENCODE +#undef ZRLE_ENCODE_TILE +#undef ZYWRLE_ENCODE_TILE +#undef ZRLE_BPP_OUT +#undef ZRLE_WRITE_SUFFIX +#undef ZRLE_ENCODE_SUFFIX diff --git a/ui/vnc-enc-zrle.c b/ui/vnc-enc-zrle.c new file mode 100644 index 0000000..4460890 --- /dev/null +++ b/ui/vnc-enc-zrle.c @@ -0,0 +1,389 @@ +/* + * QEMU VNC display driver: Zlib Run-length Encoding (ZRLE) + * + * From libvncserver/libvncserver/zrle.c + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * Copyright (C) 2010 Corentin Chary + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "vnc.h" +#include "vnc-enc-zrle.h" + +static const int bits_per_packed_pixel[] = { + 0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 +}; + + +static void vnc_zrle_start(VncState *vs) +{ + buffer_reset(&vs->zrle.zrle); + + // make the output buffer be the zlib buffer, so we can compress it later + vs->zrle.tmp = vs->output; + vs->output = vs->zrle.zrle; +} + +static void vnc_zrle_stop(VncState *vs) +{ + // switch back to normal output/zlib buffers + vs->zrle.zrle = vs->output; + vs->output = vs->zrle.tmp; +} + +static void *zrle_convert_fb(VncState *vs, int x, int y, int w, int h, + int bpp) +{ + Buffer tmp; + + buffer_reset(&vs->zrle.fb); + buffer_reserve(&vs->zrle.fb, w * h * bpp + bpp); + + tmp = vs->output; + vs->output = vs->zrle.fb; + + vnc_raw_send_framebuffer_update(vs, x, y, w, h); + + vs->zrle.fb = vs->output; + vs->output = tmp; + return vs->zrle.fb.buffer; +} + +static int zrle_compress_data(VncState *vs, int level) +{ + z_streamp zstream = &vs->zrle.stream; + + buffer_reset(&vs->zrle.zlib); + + if (zstream->opaque != vs) { + int err; + + zstream->zalloc = vnc_zlib_zalloc; + zstream->zfree = vnc_zlib_zfree; + + err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS, + MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + + if (err != Z_OK) { + fprintf(stderr, "VNC: error initializing zlib\n"); + return -1; + } + + zstream->opaque = vs; + } + + /* reserve memory in output buffer */ + buffer_reserve(&vs->zrle.zlib, vs->zrle.zrle.offset + 64); + + /* set pointers */ + zstream->next_in = vs->zrle.zrle.buffer; + zstream->avail_in = vs->zrle.zrle.offset; + zstream->next_out = vs->zrle.zlib.buffer + vs->zrle.zlib.offset; + zstream->avail_out = vs->zrle.zlib.capacity - vs->zrle.zlib.offset; + zstream->data_type = Z_BINARY; + + /* start encoding */ + if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) { + fprintf(stderr, "VNC: error during zrle compression\n"); + return -1; + } + + vs->zrle.zlib.offset = vs->zrle.zlib.capacity - zstream->avail_out; + return vs->zrle.zlib.offset; +} + +/* Try to work out whether to use RLE and/or a palette. We do this by + * estimating the number of bytes which will be generated and picking the + * method which results in the fewest bytes. Of course this may not result + * in the fewest bytes after compression... */ +static void zrle_choose_palette_rle(VncState *vs, int w, int h, + VncPalette *palette, int bpp_out, + int runs, int single_pixels, + int zywrle_level, + bool *use_rle, bool *use_palette) +{ + size_t estimated_bytes; + size_t plain_rle_bytes; + + *use_palette = *use_rle = false; + + estimated_bytes = w * h * (bpp_out / 8); /* start assuming raw */ + + if (bpp_out != 8) { + if (zywrle_level > 0 && !(zywrle_level & 0x80)) + estimated_bytes >>= zywrle_level; + } + + plain_rle_bytes = ((bpp_out / 8) + 1) * (runs + single_pixels); + + if (plain_rle_bytes < estimated_bytes) { + *use_rle = true; + estimated_bytes = plain_rle_bytes; + } + + if (palette_size(palette) < 128) { + int palette_rle_bytes; + + palette_rle_bytes = (bpp_out / 8) * palette_size(palette); + palette_rle_bytes += 2 * runs + single_pixels; + + if (palette_rle_bytes < estimated_bytes) { + *use_rle = true; + *use_palette = true; + estimated_bytes = palette_rle_bytes; + } + + if (palette_size(palette) < 17) { + int packed_bytes; + + packed_bytes = (bpp_out / 8) * palette_size(palette); + packed_bytes += w * h * + bits_per_packed_pixel[palette_size(palette)-1] / 8; + + if (packed_bytes < estimated_bytes) { + *use_rle = false; + *use_palette = true; + estimated_bytes = packed_bytes; + } + } + } +} + +static void zrle_write_u32(VncState *vs, uint32_t value) +{ + uint8_t buf[4]; + + buf[0] = ((uint8_t *)&value)[0]; + buf[1] = ((uint8_t *)&value)[1]; + buf[2] = ((uint8_t *)&value)[2]; + buf[3] = ((uint8_t *)&value)[3]; + + vnc_write(vs, buf, 4); +} + +static void zrle_write_u24a(VncState *vs, uint32_t value) +{ + uint8_t buf[3]; + + buf[0] = ((uint8_t *)&value)[0]; + buf[1] = ((uint8_t *)&value)[1]; + buf[2] = ((uint8_t *)&value)[2]; + + vnc_write(vs, buf, 3); +} + +static void zrle_write_u24b(VncState *vs, uint32_t value) +{ + uint8_t buf[3]; + + buf[0] = ((uint8_t *)&value)[1]; + buf[1] = ((uint8_t *)&value)[2]; + buf[2] = ((uint8_t *)&value)[3]; + + vnc_write(vs, buf, 3); +} + +static void zrle_write_u16(VncState *vs, uint16_t value) +{ + uint8_t buf[2]; + + buf[0] = ((uint8_t *)&value)[0]; + buf[1] = ((uint8_t *)&value)[1]; + + vnc_write(vs, buf, 2); +} + +static void zrle_write_u8(VncState *vs, uint8_t value) +{ + vnc_write_u8(vs, value); +} + +#define ENDIAN_LITTLE 0 +#define ENDIAN_BIG 1 +#define ENDIAN_NO 2 + +#define ZRLE_BPP 8 +#define ZYWRLE_ENDIAN ENDIAN_NO +#include "vnc-enc-zrle-template.c" +#undef ZRLE_BPP + +#define ZRLE_BPP 15 +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_LITTLE +#include "vnc-enc-zrle-template.c" + +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_BIG +#include "vnc-enc-zrle-template.c" + +#undef ZRLE_BPP +#define ZRLE_BPP 16 +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_LITTLE +#include "vnc-enc-zrle-template.c" + +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_BIG +#include "vnc-enc-zrle-template.c" + +#undef ZRLE_BPP +#define ZRLE_BPP 32 +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_LITTLE +#include "vnc-enc-zrle-template.c" + +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_BIG +#include "vnc-enc-zrle-template.c" + +#define ZRLE_COMPACT_PIXEL 24a +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_LITTLE +#include "vnc-enc-zrle-template.c" + +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_BIG +#include "vnc-enc-zrle-template.c" + +#undef ZRLE_COMPACT_PIXEL +#define ZRLE_COMPACT_PIXEL 24b +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_LITTLE +#include "vnc-enc-zrle-template.c" + +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_BIG +#include "vnc-enc-zrle-template.c" +#undef ZRLE_COMPACT_PIXEL +#undef ZRLE_BPP + +static int zrle_send_framebuffer_update(VncState *vs, int x, int y, + int w, int h) +{ + bool be = !!(vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG); + size_t bytes; + int zywrle_level; + + if (vs->zrle.type == VNC_ENCODING_ZYWRLE) { + if (!vs->vd->lossy || vs->tight.quality < 0 || vs->tight.quality == 9) { + zywrle_level = 0; + vs->zrle.type = VNC_ENCODING_ZRLE; + } else if (vs->tight.quality < 3) { + zywrle_level = 3; + } else if (vs->tight.quality < 6) { + zywrle_level = 2; + } else { + zywrle_level = 1; + } + } else { + zywrle_level = 0; + } + + vnc_zrle_start(vs); + + switch(vs->clientds.pf.bytes_per_pixel) { + case 1: + zrle_encode_8ne(vs, x, y, w, h, zywrle_level); + break; + + case 2: + if (vs->clientds.pf.gmax > 0x1F) { + if (be) { + zrle_encode_16be(vs, x, y, w, h, zywrle_level); + } else { + zrle_encode_16le(vs, x, y, w, h, zywrle_level); + } + } else { + if (be) { + zrle_encode_15be(vs, x, y, w, h, zywrle_level); + } else { + zrle_encode_15le(vs, x, y, w, h, zywrle_level); + } + } + break; + + case 4: + { + bool fits_in_ls3bytes; + bool fits_in_ms3bytes; + + fits_in_ls3bytes = + ((vs->clientds.pf.rmax << vs->clientds.pf.rshift) < (1 << 24) && + (vs->clientds.pf.gmax << vs->clientds.pf.gshift) < (1 << 24) && + (vs->clientds.pf.bmax << vs->clientds.pf.bshift) < (1 << 24)); + + fits_in_ms3bytes = (vs->clientds.pf.rshift > 7 && + vs->clientds.pf.gshift > 7 && + vs->clientds.pf.bshift > 7); + + if ((fits_in_ls3bytes && !be) || (fits_in_ms3bytes && be)) { + if (be) { + zrle_encode_24abe(vs, x, y, w, h, zywrle_level); + } else { + zrle_encode_24ale(vs, x, y, w, h, zywrle_level); + } + } else if ((fits_in_ls3bytes && be) || (fits_in_ms3bytes && !be)) { + if (be) { + zrle_encode_24bbe(vs, x, y, w, h, zywrle_level); + } else { + zrle_encode_24ble(vs, x, y, w, h, zywrle_level); + } + } else { + if (be) { + zrle_encode_32be(vs, x, y, w, h, zywrle_level); + } else { + zrle_encode_32le(vs, x, y, w, h, zywrle_level); + } + } + } + break; + } + + vnc_zrle_stop(vs); + bytes = zrle_compress_data(vs, Z_DEFAULT_COMPRESSION); + vnc_framebuffer_update(vs, x, y, w, h, vs->zrle.type); + vnc_write_u32(vs, bytes); + vnc_write(vs, vs->zrle.zlib.buffer, vs->zrle.zlib.offset); + return 1; +} + +int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +{ + vs->zrle.type = VNC_ENCODING_ZRLE; + return zrle_send_framebuffer_update(vs, x, y, w, h); +} + +int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +{ + vs->zrle.type = VNC_ENCODING_ZYWRLE; + return zrle_send_framebuffer_update(vs, x, y, w, h); +} + +void vnc_zrle_clear(VncState *vs) +{ + if (vs->zrle.stream.opaque) { + deflateEnd(&vs->zrle.stream); + } + buffer_free(&vs->zrle.zrle); + buffer_free(&vs->zrle.fb); + buffer_free(&vs->zrle.zlib); +} diff --git a/ui/vnc-enc-zrle.h b/ui/vnc-enc-zrle.h new file mode 100644 index 0000000..6b18213 --- /dev/null +++ b/ui/vnc-enc-zrle.h @@ -0,0 +1,40 @@ +/* + * QEMU VNC display driver: Zlib Run-length Encoding (ZRLE) + * + * From libvncserver/libvncserver/zrle.c + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * Copyright (C) 2010 Corentin Chary + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef VNC_ENCODING_ZRLE_H +#define VNC_ENCODING_ZRLE_H + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * ZRLE - encoding combining Zlib compression, tiling, palettisation and + * run-length encoding. + */ + +#define VNC_ZRLE_TILE_WIDTH 64 +#define VNC_ZRLE_TILE_HEIGHT 64 + +#endif diff --git a/ui/vnc-enc-zywrle-template.c b/ui/vnc-enc-zywrle-template.c new file mode 100644 index 0000000..4cde6e4 --- /dev/null +++ b/ui/vnc-enc-zywrle-template.c @@ -0,0 +1,170 @@ + +/******************************************************************** + * * + * THIS FILE IS PART OF THE 'ZYWRLE' VNC CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A FOLLOWING BSD-STYLE SOURCE LICENSE. * + * PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE 'ZYWRLE' VNC CODEC SOURCE CODE IS (C) COPYRIGHT 2006 * + * BY Hitachi Systems & Services, Ltd. * + * (Noriaki Yamazaki, Research & Developement Center) * + * * + * * + ******************************************************************** +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Hitachi Systems & Services, Ltd. nor +the names of its contributors may be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************/ + +/* Change Log: + V0.02 : 2008/02/04 : Fix mis encode/decode when width != scanline + (Thanks Johannes Schindelin, author of LibVNC + Server/Client) + V0.01 : 2007/02/06 : Initial release +*/ + +/* +[References] + PLHarr: + Senecal, J. G., P. Lindstrom, M. A. Duchaineau, and K. I. Joy, + "An Improved N-Bit to N-Bit Reversible Haar-Like Transform," + Pacific Graphics 2004, October 2004, pp. 371-380. + EZW: + Shapiro, JM: Embedded Image Coding Using Zerotrees of Wavelet Coefficients, + IEEE Trans. Signal. Process., Vol.41, pp.3445-3462 (1993). +*/ + + +/* Template Macro stuffs. */ +#undef ZYWRLE_ANALYZE +#undef ZYWRLE_SYNTHESIZE + +#define ZYWRLE_SUFFIX ZRLE_CONCAT2(ZRLE_BPP,ZRLE_ENDIAN_SUFFIX) + +#define ZYWRLE_ANALYZE ZRLE_CONCAT2(zywrle_analyze_, ZYWRLE_SUFFIX) +#define ZYWRLE_SYNTHESIZE ZRLE_CONCAT2(zywrle_synthesize_,ZYWRLE_SUFFIX) + +#define ZYWRLE_RGBYUV ZRLE_CONCAT2(zywrle_rgbyuv_, ZYWRLE_SUFFIX) +#define ZYWRLE_YUVRGB ZRLE_CONCAT2(zywrle_yuvrgb_, ZYWRLE_SUFFIX) +#define ZYWRLE_YMASK ZRLE_CONCAT2(ZYWRLE_YMASK, ZRLE_BPP) +#define ZYWRLE_UVMASK ZRLE_CONCAT2(ZYWRLE_UVMASK, ZRLE_BPP) +#define ZYWRLE_LOAD_PIXEL ZRLE_CONCAT2(ZYWRLE_LOAD_PIXEL, ZRLE_BPP) +#define ZYWRLE_SAVE_PIXEL ZRLE_CONCAT2(ZYWRLE_SAVE_PIXEL, ZRLE_BPP) + +/* Packing/Unpacking pixel stuffs. + Endian conversion stuffs. */ +#undef S_0 +#undef S_1 +#undef L_0 +#undef L_1 +#undef L_2 + +#if ZYWRLE_ENDIAN == ENDIAN_BIG +# define S_0 1 +# define S_1 0 +# define L_0 3 +# define L_1 2 +# define L_2 1 +#else +# define S_0 0 +# define S_1 1 +# define L_0 0 +# define L_1 1 +# define L_2 2 +#endif + +#define ZYWRLE_QUANTIZE +#include "vnc-enc-zywrle.h" + +#ifndef ZRLE_COMPACT_PIXEL +static inline void ZYWRLE_RGBYUV(int *buf, ZRLE_PIXEL *data, + int width, int height, int scanline) +{ + int r, g, b; + int y, u, v; + int *line; + int *end; + + end = buf + height * width; + while (buf < end) { + line = buf + width; + while (buf < line) { + ZYWRLE_LOAD_PIXEL(data, r, g, b); + ZYWRLE_RGBYUV_(r, g, b, y, u, v, ZYWRLE_YMASK, ZYWRLE_UVMASK); + ZYWRLE_SAVE_COEFF(buf, v, y, u); + buf++; + data++; + } + data += scanline - width; + } +} + +static ZRLE_PIXEL *ZYWRLE_ANALYZE(ZRLE_PIXEL *dst, ZRLE_PIXEL *src, + int w, int h, int scanline, int level, + int *buf) { + int l; + int uw = w; + int uh = h; + int *top; + int *end; + int *line; + ZRLE_PIXEL *p; + int r, g, b; + int s; + int *ph; + + zywrle_calc_size(&w, &h, level); + + if (w == 0 || h == 0) { + return NULL; + } + uw -= w; + uh -= h; + + p = dst; + ZYWRLE_LOAD_UNALIGN(src,*(ZRLE_PIXEL*)top = *p;); + ZYWRLE_RGBYUV(buf, src, w, h, scanline); + wavelet(buf, w, h, level); + for (l = 0; l < level; l++) { + ZYWRLE_PACK_COEFF(buf, dst, 3, w, h, scanline, l); + ZYWRLE_PACK_COEFF(buf, dst, 2, w, h, scanline, l); + ZYWRLE_PACK_COEFF(buf, dst, 1, w, h, scanline, l); + if (l == level - 1) { + ZYWRLE_PACK_COEFF(buf, dst, 0, w, h, scanline, l); + } + } + ZYWRLE_SAVE_UNALIGN(dst,*dst = *(ZRLE_PIXEL*)top;); + return dst; +} +#endif /* ZRLE_COMPACT_PIXEL */ + +#undef ZYWRLE_RGBYUV +#undef ZYWRLE_YUVRGB +#undef ZYWRLE_LOAD_PIXEL +#undef ZYWRLE_SAVE_PIXEL diff --git a/ui/vnc-enc-zywrle.h b/ui/vnc-enc-zywrle.h new file mode 100644 index 0000000..722bd7d --- /dev/null +++ b/ui/vnc-enc-zywrle.h @@ -0,0 +1,652 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE 'ZYWRLE' VNC CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A FOLLOWING BSD-STYLE SOURCE LICENSE. * + * PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE 'ZYWRLE' VNC CODEC SOURCE CODE IS (C) COPYRIGHT 2006 * + * BY Hitachi Systems & Services, Ltd. * + * (Noriaki Yamazaki, Research & Developement Center) * + * * + * * + ******************************************************************** +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Hitachi Systems & Services, Ltd. nor +the names of its contributors may be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************/ + +#ifndef VNC_ENCODING_ZYWRLE_H +#define VNC_ENCODING_ZYWRLE_H + +/* Tables for Coefficients filtering. */ +#ifndef ZYWRLE_QUANTIZE +/* Type A:lower bit omitting of EZW style. */ +const static unsigned int zywrle_param[3][3]={ + {0x0000F000,0x00000000,0x00000000}, + {0x0000C000,0x00F0F0F0,0x00000000}, + {0x0000C000,0x00C0C0C0,0x00F0F0F0}, +/* {0x0000FF00,0x00000000,0x00000000}, + {0x0000FF00,0x00FFFFFF,0x00000000}, + {0x0000FF00,0x00FFFFFF,0x00FFFFFF}, */ +}; +#else +/* Type B:Non liner quantization filter. */ +static const signed char zywrle_conv[4][256]={ +{ /* bi=5, bo=5 r=0.0:PSNR=24.849 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}, +{ /* bi=5, bo=5 r=2.0:PSNR=74.031 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 64, 64, 64, 64, + 64, 64, 64, 64, 72, 72, 72, 72, + 72, 72, 72, 72, 80, 80, 80, 80, + 80, 80, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 96, 96, + 96, 96, 96, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 104, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 0, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -104, -104, -104, -104, + -104, -104, -104, -104, -104, -104, -96, -96, + -96, -96, -96, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -80, + -80, -80, -80, -80, -80, -72, -72, -72, + -72, -72, -72, -72, -72, -64, -64, -64, + -64, -64, -64, -64, -64, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, + -48, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}, +{ /* bi=5, bo=4 r=2.0:PSNR=64.441 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 0, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -120, -120, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -104, -104, + -104, -104, -104, -104, -104, -104, -104, -104, + -104, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, + -80, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, + -64, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, + -48, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}, +{ /* bi=5, bo=2 r=2.0:PSNR=43.175 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 0, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +} +}; + +static const signed char *zywrle_param[3][3][3]={ + {{zywrle_conv[0], zywrle_conv[2], zywrle_conv[0]}, + {zywrle_conv[0], zywrle_conv[0], zywrle_conv[0]}, + {zywrle_conv[0], zywrle_conv[0], zywrle_conv[0]}}, + {{zywrle_conv[0], zywrle_conv[3], zywrle_conv[0]}, + {zywrle_conv[1], zywrle_conv[1], zywrle_conv[1]}, + {zywrle_conv[0], zywrle_conv[0], zywrle_conv[0]}}, + {{zywrle_conv[0], zywrle_conv[3], zywrle_conv[0]}, + {zywrle_conv[2], zywrle_conv[2], zywrle_conv[2]}, + {zywrle_conv[1], zywrle_conv[1], zywrle_conv[1]}}, +}; +#endif + +/* Load/Save pixel stuffs. */ +#define ZYWRLE_YMASK15 0xFFFFFFF8 +#define ZYWRLE_UVMASK15 0xFFFFFFF8 +#define ZYWRLE_LOAD_PIXEL15(src, r, g, b) \ + do { \ + r = (((uint8_t*)src)[S_1]<< 1)& 0xF8; \ + g = (((uint8_t*)src)[S_1]<< 6) | (((uint8_t*)src)[S_0]>> 2); \ + g &= 0xF8; \ + b = (((uint8_t*)src)[S_0]<< 3)& 0xF8; \ + } while (0) + +#define ZYWRLE_SAVE_PIXEL15(dst, r, g, b) \ + do { \ + r &= 0xF8; \ + g &= 0xF8; \ + b &= 0xF8; \ + ((uint8_t*)dst)[S_1] = (uint8_t)((r >> 1)|(g >> 6)); \ + ((uint8_t*)dst)[S_0] = (uint8_t)(((b >> 3)|(g << 2))& 0xFF); \ + } while (0) + +#define ZYWRLE_YMASK16 0xFFFFFFFC +#define ZYWRLE_UVMASK16 0xFFFFFFF8 +#define ZYWRLE_LOAD_PIXEL16(src, r, g, b) \ + do { \ + r = ((uint8_t*)src)[S_1] & 0xF8; \ + g = (((uint8_t*)src)[S_1]<< 5) | (((uint8_t*)src)[S_0] >> 3); \ + g &= 0xFC; \ + b = (((uint8_t*)src)[S_0]<< 3) & 0xF8; \ + } while (0) + +#define ZYWRLE_SAVE_PIXEL16(dst, r, g,b) \ + do { \ + r &= 0xF8; \ + g &= 0xFC; \ + b &= 0xF8; \ + ((uint8_t*)dst)[S_1] = (uint8_t)(r | (g >> 5)); \ + ((uint8_t*)dst)[S_0] = (uint8_t)(((b >> 3)|(g << 3)) & 0xFF); \ + } while (0) + +#define ZYWRLE_YMASK32 0xFFFFFFFF +#define ZYWRLE_UVMASK32 0xFFFFFFFF +#define ZYWRLE_LOAD_PIXEL32(src, r, g, b) \ + do { \ + r = ((uint8_t*)src)[L_2]; \ + g = ((uint8_t*)src)[L_1]; \ + b = ((uint8_t*)src)[L_0]; \ + } while (0) +#define ZYWRLE_SAVE_PIXEL32(dst, r, g, b) \ + do { \ + ((uint8_t*)dst)[L_2] = (uint8_t)r; \ + ((uint8_t*)dst)[L_1] = (uint8_t)g; \ + ((uint8_t*)dst)[L_0] = (uint8_t)b; \ + } while (0) + +static inline void harr(int8_t *px0, int8_t *px1) +{ + /* Piecewise-Linear Harr(PLHarr) */ + int x0 = (int)*px0, x1 = (int)*px1; + int orgx0 = x0, orgx1 = x1; + + if ((x0 ^ x1) & 0x80) { + /* differ sign */ + x1 += x0; + if (((x1 ^ orgx1) & 0x80) == 0) { + /* |x1| > |x0| */ + x0 -= x1; /* H = -B */ + } + } else { + /* same sign */ + x0 -= x1; + if (((x0 ^ orgx0) & 0x80) == 0) { + /* |x0| > |x1| */ + x1 += x0; /* L = A */ + } + } + *px0 = (int8_t)x1; + *px1 = (int8_t)x0; +} + +/* + 1D-Wavelet transform. + + In coefficients array, the famous 'pyramid' decomposition is well used. + + 1D Model: + |L0L0L0L0|L0L0L0L0|H0H0H0H0|H0H0H0H0| : level 0 + |L1L1L1L1|H1H1H1H1|H0H0H0H0|H0H0H0H0| : level 1 + + But this method needs line buffer because H/L is different position from X0/X1. + So, I used 'interleave' decomposition instead of it. + + 1D Model: + |L0H0L0H0|L0H0L0H0|L0H0L0H0|L0H0L0H0| : level 0 + |L1H0H1H0|L1H0H1H0|L1H0H1H0|L1H0H1H0| : level 1 + + In this method, H/L and X0/X1 is always same position. + This lead us to more speed and less memory. + Of cause, the result of both method is quite same + because it's only difference that coefficient position. +*/ +static inline void wavelet_level(int *data, int size, int l, int skip_pixel) +{ + int s, ofs; + int8_t *px0; + int8_t *end; + + px0 = (int8_t*)data; + s = (8 << l) * skip_pixel; + end = px0 + (size >> (l + 1)) * s; + s -= 2; + ofs = (4 << l) * skip_pixel; + + while (px0 < end) { + harr(px0, px0 + ofs); + px0++; + harr(px0, px0 + ofs); + px0++; + harr(px0, px0 + ofs); + px0 += s; + } +} + +#ifndef ZYWRLE_QUANTIZE +/* Type A:lower bit omitting of EZW style. */ +static inline void filter_wavelet_square(int *buf, int width, int height, + int level, int l) +{ + int r, s; + int x, y; + int *h; + const unsigned int *m; + + m = &(zywrle_param[level - 1][l]); + s = 2 << l; + + for (r = 1; r < 4; r++) { + h = buf; + if (r & 0x01) + h += s >> 1; + if (r & 0x02) + h += (s >> 1) * width; + for (y = 0; y < height / s; y++) { + for (x = 0; x < width / s; x++) { + /* + these are same following code. + h[x] = h[x] / (~m[x]+1) * (~m[x]+1); + ( round h[x] with m[x] bit ) + '&' operator isn't 'round' but is 'floor'. + So, we must offset when h[x] is negative. + */ + if (((int8_t*)h)[0] & 0x80) + ((int8_t*)h)[0] += ~((int8_t*)m)[0]; + if (((int8_t*)h)[1] & 0x80) + ((int8_t*)h)[1] += ~((int8_t*)m)[1]; + if (((int8_t*)h)[2] & 0x80) + ((int8_t*)h)[2] += ~((int8_t*)m)[2]; + *h &= *m; + h += s; + } + h += (s-1)*width; + } + } +} +#else +/* + Type B:Non liner quantization filter. + + Coefficients have Gaussian curve and smaller value which is + large part of coefficients isn't more important than larger value. + So, I use filter of Non liner quantize/dequantize table. + In general, Non liner quantize formula is explained as following. + + y=f(x) = sign(x)*round( ((abs(x)/(2^7))^ r )* 2^(bo-1) )*2^(8-bo) + x=f-1(y) = sign(y)*round( ((abs(y)/(2^7))^(1/r))* 2^(bi-1) )*2^(8-bi) + ( r:power coefficient bi:effective MSB in input bo:effective MSB in output ) + + r < 1.0 : Smaller value is more important than larger value. + r > 1.0 : Larger value is more important than smaller value. + r = 1.0 : Liner quantization which is same with EZW style. + + r = 0.75 is famous non liner quantization used in MP3 audio codec. + In contrast to audio data, larger value is important in wavelet coefficients. + So, I select r = 2.0 table( quantize is x^2, dequantize sqrt(x) ). + + As compared with EZW style liner quantization, this filter tended to be + more sharp edge and be more compression rate but be more blocking noise and be + less quality. Especially, the surface of graphic objects has distinguishable + noise in middle quality mode. + + We need only quantized-dequantized(filtered) value rather than quantized value + itself because all values are packed or palette-lized in later ZRLE section. + This lead us not to need to modify client decoder when we change + the filtering procedure in future. + Client only decodes coefficients given by encoder. +*/ +static inline void filter_wavelet_square(int *buf, int width, int height, + int level, int l) +{ + int r, s; + int x, y; + int *h; + const int8_t **m; + + m = zywrle_param[level - 1][l]; + s = 2 << l; + + for (r = 1; r < 4; r++) { + h = buf; + if (r & 0x01) + h += s >> 1; + if (r & 0x02) + h += (s >> 1) * width; + for (y = 0; y < height / s; y++) { + for (x = 0; x < width / s; x++) { + ((int8_t*)h)[0] = m[0][((uint8_t*)h)[0]]; + ((int8_t*)h)[1] = m[1][((uint8_t*)h)[1]]; + ((int8_t*)h)[2] = m[2][((uint8_t*)h)[2]]; + h += s; + } + h += (s - 1) * width; + } + } +} +#endif + +static inline void wavelet(int *buf, int width, int height, int level) +{ + int l, s; + int *top; + int *end; + + for (l = 0; l < level; l++) { + top = buf; + end = buf + height * width; + s = width << l; + while (top < end) { + wavelet_level(top, width, l, 1); + top += s; + } + top = buf; + end = buf + width; + s = 1< YUV conversion stuffs. + YUV coversion is explained as following formula in strict meaning: + Y = 0.299R + 0.587G + 0.114B ( 0<=Y<=255) + U = -0.169R - 0.331G + 0.500B (-128<=U<=127) + V = 0.500R - 0.419G - 0.081B (-128<=V<=127) + + I use simple conversion RCT(reversible color transform) which is described + in JPEG-2000 specification. + Y = (R + 2G + B)/4 ( 0<=Y<=255) + U = B-G (-256<=U<=255) + V = R-G (-256<=V<=255) +*/ + +/* RCT is N-bit RGB to N-bit Y and N+1-bit UV. + For make Same N-bit, UV is lossy. + More exact PLHarr, we reduce to odd range(-127<=x<=127). */ +#define ZYWRLE_RGBYUV_(r, g, b, y, u, v, ymask, uvmask) \ + do { \ + y = (r + (g << 1) + b) >> 2; \ + u = b - g; \ + v = r - g; \ + y -= 128; \ + u >>= 1; \ + v >>= 1; \ + y &= ymask; \ + u &= uvmask; \ + v &= uvmask; \ + if (y == -128) { \ + y += (0xFFFFFFFF - ymask + 1); \ + } \ + if (u == -128) { \ + u += (0xFFFFFFFF - uvmask + 1); \ + } \ + if (v == -128) { \ + v += (0xFFFFFFFF - uvmask + 1); \ + } \ + } while (0) + + +/* + coefficient packing/unpacking stuffs. + Wavelet transform makes 4 sub coefficient image from 1 original image. + + model with pyramid decomposition: + +------+------+ + | | | + | L | Hx | + | | | + +------+------+ + | | | + | H | Hxy | + | | | + +------+------+ + + So, we must transfer each sub images individually in strict meaning. + But at least ZRLE meaning, following one decompositon image is same as + avobe individual sub image. I use this format. + (Strictly saying, transfer order is reverse(Hxy->Hy->Hx->L) + for simplified procedure for any wavelet level.) + + +------+------+ + | L | + +------+------+ + | Hx | + +------+------+ + | Hy | + +------+------+ + | Hxy | + +------+------+ +*/ +#define ZYWRLE_INC_PTR(data) \ + do { \ + data++; \ + if( data - p >= (w + uw) ) { \ + data += scanline-(w + uw); \ + p = data; \ + } \ + } while (0) + +#define ZYWRLE_TRANSFER_COEFF(buf, data, t, w, h, scanline, level, TRANS) \ + do { \ + ph = buf; \ + s = 2 << level; \ + if (t & 0x01) { \ + ph += s >> 1; \ + } \ + if (t & 0x02) { \ + ph += (s >> 1) * w; \ + } \ + end = ph + h * w; \ + while (ph < end) { \ + line = ph + w; \ + while (ph < line) { \ + TRANS \ + ZYWRLE_INC_PTR(data); \ + ph += s; \ + } \ + ph += (s - 1) * w; \ + } \ + } while (0) + +#define ZYWRLE_PACK_COEFF(buf, data, t, width, height, scanline, level) \ + ZYWRLE_TRANSFER_COEFF(buf, data, t, width, height, scanline, level, \ + ZYWRLE_LOAD_COEFF(ph, r, g, b); \ + ZYWRLE_SAVE_PIXEL(data, r, g, b);) + +#define ZYWRLE_UNPACK_COEFF(buf, data, t, width, height, scanline, level) \ + ZYWRLE_TRANSFER_COEFF(buf, data, t, width, height, scanline, level, \ + ZYWRLE_LOAD_PIXEL(data, r, g, b); \ + ZYWRLE_SAVE_COEFF(ph, r, g, b);) + +#define ZYWRLE_SAVE_UNALIGN(data, TRANS) \ + do { \ + top = buf + w * h; \ + end = buf + (w + uw) * (h + uh); \ + while (top < end) { \ + TRANS \ + ZYWRLE_INC_PTR(data); \ + top++; \ + } \ + } while (0) + +#define ZYWRLE_LOAD_UNALIGN(data,TRANS) \ + do { \ + top = buf + w * h; \ + if (uw) { \ + p = data + w; \ + end = (int*)(p + h * scanline); \ + while (p < (ZRLE_PIXEL*)end) { \ + line = (int*)(p + uw); \ + while (p < (ZRLE_PIXEL*)line) { \ + TRANS \ + p++; \ + top++; \ + } \ + p += scanline - uw; \ + } \ + } \ + if (uh) { \ + p = data + h * scanline; \ + end = (int*)(p + uh * scanline); \ + while (p < (ZRLE_PIXEL*)end) { \ + line = (int*)(p + w); \ + while (p < (ZRLE_PIXEL*)line) { \ + TRANS \ + p++; \ + top++; \ + } \ + p += scanline - w; \ + } \ + } \ + if (uw && uh) { \ + p= data + w + h * scanline; \ + end = (int*)(p + uh * scanline); \ + while (p < (ZRLE_PIXEL*)end) { \ + line = (int*)(p + uw); \ + while (p < (ZRLE_PIXEL*)line) { \ + TRANS \ + p++; \ + top++; \ + } \ + p += scanline-uw; \ + } \ + } \ + } while (0) + +static inline void zywrle_calc_size(int *w, int *h, int level) +{ + *w &= ~((1 << level) - 1); + *h &= ~((1 << level) - 1); +} + +#endif diff --git a/ui/vnc-jobs-async.c b/ui/vnc-jobs-async.c index 31f8436..b98dc79 100644 --- a/ui/vnc-jobs-async.c +++ b/ui/vnc-jobs-async.c @@ -172,6 +172,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->tight = orig->tight; local->zlib = orig->zlib; local->hextile = orig->hextile; + local->zrle = orig->zrle; local->output = queue->buffer; local->csock = -1; /* Don't do any network work on this thread */ @@ -183,6 +184,7 @@ static void vnc_async_encoding_end(VncState *orig, VncState *local) orig->tight = local->tight; orig->zlib = local->zlib; orig->hextile = local->hextile; + orig->zrle = local->zrle; orig->lossy_rect = local->lossy_rect; } diff --git a/ui/vnc.c b/ui/vnc.c index e1643d1..227efb6 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -696,6 +696,12 @@ int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) case VNC_ENCODING_TIGHT_PNG: n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h); break; + case VNC_ENCODING_ZRLE: + n = vnc_zrle_send_framebuffer_update(vs, x, y, w, h); + break; + case VNC_ENCODING_ZYWRLE: + n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h); + break; default: vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); n = vnc_raw_send_framebuffer_update(vs, x, y, w, h); @@ -1028,6 +1034,7 @@ static void vnc_disconnect_finish(VncState *vs) vnc_zlib_clear(vs); vnc_tight_clear(vs); + vnc_zrle_clear(vs); #ifdef CONFIG_VNC_TLS vnc_tls_client_cleanup(vs); @@ -1766,6 +1773,14 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) vs->features |= VNC_FEATURE_ZLIB_MASK; vs->vnc_encoding = enc; break; + case VNC_ENCODING_ZRLE: + vs->features |= VNC_FEATURE_ZRLE_MASK; + vs->vnc_encoding = enc; + break; + case VNC_ENCODING_ZYWRLE: + vs->features |= VNC_FEATURE_ZYWRLE_MASK; + vs->vnc_encoding = enc; + break; case VNC_ENCODING_DESKTOPRESIZE: vs->features |= VNC_FEATURE_RESIZE_MASK; break; diff --git a/ui/vnc.h b/ui/vnc.h index 00bd21c..5cfa247 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -39,6 +39,8 @@ #include #include "keymaps.h" +#include "vnc-palette.h" +#include "vnc-enc-zrle.h" // #define _VNC_DEBUG 1 @@ -179,6 +181,20 @@ typedef struct VncZlib { int level; } VncZlib; +typedef struct VncZrle { + int type; + Buffer fb; + Buffer zrle; + Buffer tmp; + Buffer zlib; + z_stream stream; + VncPalette palette; +} VncZrle; + +typedef struct VncZywrle { + int buf[VNC_ZRLE_TILE_WIDTH * VNC_ZRLE_TILE_HEIGHT]; +} VncZywrle; + #ifdef CONFIG_VNC_THREAD struct VncRect { @@ -272,7 +288,8 @@ struct VncState VncTight tight; VncZlib zlib; VncHextile hextile; - + VncZrle zrle; + VncZywrle zywrle; Notifier mouse_mode_notifier; @@ -376,6 +393,8 @@ enum { #define VNC_FEATURE_COPYRECT 6 #define VNC_FEATURE_RICH_CURSOR 7 #define VNC_FEATURE_TIGHT_PNG 8 +#define VNC_FEATURE_ZRLE 9 +#define VNC_FEATURE_ZYWRLE 10 #define VNC_FEATURE_RESIZE_MASK (1 << VNC_FEATURE_RESIZE) #define VNC_FEATURE_HEXTILE_MASK (1 << VNC_FEATURE_HEXTILE) @@ -386,6 +405,8 @@ enum { #define VNC_FEATURE_COPYRECT_MASK (1 << VNC_FEATURE_COPYRECT) #define VNC_FEATURE_RICH_CURSOR_MASK (1 << VNC_FEATURE_RICH_CURSOR) #define VNC_FEATURE_TIGHT_PNG_MASK (1 << VNC_FEATURE_TIGHT_PNG) +#define VNC_FEATURE_ZRLE_MASK (1 << VNC_FEATURE_ZRLE) +#define VNC_FEATURE_ZYWRLE_MASK (1 << VNC_FEATURE_ZYWRLE) /* Client -> Server message IDs */ @@ -520,4 +541,8 @@ int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); void vnc_tight_clear(VncState *vs); +int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); +int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); +void vnc_zrle_clear(VncState *vs); + #endif /* __QEMU_VNC_H */