/* Copyright (C) 2002-2005 RealVNC Ltd. 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., 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 int 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 ((ByteBuffer)r.rewind()).getInt();
}
private static int readOpaque24B(InStream is)
{
is.check(3);
ByteBuffer r = ByteBuffer.allocate(4);
r.put(2, (byte)is.readU8());
r.put(1, (byte)is.readU8());
r.put(0, (byte)is.readU8());
return ((ByteBuffer)r.rewind()).getInt();
}
public ZRLEDecoder() {
super(DecoderFlags.DecoderOrdered);
zis = new ZlibInStream();
}
public void readRect(Rect r, InStream is,
ConnParams cp, OutStream os)
{
int len;
len = is.readU32();
os.writeU32(len);
os.copyBytes(is, len);
}
public void decodeRect(Rect r, Object buffer,
int buflen, ConnParams cp,
ModifiablePixelBuffer pb)
{
MemInStream is = new MemInStream((byte[])buffer, 0, buflen);
PixelFormat pf = cp.pf();
ByteBuffer buf = ByteBuffer.allocate(64 * 64 * 4);
switch (pf.bpp) {
case 8: zrleDecode8(r, is, zis, buf, pf, pb); break;
case 16: zrleDecode16(r, is, zis, buf, pf, pb); break;
case 32:
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, buf, pf, pb);
}
else if ((fitsInLS3Bytes && pf.isBigEndian()) ||
(fitsInMS3Bytes && pf.isLittleEndian()))
{
zrleDecode24B(r, is, zis, buf, pf, pb);
}
else
{
zrleDecode32(r, is, zis, buf, pf, pb);
}
break;
}
}
private static enum PIXEL_T { U8, U16, U24A, U24B, U32 };
private static ByteBuffer READ_PIXEL(InStream is, PIXEL_T type) {
ByteBuffer b = ByteBuffer.allocate(4);
switch (type) {
case U8:
b.putInt(is.readOpaque8());
return (ByteBuffer)ByteBuffer.allocate(1).put(b.get(3)).rewind();
case U16:
b.putInt(is.readOpaque16());
return (ByteBuffer)ByteBuffer.allocate(2).put(b.array(), 2, 2).rewind();
case U24A:
return (ByteBuffer)b.putInt(readOpaque24A(is)).rewind();
case U24B:
return (ByteBuffer)b.putInt(readOpaque24B(is)).rewind();
case U32:
default:
return (ByteBuffer)b.putInt(is.readOpaque32()).rewind();
}
}
private void zrleDecode8(Rect r, InStream is,
ZlibInStream zis, ByteBuffer buf,
PixelFormat pf, ModifiablePixelBuffer pb)
{
ZRLE_DECODE(r, is, zis, buf, pf, pb, PIXEL_T.U8);
}
private void zrleDecode16(Rect r, InStream is,
ZlibInStream zis, ByteBuffer buf,
PixelFormat pf, ModifiablePixelBuffer pb)
{
ZRLE_DECODE(r, is, zis, buf, pf, pb, PIXEL_T.U16);
}
private void zrleDecode24A(Rect r, InStream is,
ZlibInStream zis, ByteBuffer buf,
PixelFormat pf, ModifiablePixelBuffer pb)
{
ZRLE_DECODE(r, is, zis, buf, pf, pb, PIXEL_T.U24A);
}
private void zrleDecode24B(Rect r, InStream is,
ZlibInStream zis, ByteBuffer buf,
PixelFormat pf, ModifiablePixelBuffer pb)
{
ZRLE_DECODE(r, is, zis, buf, pf, pb, PIXEL_T.U24B);
}
private void zrleDecode32(Rect r, InStream is,
ZlibInStream zis, ByteBuffer buf,
PixelFormat pf, ModifiablePixelBuffer pb)
{
ZRLE_DECODE(r, is, zis, buf, pf, pb, PIXEL_T.U32);
}
private void ZRLE_DECODE(Rect r, InStream is,
ZlibInStream zis, ByteBuffer buf,
PixelFormat pf, ModifiablePixelBuffer pb,
PIXEL_T pix_t)
{
int length = is.readU32();
zis.setUnderlying(is, length);
Rect t = new Rect();
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));
}
if (palSize == 1) {
ByteBuffer pix =
ByteBuffer.allocate(pf.bpp/8).put(palette.array(), 0, pf.bpp/8);
pb.fillRect(pf, t, pix.array());
continue;
}
if (!rle) {
if (palSize == 0) {
// raw
switch (pix_t) {
case U24A:
case U24B:
ByteBuffer ptr = buf.duplicate();
for (int iptr=0; iptr < t.area(); iptr++) {
ptr.put(READ_PIXEL(zis, pix_t));
}
break;
default:
zis.readBytes(buf, 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);
}
} 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;
while (len-- > 0) ptr.put(palette.array(), index*pf.bpp/8, pf.bpp/8);
}
}
}
pb.imageRect(pf, t, buf.array());
}
}
zis.removeUnderlying();
}
private ZlibInStream zis;
}