in vncclient/encoding.go [523:676]
func (t *TightEncoding) Read(c *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) {
t.size = 0
if t.buf == nil {
t.buf = bytes.NewBuffer(nil)
for i := range t.streamBufs {
t.streamBufs[i] = bytes.NewBuffer(nil)
}
}
// To reduce implementation complexity, the width of any Tight-encoded
// rectangle cannot exceed 2048 pixels. If a wider rectangle is
// desired, it must be split into several rectangles and each one
// should be encoded separately.
if rect.Width > 2048 {
return nil, errors.Errorf("rectangle too wide: %vpx. tight-encoded rectangles cannot be wider than 2048 pixels.", rect.Width)
}
// To simplify implementation at the cost of full spec compliance,
// we only accept the simplest case of pixel format.
if f := c.PixelFormat; f.BPP != 32 || f.Depth != 24 || !f.TrueColor || f.RedMax != 255 || f.GreenMax != 255 || f.BlueMax != 255 {
return nil, errors.Errorf("this implementation of Tight encoding does not support this pixel format: %#v", f)
}
// The first byte of each Tight-encoded rectangle is a compression-
// control byte:
//
// +---------------------+--------------+---------------------+
// | No. of bytes | Type [Value] | Description |
// +---------------------+--------------+---------------------+
// | 1 | U8 | compression-control |
// +---------------------+--------------+---------------------+
var compressionControl uint8
if err := binary.Read(r, binary.BigEndian, &compressionControl); err != nil {
return nil, err
}
t.size++
// The least significant four bits of the compression-control byte
// inform the client which zlib compression streams should be reset
// before decoding the rectangle. Each bit is independent and
// corresponds to a separate zlib stream that should be reset:
//
// +-----+----------------+
// | Bit | Description |
// +-----+----------------+
// | 0 | Reset stream 0 |
// +-----+----------------+
// | 1 | Reset stream 1 |
// +-----+----------------+
// | 2 | Reset stream 2 |
// +-----+----------------+
// | 3 | Reset stream 3 |
// +-----+----------------+
t.reset |= compressionControl & 0x0F
// One of three possible compression methods are supported in the Tight
// encoding. These are BasicCompression, FillCompression and
// JpegCompression. If the bit 7 (the most significant bit) of the
// compression-control byte is 0, then the compression type is
// BasicCompression.
if compressionControl>>7 == 0 {
// In that case, bits 7-4 (the most significant four bits) of
// compression-control should be interpreted as follows:
//
// +------+--------------+------------------+
// | Bits | Binary value | Description |
// +------+--------------+------------------+
// | 5-4 | 00 | Use stream 0 |
// +------+--------------+------------------+
// | | 01 | Use stream 1 |
// +------+--------------+------------------+
// | | 10 | Use stream 2 |
// +------+--------------+------------------+
// | | 11 | Use stream 3 |
// +------+--------------+------------------+
// | 6 | 0 | --- |
// +------+--------------+------------------+
// | | 1 | read-filter-id |
// +------+--------------+------------------+
// | 7 | 0 | BasicCompression |
// +------+--------------+------------------+
readFilterID := compressionControl>>6 == 1
stream := compressionControl >> 4 & 0x03
log.Debugf("BasicCompression")
return t.readBasicCompression(c, rect, r, readFilterID, stream)
}
// Otherwise, if the bit 7 of compression-control is set to 1, then the
// compression method is either FillCompression or JpegCompression,
// depending on other bits of the same byte:
//
// +------+--------------+------------------+
// | Bits | Binary value | Description |
// +------+--------------+------------------+
// | 7-4 | 1000 | FillCompression |
// +------+--------------+------------------+
// | | 1001 | JpegCompression |
// +------+--------------+------------------+
// | | any other | invalid |
// +------+--------------+------------------+
switch compressionControl >> 4 {
// FillCompression
case 8:
log.Debugf("FillCompression")
// If the compression type is FillCompression, then the only
// pixel value follows, in TPIXEL format. This value applies to
// all pixels of the rectangle.
t.buf.Reset()
fill, err := t.readTPixels(r, 1)
if err != nil {
return nil, err
}
colors := make([]Color, rect.Area())
for i := range colors {
colors[i] = fill[0]
}
return &TightEncoding{Colors: colors, size: t.size}, nil
// JpegCompression
case 9:
log.Debugf("JpegCompression")
// If the compression type is JpegCompression, the following data
// stream looks like this:
//
// +--------------+----------+----------------------------------+
// | No. of bytes | Type | Description |
// +--------------+----------+----------------------------------+
// | 1-3 | | length in compact representation |
// +--------------+----------+----------------------------------+
// | length | U8 array | jpeg-data |
// +--------------+----------+----------------------------------+
//
// The jpeg-data is a JFIF stream.
length, err := t.readCompactLength(byteIOReader{Reader: r})
if err != nil {
return nil, err
}
buf := io.LimitReader(r, int64(length))
img, err := jpeg.DecodeIntoRGB(buf, &jpeg.DecoderOptions{})
if err != nil {
return nil, errors.Annotate(err, "could not decode jpeg")
} else if img == nil {
return nil, errors.New("jpeg decoding returned nil (usually a result of the network being closed)")
}
t.size += length
qbuf := NewQuickBuf(img.Pix)
colors, err := qbuf.ReadColors(rect.Area())
if err != nil {
return nil, err
}
return &TightEncoding{Colors: colors, size: t.size}, nil
default:
return nil, errors.Errorf("invalid compression control byte: %b", compressionControl)
}
}