297 lines
8.3 KiB
Java
297 lines
8.3 KiB
Java
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
|
* Copyright (C) 2019 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 java.awt.image.*;
|
|
import java.nio.*;
|
|
import java.util.*;
|
|
|
|
import com.tigervnc.rdr.*;
|
|
|
|
public class ZRLEDecoder extends Decoder {
|
|
|
|
private static ByteBuffer readOpaque24A(InStream is)
|
|
{
|
|
is.check(3);
|
|
ByteBuffer r = ByteBuffer.allocate(4);
|
|
r.put(0, (byte)is.readU8());
|
|
r.put(1, (byte)is.readU8());
|
|
r.put(2, (byte)is.readU8());
|
|
return r;
|
|
}
|
|
|
|
private static ByteBuffer readOpaque24B(InStream is)
|
|
{
|
|
is.check(3);
|
|
ByteBuffer r = ByteBuffer.allocate(4);
|
|
r.put(1, (byte)is.readU8());
|
|
r.put(2, (byte)is.readU8());
|
|
r.put(3, (byte)is.readU8());
|
|
return r;
|
|
}
|
|
|
|
public ZRLEDecoder() {
|
|
super(DecoderFlags.DecoderOrdered);
|
|
zis = new ZlibInStream();
|
|
}
|
|
|
|
public void readRect(Rect r, InStream is,
|
|
ServerParams server, OutStream os)
|
|
{
|
|
int len;
|
|
|
|
len = is.readU32();
|
|
os.writeU32(len);
|
|
os.copyBytes(is, len);
|
|
}
|
|
|
|
public void decodeRect(Rect r, Object buffer,
|
|
int buflen, ServerParams server,
|
|
ModifiablePixelBuffer pb)
|
|
{
|
|
MemInStream is = new MemInStream((byte[])buffer, 0, buflen);
|
|
PixelFormat pf = server.pf();
|
|
switch (pf.bpp) {
|
|
case 8: zrleDecode8(r, is, zis, pf, pb); break;
|
|
case 16: zrleDecode16(r, is, zis, pf, pb); break;
|
|
case 32:
|
|
{
|
|
if (pf.depth <= 24) {
|
|
int maxPixel = pf.pixelFromRGB(-1, -1, -1, pf.getColorModel());
|
|
boolean fitsInLS3Bytes = maxPixel < (1<<24);
|
|
boolean fitsInMS3Bytes = (maxPixel & 0xff) == 0;
|
|
|
|
if ((fitsInLS3Bytes && pf.isLittleEndian()) ||
|
|
(fitsInMS3Bytes && pf.isBigEndian()))
|
|
{
|
|
zrleDecode24A(r, is, zis, pf, pb);
|
|
break;
|
|
}
|
|
|
|
if ((fitsInLS3Bytes && pf.isBigEndian()) ||
|
|
(fitsInMS3Bytes && pf.isLittleEndian()))
|
|
{
|
|
zrleDecode24B(r, is, zis, pf, pb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
zrleDecode32(r, is, zis, pf, pb);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static enum PIXEL_T { U8, U16, U24A, U24B, U32 };
|
|
|
|
private static ByteBuffer READ_PIXEL(InStream is, PIXEL_T type) {
|
|
switch (type) {
|
|
case U8:
|
|
return ByteBuffer.allocate(1).put(0, (byte)is.readOpaque8());
|
|
case U16:
|
|
return ByteBuffer.allocate(2).putShort(0, (short)is.readOpaque16());
|
|
case U24A:
|
|
return readOpaque24A(is);
|
|
case U24B:
|
|
return readOpaque24B(is);
|
|
default:
|
|
return ByteBuffer.allocate(4).putInt(0, is.readOpaque32());
|
|
}
|
|
}
|
|
|
|
private void zrleDecode8(Rect r, InStream is,
|
|
ZlibInStream zis,
|
|
PixelFormat pf, ModifiablePixelBuffer pb)
|
|
{
|
|
ZRLE_DECODE(r, is, zis, pf, pb, PIXEL_T.U8);
|
|
}
|
|
|
|
private void zrleDecode16(Rect r, InStream is,
|
|
ZlibInStream zis,
|
|
PixelFormat pf, ModifiablePixelBuffer pb)
|
|
{
|
|
ZRLE_DECODE(r, is, zis, pf, pb, PIXEL_T.U16);
|
|
}
|
|
|
|
private void zrleDecode24A(Rect r, InStream is,
|
|
ZlibInStream zis,
|
|
PixelFormat pf, ModifiablePixelBuffer pb)
|
|
{
|
|
ZRLE_DECODE(r, is, zis, pf, pb, PIXEL_T.U24A);
|
|
}
|
|
|
|
private void zrleDecode24B(Rect r, InStream is,
|
|
ZlibInStream zis,
|
|
PixelFormat pf, ModifiablePixelBuffer pb)
|
|
{
|
|
ZRLE_DECODE(r, is, zis, pf, pb, PIXEL_T.U24B);
|
|
}
|
|
|
|
private void zrleDecode32(Rect r, InStream is,
|
|
ZlibInStream zis,
|
|
PixelFormat pf, ModifiablePixelBuffer pb)
|
|
{
|
|
ZRLE_DECODE(r, is, zis, pf, pb, PIXEL_T.U32);
|
|
}
|
|
|
|
private void ZRLE_DECODE(Rect r, InStream is,
|
|
ZlibInStream zis,
|
|
PixelFormat pf, ModifiablePixelBuffer pb,
|
|
PIXEL_T pix_t)
|
|
{
|
|
int length = is.readU32();
|
|
zis.setUnderlying(is, length);
|
|
Rect t = new Rect();
|
|
ByteBuffer buf = ByteBuffer.allocate(64 * 64 * pf.bpp/8);
|
|
|
|
for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) {
|
|
|
|
t.br.y = Math.min(r.br.y, t.tl.y + 64);
|
|
|
|
for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) {
|
|
|
|
t.br.x = Math.min(r.br.x, t.tl.x + 64);
|
|
|
|
int mode = zis.readU8();
|
|
boolean rle = (mode & 128) != 0;
|
|
int palSize = mode & 127;
|
|
ByteBuffer palette = ByteBuffer.allocate(128 * pf.bpp/8);
|
|
|
|
for (int i = 0; i < palSize; i++) {
|
|
palette.put(READ_PIXEL(zis, pix_t));
|
|
}
|
|
palette.flip();
|
|
|
|
if (palSize == 1) {
|
|
byte[] pix = new byte[pf.bpp/8];
|
|
palette.get(pix);
|
|
pb.fillRect(pf, t, pix);
|
|
continue;
|
|
}
|
|
|
|
if (!rle) {
|
|
if (palSize == 0) {
|
|
|
|
// raw
|
|
switch (pix_t) {
|
|
case U24A:
|
|
case U24B:
|
|
ByteBuffer ptr = buf.duplicate();
|
|
for (int i=0; i < t.area(); i++) {
|
|
ptr.put(READ_PIXEL(zis, pix_t));
|
|
}
|
|
break;
|
|
default:
|
|
zis.readBytes(buf.duplicate(), t.area() * (pf.bpp/8));
|
|
}
|
|
|
|
} else {
|
|
|
|
// packed pixels
|
|
int bppp = ((palSize > 16) ? 8 :
|
|
((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1)));
|
|
|
|
ByteBuffer ptr = buf.duplicate();
|
|
|
|
for (int i = 0; i < t.height(); i++) {
|
|
int eol = ptr.position() + t.width()*pf.bpp/8;
|
|
int b = 0;
|
|
int nbits = 0;
|
|
|
|
while (ptr.position() < eol) {
|
|
if (nbits == 0) {
|
|
b = zis.readU8();
|
|
nbits = 8;
|
|
}
|
|
nbits -= bppp;
|
|
int index = (b >> nbits) & ((1 << bppp) - 1) & 127;
|
|
ptr.put(palette.array(), index*pf.bpp/8, pf.bpp/8);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
if (palSize == 0) {
|
|
|
|
// plain RLE
|
|
|
|
ByteBuffer ptr = buf.duplicate();
|
|
int end = ptr.position() + t.area()*pf.bpp/8;
|
|
while (ptr.position() < end) {
|
|
ByteBuffer pix = READ_PIXEL(zis, pix_t);
|
|
int len = 1;
|
|
int b;
|
|
do {
|
|
b = zis.readU8();
|
|
len += b;
|
|
} while (b == 255);
|
|
|
|
if (end - ptr.position() < len*pf.bpp/8) {
|
|
System.err.println("ZRLE decode error\n");
|
|
throw new Exception("ZRLE decode error");
|
|
}
|
|
|
|
while (len-- > 0) ptr.put(pix.array());
|
|
|
|
}
|
|
} else {
|
|
|
|
// palette RLE
|
|
|
|
ByteBuffer ptr = buf.duplicate();
|
|
int end = ptr.position() + t.area()*pf.bpp/8;
|
|
while (ptr.position() < end) {
|
|
int index = zis.readU8();
|
|
int len = 1;
|
|
if ((index & 128) != 0) {
|
|
int b;
|
|
do {
|
|
b = zis.readU8();
|
|
len += b;
|
|
} while (b == 255);
|
|
|
|
if (end - ptr.position() < len*pf.bpp/8) {
|
|
System.err.println("ZRLE decode error\n");
|
|
throw new Exception("ZRLE decode error");
|
|
}
|
|
}
|
|
|
|
index &= 127;
|
|
|
|
ByteBuffer pix = ByteBuffer.allocate(pf.bpp/8);
|
|
pix.put(palette.array(), index*pf.bpp/8, pf.bpp/8);
|
|
|
|
while (len-- > 0) ptr.put(pix.array());
|
|
}
|
|
}
|
|
}
|
|
|
|
pb.imageRect(pf, t, buf.array());
|
|
}
|
|
}
|
|
|
|
zis.flushUnderlying();
|
|
zis.setUnderlying(null, 0);
|
|
}
|
|
|
|
private ZlibInStream zis;
|
|
}
|