vncclient/encoding.go (555 lines of code) (raw):

package vncclient import ( "bytes" "compress/zlib" "encoding/binary" "fmt" "io" "github.com/juju/errors" logging "github.com/op/go-logging" "github.com/pixiv/go-libjpeg/jpeg" ) var log = logging.MustGetLogger("vncclient") // An Encoding implements a method for encoding pixel data that is // sent by the server to the client. type Encoding interface { // The number that uniquely identifies this encoding type. Type() int32 // Read reads the contents of the encoded pixel data from the reader. // This should return a new Encoding implementation that contains // the proper data. Read(*ClientConn, *Rectangle, io.Reader) (Encoding, error) Size() int } type QualityLevel uint32 func (f QualityLevel) Size() int { return 0 } func (f QualityLevel) Type() int32 { return -32 + int32(f) } func (f QualityLevel) Read(c *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) { return f, errors.NotImplementedf("quality level is a pseudo-encoding") } // Compression level type CompressLevel uint32 func (l CompressLevel) Size() int { return 0 } func (l CompressLevel) Type() int32 { return -256 + int32(l) } func (l CompressLevel) Read(c *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) { return l, errors.NotImplementedf("compress level is a pseudo-encoding") } type FineQualityLevel uint32 func (f FineQualityLevel) Size() int { return 0 } func (f FineQualityLevel) Type() int32 { return -512 + int32(f) } func (f FineQualityLevel) Read(c *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) { return f, errors.NotImplementedf("fine quality level is a pseudo-encoding") } type SubsampleLevel uint32 func (s SubsampleLevel) Size() int { return 0 } func (s SubsampleLevel) Type() int32 { return -768 + int32(s) } func (s SubsampleLevel) Read(c *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) { return s, errors.NotImplementedf("fine quality level is a pseudo-encoding") } // RawEncoding is raw pixel data sent by the server. // // See RFC 6143 Section 7.7.1 type RawEncoding struct { Colors []Color } func (r *RawEncoding) Size() int { return len(r.Colors) * 3 } func (*RawEncoding) Type() int32 { return 0 } func (*RawEncoding) Read(c *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) { bytesPerPixel := c.PixelFormat.BPP / 8 // Various housekeeping helpers pixelBytes := make([]uint8, bytesPerPixel) var byteOrder binary.ByteOrder = binary.LittleEndian if c.PixelFormat.BigEndian { byteOrder = binary.BigEndian } // Output buffer colors := make([]Color, rect.Area()) // Read all needed bytes: this improves performance so we // don't have to do piecemeal unbuffered reads. buf := make([]byte, rect.Area()*int(bytesPerPixel)) if _, err := io.ReadFull(r, buf); err != nil { return nil, err } r = bytes.NewBuffer(buf) for y := uint16(0); y < rect.Height; y++ { for x := uint16(0); x < rect.Width; x++ { if _, err := io.ReadFull(r, pixelBytes); err != nil { return nil, err } var rawPixel uint32 if c.PixelFormat.BPP == 8 { rawPixel = uint32(pixelBytes[0]) } else if c.PixelFormat.BPP == 16 { rawPixel = uint32(byteOrder.Uint16(pixelBytes)) } else if c.PixelFormat.BPP == 32 { rawPixel = byteOrder.Uint32(pixelBytes) } color := &colors[int(y)*int(rect.Width)+int(x)] if c.PixelFormat.TrueColor { color.R = uint8((rawPixel >> c.PixelFormat.RedShift) & uint32(c.PixelFormat.RedMax)) color.G = uint8((rawPixel >> c.PixelFormat.GreenShift) & uint32(c.PixelFormat.GreenMax)) color.B = uint8((rawPixel >> c.PixelFormat.BlueShift) & uint32(c.PixelFormat.BlueMax)) } else { *color = c.ColorMap[rawPixel] } } } return &RawEncoding{colors}, nil } // ZRLEEncoding is Zlib run-length encoded pixel data // // See RFC 6143 Section 7.7.6 type ZRLEEncoding struct { Colors []Color size int32 } func (*ZRLEEncoding) Type() int32 { return 16 } func (z *ZRLEEncoding) Size() int { return int(z.size) } func (z *ZRLEEncoding) Read(c *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) { var length int32 if err := binary.Read(r, binary.BigEndian, &length); err != nil { return nil, err } // Could maybe get by without the copy compressed := make([]uint8, length) if err := binary.Read(r, binary.BigEndian, &compressed); err != nil { return nil, err } inflated, err := c.inflator.Inflate(compressed) if err != nil { return nil, errors.Annotate(err, "could not inflate") } // It's now safe to start reading other ZRLE messages if desired log.Debugf("expanded zlib: %d bytes -> %d bytes", len(compressed), len(inflated)) // TODO: other format checks here if c.PixelFormat.BPP < 24 { return nil, errors.Errorf("unsupported bitsPerPixel: %d", c.PixelFormat.BPP) } // data := base64.StdEncoding.EncodeToString(inflated) // log.Infof("payload %v %v %v %v: %v", rect.X, rect.Y, rect.Width, rect.Height, data) buf := NewQuickBuf(inflated) colors, err := z.parse(rect, buf) if err != nil { return nil, errors.Annotatef(err, "could not parse ZRLEEncoding colors") } if buf.Len() != 0 { return nil, errors.Errorf("BUG: buffer still had %d unread bytes", buf.Len()) } // buf := bytes.NewBuffer(inflated) return &ZRLEEncoding{colors, length}, nil } func (z *ZRLEEncoding) parse(rect *Rectangle, r *QuickBuf) ([]Color, error) { colors := make([]Color, rect.Area()) // We pass in a scratch buffer so that parseTile doesn't need // to allocate its own. A better implementation would probably // write directly into the colors buffer. scratch := make([]Color, 64*64) for tileY := uint16(0); tileY < rect.Height; tileY += 64 { tileHeight := min(64, rect.Height-tileY) for tileX := uint16(0); tileX < rect.Width; tileX += 64 { tileWidth := min(64, rect.Width-tileX) err := z.parseTile(rect, colors, r, tileX, tileY, tileWidth, tileHeight, scratch[:int(tileHeight)*int(tileWidth)]) if err != nil { return nil, err } } } return colors, nil } func (*ZRLEEncoding) parseTile(rect *Rectangle, colors []Color, r *QuickBuf, tileX, tileY, tileWidth, tileHeight uint16, scratch []Color) error { // Each tile begins with a subencoding type byte. The top bit of this // byte is set if the tile has been run-length encoded, clear otherwise. // The bottom 7 bits indicate the size of the palette used: zero means // no palette, 1 means that the tile is of a single color, and 2 to 127 // indicate a palette of that size. The special subencoding values 129 // and 127 indicate that the palette is to be reused from the last tile // that had a palette, with and without RLE, respectively. subencoding, err := r.ReadByte() if err != nil { return errors.Annotate(err, "failed to read subencoding") } runLengthEncoded := subencoding&128 != 0 paletteSize := subencoding & 127 paletteData, err := r.ReadColors(int(paletteSize)) if err != nil { return errors.Annotatef(err, "failed to read palette: runLengthEncoded:%v paletteSize:%v", runLengthEncoded, paletteSize) } if paletteSize == 0 && !runLengthEncoded { // 0: Raw pixel data. width*height pixel values follow (where width and // height are the width and height of the tile): // // +-----------------------------+--------------+-------------+ // | No. of bytes | Type [Value] | Description | // +-----------------------------+--------------+-------------+ // | width*height*BytesPerCPixel | CPIXEL array | pixels | // +-----------------------------+--------------+-------------+ colors, err := r.ReadColors(len(scratch)) if err != nil { return errors.Annotate(err, "failed to read raw colors") } // Don't bother with the scratch buffer scratch = colors } else if paletteSize == 1 && !runLengthEncoded { // 1: A solid tile consisting of a single color. The pixel value // follows: // // +----------------+--------------+-------------+ // | No. of bytes | Type [Value] | Description | // +----------------+--------------+-------------+ // | bytesPerCPixel | CPIXEL | pixelValue | // +----------------+--------------+-------------+ pixelValue := paletteData[0] fillColor(scratch, pixelValue) } else if !runLengthEncoded { // 2 to 16: Packed palette types. The paletteSize is the value of the // subencoding, which is followed by the palette, consisting of // paletteSize pixel values. The packed pixels follow, with each // pixel represented as a bit field yielding a zero-based index into // the palette. For paletteSize 2, a 1-bit field is used; for // paletteSize 3 or 4, a 2-bit field is used; and for paletteSize // from 5 to 16, a 4-bit field is used. The bit fields are packed // into bytes, with the most significant bits representing the // leftmost pixel (i.e., big endian). For tiles not a multiple of 8, // 4, or 2 pixels wide (as appropriate), padding bits are used to // align each row to an exact number of bytes. // +----------------------------+--------------+--------------+ // | No. of bytes | Type [Value] | Description | // +----------------------------+--------------+--------------+ // | paletteSize*bytesPerCPixel | CPIXEL array | palette | // | m | U8 array | packedPixels | // +----------------------------+--------------+--------------+ // where m is the number of bytes representing the packed pixels. // For paletteSize of 2, this is div(width+7,8)*height; for // paletteSize of 3 or 4, this is div(width+3,4)*height; or for // paletteSize of 5 to 16, this is div(width+1,2)*height. var bitsPerPackedPixel uint8 if paletteSize > 16 { // No palette reuse in zrle bitsPerPackedPixel = 8 } else if paletteSize > 4 { bitsPerPackedPixel = 4 } else if paletteSize > 2 { bitsPerPackedPixel = 2 } else { bitsPerPackedPixel = 1 } for j := uint16(0); j < tileHeight; j++ { // We discard any leftover bits for each new line var b uint8 var nbits uint8 for i := uint16(0); i < tileWidth; i++ { if nbits == 0 { b, err = r.ReadByte() if err != nil { return errors.Annotate(err, "failed to read nbits byte") } nbits = 8 } nbits -= bitsPerPackedPixel paletteIdx := (b >> nbits) & ((1 << bitsPerPackedPixel) - 1) & 127 pixelValue := paletteData[paletteIdx] scratch[j*tileWidth+i] = pixelValue } } } else if runLengthEncoded && paletteSize == 0 { // 128: Plain RLE. The data consists of a number of runs, repeated // until the tile is done. Runs may continue from the end of one row // to the beginning of the next. Each run is represented by a single // pixel value followed by the length of the run. The length is // represented as one or more bytes. The length is calculated as one // more than the sum of all the bytes representing the length. Any // byte value other than 255 indicates the final byte. So for // example, length 1 is represented as [0], 255 as [254], 256 as // [255,0], 257 as [255,1], 510 as [255,254], 511 as [255,255,0], and // so on. // // +-------------------------+--------------+-----------------------+ // | No. of bytes | Type [Value] | Description | // +-------------------------+--------------+-----------------------+ // | bytesPerCPixel | CPIXEL | pixelValue | // | div(runLength - 1, 255) | U8 array | 255 | // | 1 | U8 | (runLength-1) mod 255 | // +-------------------------+--------------+-----------------------+ for pos := 0; pos < len(scratch); { pixelValue, err := r.ReadColor() if err != nil { return err } count := 1 for b := uint8(255); b == 255; { b, err = r.ReadByte() if err != nil { return errors.Annotate(err, "failed to read rle byte") } count += int(b) } fillColor2(scratch[pos:pos+count], pixelValue) pos += count } } else if runLengthEncoded && paletteSize > 1 { // 130 to 255: Palette RLE. Followed by the palette, consisting of // paletteSize = (subencoding - 128) pixel values: // // +----------------------------+--------------+-------------+ // | No. of bytes | Type [Value] | Description | // +----------------------------+--------------+-------------+ // | paletteSize*bytesPerCPixel | CPIXEL array | palette | // +----------------------------+--------------+-------------+ // // Following the palette is, as with plain RLE, a number of runs, // repeated until the tile is done. A run of length one is // represented simply by a palette index: // // +--------------+--------------+--------------+ // | No. of bytes | Type [Value] | Description | // +--------------+--------------+--------------+ // | 1 | U8 | paletteIndex | // +--------------+--------------+--------------+ // // A run of length more than one is represented by a palette index // with the top bit set, followed by the length of the run as for // plain RLE. // // +-------------------------+--------------+-----------------------+ // | No. of bytes | Type [Value] | Description | // +-------------------------+--------------+-----------------------+ // | 1 | U8 | paletteIndex + 128 | // | div(runLength - 1, 255) | U8 array | 255 | // | 1 | U8 | (runLength-1) mod 255 | // +-------------------------+--------------+-----------------------+ for pos := 0; pos < len(scratch); { paletteIdx, err := r.ReadByte() if err != nil { return errors.Annotate(err, "failed to read palette index") } count := 1 if paletteIdx&128 != 0 { for b := uint8(255); b == 255; { b, err = r.ReadByte() if err != nil { return errors.Annotate(err, "failed to read byte") } count += int(b) } } paletteIdx &= 127 pixelValue := paletteData[paletteIdx] fillColor(scratch[pos:pos+count], pixelValue) pos += count } } else { return errors.Errorf("Unhandled case: runLengthEncoded=%v paletteSize=%v", runLengthEncoded, paletteSize) } for j := 0; j < int(tileHeight); j++ { off := int(tileY)*int(rect.Width) + int(tileX) start := j*int(rect.Width) + off copy(colors[start:start+int(tileWidth)], scratch[j*int(tileWidth):]) } return nil } func min(x, y uint16) uint16 { if x > y { return y } return x } func fillColor(dst []Color, pixelValue Color) { dst[0] = pixelValue for bp := 1; bp < len(dst); { copy(dst[bp:], dst[:bp]) bp *= 2 } } func fillColor2(dst []Color, pixelValue Color) { for i := range dst { dst[i] = pixelValue } } type readCloseResetter interface { io.ReadCloser zlib.Resetter } // Superceded by the Fine Quality Level / Compress Level options type JPEGQuality uint8 func (JPEGQuality) Size() int { return 0 } func (j JPEGQuality) Type() int32 { return -32 + int32(j) } func (j JPEGQuality) Read(*ClientConn, *Rectangle, io.Reader) (Encoding, error) { return j, errors.NotImplementedf("jpeg quality is a pseudo-encoding") } // TightEncoding provides efficient compression for pixel data. // // Spec: // https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#tight-encoding type TightEncoding struct { Colors []Color streams [4]readCloseResetter // streamBufs holds the underlying io.Readers for the zlib streams. // We can't share the same underlying io.Reader between the streams // because zlib does not always read all available data in a given frame. // // TODO: could skip the bytes.Buffer and read directly from Readers // here. But see zlib note: // // If r does not implement io.ByteReader, the decompressor may read // more data than necessary from r. // // Reading more data than necessary might cause it to encounter an // unwanted EOF, while providing an unbuffered ByteReader // implementationa might be slower than using bytes.Buffer anyway. streamBufs [4]*bytes.Buffer // reset is a bitmap that represents which zlib streams need // to be reset before their next use. reset uint8 buf *bytes.Buffer size int } func (*TightEncoding) Type() int32 { return 7 } func (t *TightEncoding) Size() int { return t.size } 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) } } 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) } } // colorRect simplifies gradient filter computation type colorRect struct { width int colors []Color } func (r *colorRect) at(y, x, c int) uint8 { if y < 0 || x < 0 { return 0 } return *component(r.colors[y*r.width+x], c) } func component(c Color, x int) *uint8 { switch x { case 0: return &c.R case 1: return &c.G case 2: return &c.B } panic(fmt.Sprintf("bad component number: %v", component)) } // readCompressedBytes reads compressed data from r. // func (t *TightEncoding) readCompressedBytes(r io.Reader, size int, stream uint8) ([]byte, error) { // basicCompressionReader returns an io.Reader that decompresses data from r. func (t *TightEncoding) basicCompressionReader(r io.Reader, size int, stream uint8) (out io.Reader, err error) { // After the pixel data has been filtered with one of the above three // filters, it is compressed using the zlib library. But if the data // size after applying the filter but before the compression is less // then 12, then the data is sent as is, uncompressed. if size < 12 { return io.LimitReader(r, int64(size)), nil } // Four separate zlib streams (0..3) can be used and the // decoder should read the actual stream id from the // compression-control byte (see [NOTE1]). // // If the compression is not used, then the pixel data is sent // as is, otherwise the data stream looks like this: // // +--------------+----------+----------------------------------+ // | No. of bytes | Type | Description | // +--------------+----------+----------------------------------+ // | 1-3 | | length in compact representation | // +--------------+----------+----------------------------------+ // | length | U8 array | zlibData | // +--------------+----------+----------------------------------+ length, err := t.readCompactLength(byteIOReader{Reader: r}) if err != nil { return nil, errors.Trace(err) } t.size += length buf := t.streamBufs[stream] if t.reset&(1<<stream) != 0 { buf.Reset() } buf.Grow(length) if _, err = buf.ReadFrom(io.LimitReader(r, int64(length))); err != nil { return nil, errors.Trace(err) } if t.streams[stream] == nil { zr, err := zlib.NewReader(buf) if err != nil { return nil, errors.Trace(err) } t.streams[stream] = zr.(readCloseResetter) } // NOTE1: The decoder must reset the zlib streams before // decoding the rectangle, if some of the bits 0, 1, 2 and 3 in // the compression-control byte are set to 1. Note that the // decoder must reset the indicated zlib streams even if the // compression type is FillCompression or JpegCompression. if t.reset&(1<<stream) != 0 { t.streams[stream].Reset(buf, nil) t.reset &^= 1 << stream } return t.streams[stream], nil } // readTPixels reads Colors in TPIXEL format from r. Uses t.buf as buffer space. // // NOTE: to simplify implementation, it expects the 3-byte-per-pixel version // of TPIXEL, which means it is not compatible with all pixel formats. // // NOTE: the returned []Color uses the same memory as t.buf, and so will // no longer be valid once t.buf changes. func (t *TightEncoding) readTPixels(r io.Reader, n int) ([]Color, error) { if t.buf.Len() != 0 { panic("unread bytes in t.buf before call to readTPixels") } if err := t.readToBuf(r, n*3); err != nil { return nil, err } t.size += n * 3 return (&QuickBuf{buf: t.buf.Next(n * 3)}).ReadColors(n) } func (t *TightEncoding) readCompactLength(r io.ByteReader) (int, error) { // length is compactly represented in one, two or three bytes, // according to the following scheme: // // +----------------------------+---------------------------+ // | Value | Description | // +----------------------------+---------------------------+ // | 0xxxxxxx | for values 0..127 | // +----------------------------+---------------------------+ // | 1xxxxxxx 0yyyyyyy | for values 128..16383 | // +----------------------------+---------------------------+ // | 1xxxxxxx 1yyyyyyy zzzzzzzz | for values 16384..4194303 | // +----------------------------+---------------------------+ // // Here each character denotes one bit, xxxxxxx are the least // significant 7 bits of the value (bits 0-6), yyyyyyy are bits 7-13, // and zzzzzzzz are the most significant 8 bits (bits 14-21). For // example, decimal value 10000 should be represented as two bytes: // binary 10010000 01001110, or hexadecimal 90 4E. // Implementation adapted from encoding/binary.ReadUvarint. var x uint64 var s uint for i := 0; ; i++ { b, err := r.ReadByte() if err != nil { return 0, err } t.size++ if b < 0x80 || i == 2 { return int(x | uint64(b)<<s), nil } x |= uint64(b&0x7f) << s s += 7 } } func (t *TightEncoding) readToBuf(r io.Reader, n int) error { t.buf.Grow(n) _, err := t.buf.ReadFrom(io.LimitReader(r, int64(n))) return err } // byteIOReader implements both io.ByteReader and io.Reader type byteIOReader struct { io.Reader buf [1]byte } func (b byteIOReader) ReadByte() (byte, error) { _, err := b.Read(b.buf[:]) return b.buf[0], err }