func()

in vncclient/encoding.go [678:850]


func (t *TightEncoding) readBasicCompression(c *ClientConn, rect *Rectangle, r io.Reader, readFilterID bool, stream uint8) (enc Encoding, e error) {
	var filterID uint8
	if readFilterID {
		// If the compression type is BasicCompression and bit 6 (the
		// read-filter-id bit) of the compression-control byte was set
		// to 1, then the next (second) byte specifies filter-id which
		// tells the decoder what filter type was used by the encoder
		// to pre-process pixel data before the compression.
		if err := binary.Read(r, binary.BigEndian, &filterID); err != nil {
			return nil, err
		}
		t.size++
	} else {
		// If bit 6 of the compression-control byte is set to 0 (no
		// filter-id byte), then the CopyFilter is used.
		filterID = 0
	}

	// The filter-id byte can be one of the following:
	//
	//  +--------------+------+---------+------------------------+
	//  | No. of bytes | Type | [Value] | Description            |
	//  +--------------+------+---------+------------------------+
	//  | 1            | U8   |         | filter-id              |
	//  +--------------+------+---------+------------------------+
	//  |              |      | 0       | CopyFilter (no filter) |
	//  +--------------+------+---------+------------------------+
	//  |              |      | 1       | PaletteFilter          |
	//  +--------------+------+---------+------------------------+
	//  |              |      | 2       | GradientFilter         |
	//  +--------------+------+---------+------------------------+
	log.Debug("stream: ", stream)
	switch filterID {
	// CopyFilter
	case 0:
		log.Debug("CopyFilter")
		// When the CopyFilter is active, raw pixel values in TPIXEL
		// format will be compressed.
		size := rect.Area() * 3
		r, err := t.basicCompressionReader(r, size, stream)
		if err != nil {
			return nil, err
		}
		t.buf.Reset()
		colors, err := t.readTPixels(r, size/3)

		// Copy the colors slice. It uses the same underlying memory as
		// t.buf, but when it is used to update a screen later we might
		// have already modified t.buf while reading a new frame.
		return &TightEncoding{Colors: append([]Color{}, colors...), size: t.size}, err
	// PaletteFilter
	case 1:
		log.Debug("PaletteFilter")
		// The PaletteFilter converts true-color pixel data to indexed
		// colors and a palette which can consist of 2..256 colors.
		//
		// When the PaletteFilter is used, the palette is sent before
		// the pixel data. The palette begins with an unsigned byte
		// which value is the number of colors in the palette minus 1
		// (i.e. 1 means 2 colors, 255 means 256 colors in the palette).
		// Then follows the palette itself which consist of pixel values
		// in TPIXEL format.
		var p uint8
		if err := binary.Read(r, binary.BigEndian, &p); err != nil {
			return nil, err
		}
		paletteSize := int(p) + 1
		t.buf.Reset()
		palette, err := t.readTPixels(r, paletteSize)
		if err != nil {
			return nil, err
		}
		palette = append([]Color{}, palette...)

		// If the number of colors is 2, then each pixel is encoded in
		// 1 bit, otherwise 8 bits are used to encode one pixel. 1-bit
		// encoding is performed such way that the most significant
		// bits correspond to the leftmost pixels, and each row of
		// pixels is aligned to the byte boundary.
		size := rect.Area()
		if paletteSize == 2 {
			size = ((int(rect.Width) + 7) / 8) * int(rect.Height)
		}

		r, err := t.basicCompressionReader(r, size, stream)
		if err != nil {
			return nil, err
		}
		if err = t.readToBuf(r, size); err != nil {
			return nil, err
		}
		buf := t.buf.Bytes()
		colors := make([]Color, rect.Area())
		if paletteSize == 2 {
			offset := uint8(8)
			index := -1
			for i := range colors {
				if offset == 0 || i%int(rect.Width) == 0 {
					offset = 8
					index++
				}
				offset--
				colors[i] = palette[(buf[index]>>offset)&0x01]
			}
		} else {
			for i := range colors {
				if int(buf[i]) >= paletteSize {
					return nil, errors.Errorf("invalid index %d in palette of size %d", buf[i], paletteSize)
				}
				colors[i] = palette[uint8(buf[i])]
			}
		}
		return &TightEncoding{Colors: colors, size: t.size}, nil
	// GradientFilter
	case 2:
		log.Debug("GradientFilter")
		// Note: The GradientFilter may only be used when bits-per-
		// pixel is either 16 or 32.
		if c.PixelFormat.BPP != 16 && c.PixelFormat.BPP != 32 {
			return nil, errors.Errorf("can't use GradientFilter with bitsPerPixel of %v", c.PixelFormat.BPP)
		}

		size := rect.Area() * 3
		r, err := t.basicCompressionReader(r, size, stream)
		if err != nil {
			return nil, err
		}
		t.buf.Reset()
		diffs, err := t.readTPixels(r, size)
		if err != nil {
			return nil, err
		}

		// The GradientFilter pre-processes pixel data with a simple
		// algorithm which converts each color component to a
		// difference between a "predicted" intensity and the actual
		// intensity. Such a technique does not affect uncompressed
		// data size, but helps to compress photo-like images better.
		// Pseudo-code for converting intensities to differences
		// follows:
		//
		//     P[i,j] := V[i-1,j] + V[i,j-1] - V[i-1,j-1];
		//     if (P[i,j] < 0) then P[i,j] := 0;
		//     if (P[i,j] > MAX) then P[i,j] := MAX;
		//     D[i,j] := V[i,j] - P[i,j];
		//
		// Here V[i,j] is the intensity of a color component for a
		// pixel at coordinates (i,j). For pixels outside the current
		// rectangle, V[i,j] is assumed to be zero (which is relevant
		// for P[i,0] and P[0,j]). MAX is the maximum intensity value
		// for a color component.
		colors := make([]Color, size/3)
		cr := colorRect{width: int(rect.Width), colors: colors}
		for i := 0; i < int(rect.Height); i++ {
			for j := 0; j < int(rect.Width); j++ {
				for c := 0; c < 3; c++ {
					p := cr.at(i-1, j, c) + cr.at(i, j-1, c) - cr.at(i-1, j-1, c)
					if p < 0 {
						p = 0
					}
					if p > 255 {
						p = 255
					}
					*component(colors[i], c) = *component(diffs[i], c) + p
				}
			}
		}
		return &TightEncoding{Colors: colors, size: t.size}, nil
	default:
		return nil, errors.Errorf("invalid filter-id byte: %b", filterID)
	}

}