com.tigervnc.rfb.TightDecoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sikulix2tigervnc Show documentation
Show all versions of sikulix2tigervnc Show documentation
... for visual testing and automation (TigerVNC support)
The newest version!
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
* Copyright 2004-2005 Cendio AB.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
* Copyright (C) 2011-2012 Brian P. Hinz
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package com.tigervnc.rfb;
import com.tigervnc.rdr.InStream;
import com.tigervnc.rdr.ZlibInStream;
import java.util.ArrayList;
import java.io.InputStream;
import java.awt.image.*;
import java.awt.*;
public class TightDecoder extends Decoder {
final static int TIGHT_MAX_WIDTH = 2048;
// Compression control
final static int rfbTightExplicitFilter = 0x04;
final static int rfbTightFill = 0x08;
final static int rfbTightJpeg = 0x09;
final static int rfbTightMaxSubencoding = 0x09;
// Filters to improve compression efficiency
final static int rfbTightFilterCopy = 0x00;
final static int rfbTightFilterPalette = 0x01;
final static int rfbTightFilterGradient = 0x02;
final static int rfbTightMinToCompress = 12;
final static Toolkit tk = Toolkit.getDefaultToolkit();
public TightDecoder(CMsgReader reader_) {
reader = reader_;
zis = new ZlibInStream[4];
for (int i = 0; i < 4; i++)
zis[i] = new ZlibInStream();
}
public void readRect(Rect r, CMsgHandler handler)
{
InStream is = reader.getInStream();
boolean cutZeros = false;
clientpf = handler.getPreferredPF();
serverpf = handler.cp.pf();
int bpp = serverpf.bpp;
cutZeros = false;
if (bpp == 32) {
if (serverpf.is888()) {
cutZeros = true;
}
}
int comp_ctl = is.readU8();
boolean bigEndian = handler.cp.pf().bigEndian;
// Flush zlib streams if we are told by the server to do so.
for (int i = 0; i < 4; i++) {
if ((comp_ctl & 1) != 0) {
zis[i].reset();
}
comp_ctl >>= 1;
}
// "Fill" compression type.
if (comp_ctl == rfbTightFill) {
int[] pix = new int[1];
if (cutZeros) {
byte[] bytebuf = new byte[3];
is.readBytes(bytebuf, 0, 3);
serverpf.bufferFromRGB(pix, 0, bytebuf, 0, 1);
} else {
pix[0] = is.readPixel(serverpf.bpp/8, serverpf.bigEndian);
}
handler.fillRect(r, pix[0]);
return;
}
// "JPEG" compression type.
if (comp_ctl == rfbTightJpeg) {
DECOMPRESS_JPEG_RECT(r, is, handler);
return;
}
// Quit on unsupported compression type.
if (comp_ctl > rfbTightMaxSubencoding) {
throw new Exception("TightDecoder: bad subencoding value received");
}
// "Basic" compression type.
int palSize = 0;
int[] palette = new int[256];
boolean useGradient = false;
if ((comp_ctl & rfbTightExplicitFilter) != 0) {
int filterId = is.readU8();
switch (filterId) {
case rfbTightFilterPalette:
palSize = is.readU8() + 1;
byte[] tightPalette;
if (cutZeros) {
tightPalette = new byte[256 * 3];
is.readBytes(tightPalette, 0, palSize * 3);
serverpf.bufferFromRGB(palette, 0, tightPalette, 0, palSize);
} else {
is.readPixels(palette, palSize, serverpf.bpp/8, serverpf.bigEndian);
}
break;
case rfbTightFilterGradient:
useGradient = true;
break;
case rfbTightFilterCopy:
break;
default:
throw new Exception("TightDecoder: unknown filter code recieved");
}
}
int bppp = bpp;
if (palSize != 0) {
bppp = (palSize <= 2) ? 1 : 8;
} else if (cutZeros) {
bppp = 24;
}
// Determine if the data should be decompressed or just copied.
int rowSize = (r.width() * bppp + 7) / 8;
int dataSize = r.height() * rowSize;
int streamId = -1;
InStream input;
if (dataSize < rfbTightMinToCompress) {
input = is;
} else {
int length = is.readCompactLength();
streamId = comp_ctl & 0x03;
zis[streamId].setUnderlying(is, length);
input = (ZlibInStream)zis[streamId];
}
// Allocate netbuf and read in data
byte[] netbuf = new byte[dataSize];
input.readBytes(netbuf, 0, dataSize);
int stride = r.width();
int[] buf = reader.getImageBuf(r.area());
if (palSize == 0) {
// Truecolor data.
if (useGradient) {
if (bpp == 32 && cutZeros) {
FilterGradient24(netbuf, buf, stride, r);
} else {
FilterGradient(netbuf, buf, stride, r);
}
} else {
// Copy
int h = r.height();
int ptr = 0;
int srcPtr = 0;
int w = r.width();
if (cutZeros) {
serverpf.bufferFromRGB(buf, ptr, netbuf, srcPtr, w*h);
} else {
int pixelSize = (bpp >= 24) ? 3 : bpp/8;
while (h > 0) {
for (int i = 0; i < w; i++) {
if (bpp == 8) {
buf[ptr+i] = netbuf[srcPtr+i] & 0xff;
} else {
for (int j = pixelSize-1; j >= 0; j--)
buf[ptr+i] |= ((netbuf[srcPtr+i+j] & 0xff) << j*8);
}
}
ptr += stride;
srcPtr += w * pixelSize;
h--;
}
}
}
} else {
// Indexed color
int x, h = r.height(), w = r.width(), b, pad = stride - w;
int ptr = 0;
int srcPtr = 0, bits;
if (palSize <= 2) {
// 2-color palette
while (h > 0) {
for (x = 0; x < w / 8; x++) {
bits = netbuf[srcPtr++];
for(b = 7; b >= 0; b--) {
buf[ptr++] = palette[bits >> b & 1];
}
}
if (w % 8 != 0) {
bits = netbuf[srcPtr++];
for (b = 7; b >= 8 - w % 8; b--) {
buf[ptr++] = palette[bits >> b & 1];
}
}
ptr += pad;
h--;
}
} else {
// 256-color palette
while (h > 0) {
int endOfRow = ptr + w;
while (ptr < endOfRow) {
buf[ptr++] = palette[netbuf[srcPtr++] & 0xff];
}
ptr += pad;
h--;
}
}
}
handler.imageRect(r, buf);
if (streamId != -1) {
zis[streamId].reset();
}
}
final private void DECOMPRESS_JPEG_RECT(Rect r, InStream is, CMsgHandler handler)
{
// Read length
int compressedLen = is.readCompactLength();
if (compressedLen <= 0)
vlog.info("Incorrect data received from the server.");
// Allocate netbuf and read in data
byte[] netbuf = new byte[compressedLen];
is.readBytes(netbuf, 0, compressedLen);
// Create an Image object from the JPEG data.
Image jpeg = tk.createImage(netbuf);
jpeg.setAccelerationPriority(1);
handler.imageRect(r, jpeg);
jpeg.flush();
}
final private void FilterGradient24(byte[] netbuf, int[] buf, int stride,
Rect r)
{
int x, y, c;
byte[] prevRow = new byte[TIGHT_MAX_WIDTH*3];
byte[] thisRow = new byte[TIGHT_MAX_WIDTH*3];
byte[] pix = new byte[3];
int[] est = new int[3];
// Set up shortcut variables
int rectHeight = r.height();
int rectWidth = r.width();
for (y = 0; y < rectHeight; y++) {
/* First pixel in a row */
for (c = 0; c < 3; c++) {
pix[c] = (byte)(netbuf[y*rectWidth*3+c] + prevRow[c]);
thisRow[c] = pix[c];
}
serverpf.bufferFromRGB(buf, y*stride, pix, 0, 1);
/* Remaining pixels of a row */
for (x = 1; x < rectWidth; x++) {
for (c = 0; c < 3; c++) {
est[c] = (int)(prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c]);
if (est[c] > 0xFF) {
est[c] = 0xFF;
} else if (est[c] < 0) {
est[c] = 0;
}
pix[c] = (byte)(netbuf[(y*rectWidth+x)*3+c] + est[c]);
thisRow[x*3+c] = pix[c];
}
serverpf.bufferFromRGB(buf, y*stride+x, pix, 0, 1);
}
System.arraycopy(thisRow, 0, prevRow, 0, prevRow.length);
}
}
final private void FilterGradient(byte[] netbuf, int[] buf, int stride,
Rect r)
{
int x, y, c;
byte[] prevRow = new byte[TIGHT_MAX_WIDTH];
byte[] thisRow = new byte[TIGHT_MAX_WIDTH];
byte[] pix = new byte[3];
int[] est = new int[3];
// Set up shortcut variables
int rectHeight = r.height();
int rectWidth = r.width();
for (y = 0; y < rectHeight; y++) {
/* First pixel in a row */
// FIXME
//serverpf.rgbFromBuffer(pix, 0, netbuf, y*rectWidth, 1, cm);
for (c = 0; c < 3; c++)
pix[c] += prevRow[c];
System.arraycopy(pix, 0, thisRow, 0, pix.length);
serverpf.bufferFromRGB(buf, y*stride, pix, 0, 1);
/* Remaining pixels of a row */
for (x = 1; x < rectWidth; x++) {
for (c = 0; c < 3; c++) {
est[c] = (int)(prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c]);
if (est[c] > 0xff) {
est[c] = 0xff;
} else if (est[c] < 0) {
est[c] = 0;
}
}
// FIXME
//serverpf.rgbFromBuffer(pix, 0, netbuf, y*rectWidth+x, 1, cm);
for (c = 0; c < 3; c++)
pix[c] += est[c];
System.arraycopy(pix, 0, thisRow, x*3, pix.length);
serverpf.bufferFromRGB(buf, y*stride+x, pix, 0, 1);
}
System.arraycopy(thisRow, 0, prevRow, 0, prevRow.length);
}
}
private CMsgReader reader;
private ZlibInStream[] zis;
private PixelFormat serverpf;
private PixelFormat clientpf;
static LogWriter vlog = new LogWriter("TightDecoder");
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy