// Copyright 2014 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.
// Package vp8l implements a decoder for the VP8L lossless image format.//// The VP8L specification is at:// https://developers.google.com/speed/webp/docs/riff_container
package vp8l // import "golang.org/x/image/vp8l"import ()var ( errInvalidCodeLengths = errors.New("vp8l: invalid code lengths") errInvalidHuffmanTree = errors.New("vp8l: invalid Huffman tree"))// colorCacheMultiplier is the multiplier used for the color cache hash// function, specified in section 4.2.3.const colorCacheMultiplier = 0x1e35a7bd// distanceMapTable is the look-up table for distanceMap.var distanceMapTable = [120]uint8{0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a,0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a,0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b,0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03,0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c,0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e,0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b,0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f,0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b,0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41,0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f,0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70,}// distanceMap maps a LZ77 backwards reference distance to a two-dimensional// pixel offset, specified in section 4.2.2.func distanceMap( int32, uint32) int32 {ifint32() > int32(len(distanceMapTable)) {returnint32() - int32(len(distanceMapTable)) } := int32(distanceMapTable[-1]) := >> 4 := 8 - &0xfif := * + ; >= 1 {return }return1}// decoder holds the bit-stream for a VP8L image.type decoder struct { r io.ByteReader bits uint32 nBits uint32}// read reads the next n bits from the decoder's bit-stream.func ( *decoder) ( uint32) (uint32, error) {for .nBits < { , := .r.ReadByte()if != nil {if == io.EOF { = io.ErrUnexpectedEOF }return0, } .bits |= uint32() << .nBits .nBits += 8 } := .bits & (1<< - 1) .bits >>= .nBits -= return , nil}// decodeTransform decodes the next transform and the width of the image after// transformation (or equivalently, before inverse transformation), specified// in section 3.func ( *decoder) ( int32, int32) ( transform, int32, error) { .oldWidth = .transformType, = .read(2)if != nil {returntransform{}, 0, }switch .transformType {casetransformTypePredictor, transformTypeCrossColor: .bits, = .read(3)if != nil {returntransform{}, 0, } .bits += 2 .pix, = .decodePix(nTiles(, .bits), nTiles(, .bits), 0, false)if != nil {returntransform{}, 0, }casetransformTypeSubtractGreen:// No-op.casetransformTypeColorIndexing: , := .read(8)if != nil {returntransform{}, 0, } ++ .bits = 0switch {case <= 2: .bits = 3case <= 4: .bits = 2case <= 16: .bits = 1 } = nTiles(, .bits) , := .decodePix(int32(), 1, 4*256, false)if != nil {returntransform{}, 0, }for := 4; < len(); += 4 { [+0] += [-4] [+1] += [-3] [+2] += [-2] [+3] += [-1] }// The spec says that "if the index is equal or larger than color_table_size, // the argb color value should be set to 0x00000000 (transparent black)." // We re-slice up to 256 4-byte pixels. .pix = [:4*256] }return , , nil}// repeatsCodeLength is the minimum code length for repeated codes.const repeatsCodeLength = 16// These magic numbers are specified at the end of section 5.2.2.// The 3-length arrays apply to code lengths >= repeatsCodeLength.var ( codeLengthCodeOrder = [19]uint8{17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, } repeatBits = [3]uint8{2, 3, 7} repeatOffsets = [3]uint8{3, 3, 11})// decodeCodeLengths decodes a Huffman tree's code lengths which are themselves// encoded via a Huffman tree, specified in section 5.2.2.func ( *decoder) ( []uint32, []uint32) error { := hTree{}if := .build(); != nil {return } := len() , := .read(1)if != nil {return }if != 0 { , := .read(3)if != nil {return } = 2 + 2* , := .read()if != nil {return } = int() + 2if > len() {returnerrInvalidCodeLengths } }// The spec says that "if code 16 [meaning repeat] is used before // a non-zero value has been emitted, a value of 8 is repeated." := uint32(8)for := 0; < len(); {if == 0 {break } -- , := .next()if != nil {return }if < repeatsCodeLength { [] = ++if != 0 { = }continue } , := .read(uint32(repeatBits[-repeatsCodeLength]))if != nil {return } += uint32(repeatOffsets[-repeatsCodeLength])if +int() > len() {returnerrInvalidCodeLengths }// A code length of 16 repeats the previous non-zero code. // A code length of 17 or 18 repeats zeroes. := uint32(0)if == 16 { = }for ; > 0; -- { [] = ++ } }returnnil}// decodeHuffmanTree decodes a Huffman tree into h.// h may be nil, in which case we decode and discard the tree.func ( *decoder) ( *hTree, uint32) error { , := .read(1)if != nil {return }if != 0 { , := .read(1)if != nil {return } ++ , := .read(1)if != nil {return } = 7* + 1var [2]uint32 [0], = .read()if != nil {return }if == 2 { [1], = .read(8)if != nil {return } }if == nil {returnnil }return .buildSimple(, , ) } , := .read(4)if != nil {return } += 4ifint() > len(codeLengthCodeOrder) {returnerrInvalidHuffmanTree } := [len(codeLengthCodeOrder)]uint32{}for := uint32(0); < ; ++ { [codeLengthCodeOrder[]], = .read(3)if != nil {return } } := make([]uint32, )if = .decodeCodeLengths(, [:]); != nil {return }if == nil {returnnil }return .build()}const ( huffGreen = 0 huffRed = 1 huffBlue = 2 huffAlpha = 3 huffDistance = 4 nHuff = 5)// hGroup is an array of 5 Huffman trees.type hGroup [nHuff]hTree// decodeHuffmanGroups decodes the one or more hGroups used to decode the pixel// data. If one hGroup is used for the entire image, then hPix and hBits will// be zero. If more than one hGroup is used, then hPix contains the meta-image// that maps tiles to hGroup index, and hBits contains the log-2 tile size.func ( *decoder) ( int32, int32, bool, uint32) ( []hGroup, []byte, uint32, error) {// hPix maps tiles to hGroup indices. // The number of hGroups is determined by the maximum index. // VP8L permits unused hGroups in the input. // While the number of used hGroups is bounded by the number of tiles, // the number of decoded hGroups is only bounded by the maximum hGroup index (2^16-1). // // When the total number of hGroups is large, we store only the referenced hGroups // and rewrite hGroup indices in hPix. // // When mapping is nil, we do no remapping. // Otherwise, for hGroup i (where i is the real index of the group), // mapping[i] is -1 for an unreferenced group or the remapped index otherwise.var []int16 := 0 := int16(1)if { , := .read(1)if != nil {returnnil, nil, 0, }if != 0 { , = .read(3)if != nil {returnnil, nil, 0, } += 2 := nTiles(, ) := nTiles(, ) := * , = .decodePix(, , 0, false)if != nil {returnnil, nil, 0, }for := 0; < len(); += 4 { := int([])<<8 | int([+1])if < { = } }// The standard allows for up to 2^16-1 hGroups. // // An input file containing a 1600x900 image can include // and reference every possible group. With maximum-size trees // (a ~12MiB input file), this is >3GiB of allocation. // // libwebp will decode a file containing 2^16-1 hGroups, // but it won't produce one. It encodes at most 2600 hGroups // (MAX_HUFF_IMAGE_SIZE, src/dec/common_dec.h) // Reject any image using more.const = 2600constint16 = + 1// must fit in int16if >= {returnnil, nil, 0, errors.New("vp8l: too many Huffman trees") }// If maxHGroupIndex is too large, map indices to contiguous indices // to avoid allocating large slices of unused Huffman trees. // // The definition of "too large" matches the one used in libwebp // (see src/dec/vp8l_dec.c).if >= 1000 || int32() >= { = make([]int16, +1)for := range { [] = -1 } = 0for := 0; < len(); += 4 { := int([])<<8 | int([+1])if [] == -1 { [] = ++ } [] = byte([] >> 8) [+1] = byte([]) } } else { = int16( + 1) } } } = make([]hGroup, )for := range + 1 {var *hGroupif == nil { = &[] } elseif [] != -1 { = &[[]] }for , := rangealphabetSizes {if == 0 && > 0 { += 1 << }var *hTreeif != nil { = &[] }if := .decodeHuffmanTree(, ); != nil {returnnil, nil, 0, } } }return , , , nil}const ( nLiteralCodes = 256 nLengthCodes = 24 nDistanceCodes = 40)var alphabetSizes = [nHuff]uint32{nLiteralCodes + nLengthCodes,nLiteralCodes,nLiteralCodes,nLiteralCodes,nDistanceCodes,}// decodePix decodes pixel data, specified in section 5.2.2.func ( *decoder) ( int32, int32, int32, bool) ([]byte, error) {// Decode the color cache parameters. , , := uint32(0), uint32(0), ([]uint32)(nil) , := .read(1)if != nil {returnnil, }if != 0 { , = .read(4)if != nil {returnnil, }if < 1 || 11 < {returnnil, errors.New("vp8l: invalid color cache parameters") } = 32 - = make([]uint32, 1<<) }// Decode the Huffman groups. , , , := .decodeHuffmanGroups(, , , )if != nil {returnnil, } , := int32(0), int32(0)if != 0 { , = 1<<-1, nTiles(, ) }// Decode the pixels.if < 4** { = 4 * * } := make([]byte, 4**, ) , := 0, 0 , := int32(0), int32(0) , := &[0], != 0for < len() {if { := 4 * (*(>>) + ( >> )) = &[uint32([])<<8|uint32([+1])] } , := [huffGreen].next()if != nil {returnnil, }switch {case < nLiteralCodes:// We have a literal pixel. , := [huffRed].next()if != nil {returnnil, } , := [huffBlue].next()if != nil {returnnil, } , := [huffAlpha].next()if != nil {returnnil, } [+0] = uint8() [+1] = uint8() [+2] = uint8() [+3] = uint8() += 4 ++if == { , = 0, +1 } = != 0 && & == 0case < nLiteralCodes+nLengthCodes:// We have a LZ77 backwards reference. , := .lz77Param( - nLiteralCodes)if != nil {returnnil, } , := [huffDistance].next()if != nil {returnnil, } , := .lz77Param()if != nil {returnnil, } := distanceMap(, ) := + 4*int() := - 4*int() := - 4*int()if < 0 || len() < || < 0 || len() < {returnnil, errors.New("vp8l: invalid LZ77 parameters") }for ; < ; , = +1, +1 { [] = [] } += int32()for >= { , = -, +1 } = != 0default:// We have a color cache lookup. First, insert previous pixels // into the cache. Note that VP8L assumes ARGB order, but the // Go image.RGBA type is in RGBA order.for ; < ; += 4 { := uint32([+0])<<16 |uint32([+1])<<8 |uint32([+2])<<0 |uint32([+3])<<24 [(*colorCacheMultiplier)>>] = } -= nLiteralCodes + nLengthCodesifint() >= len() {returnnil, errors.New("vp8l: invalid color cache index") } := [] [+0] = uint8( >> 16) [+1] = uint8( >> 8) [+2] = uint8( >> 0) [+3] = uint8( >> 24) += 4 ++if == { , = 0, +1 } = != 0 && & == 0 } }return , nil}// lz77Param returns the next LZ77 parameter: a length or a distance, specified// in section 4.2.2.func ( *decoder) ( uint32) (uint32, error) {if < 4 {return + 1, nil } := ( - 2) >> 1 := (2 + &1) << , := .read()if != nil {return0, }return + + 1, nil}// decodeHeader decodes the VP8L header from r.func decodeHeader( io.Reader) ( *decoder, int32, int32, error) { , := .(io.ByteReader)if ! { = bufio.NewReader() } = &decoder{r: } , := .read(8)if != nil {returnnil, 0, 0, }if != 0x2f {returnnil, 0, 0, errors.New("vp8l: invalid header") } , := .read(14)if != nil {returnnil, 0, 0, } ++ , := .read(14)if != nil {returnnil, 0, 0, } ++ _, = .read(1) // Read and ignore the hasAlpha hint.if != nil {returnnil, 0, 0, } , := .read(3)if != nil {returnnil, 0, 0, }if != 0 {returnnil, 0, 0, errors.New("vp8l: invalid version") }return , int32(), int32(), nil}// DecodeConfig decodes the color model and dimensions of a VP8L image from r.func ( io.Reader) (image.Config, error) { , , , := decodeHeader()if != nil {returnimage.Config{}, }returnimage.Config{ColorModel: color.NRGBAModel,Width: int(),Height: int(), }, nil}// Decode decodes a VP8L image from r.func ( io.Reader) (image.Image, error) { , , , := decodeHeader()if != nil {returnnil, }// Decode the transforms.var (int [nTransformTypes]transform [nTransformTypes]bool = )for { , := .read(1)if != nil {returnnil, }if == 0 {break }vartransform , , = .decodeTransform(, )if != nil {returnnil, }if [.transformType] {returnnil, errors.New("vp8l: repeated transform") } [.transformType] = true [] = ++ }// Decode the transformed pixels. , := .decodePix(, , 0, true)if != nil {returnnil, }// Apply the inverse transformations.for := - 1; >= 0; -- { := &[] = inverseTransforms[.transformType](, , ) }return &image.NRGBA{Pix: ,Stride: 4 * int(),Rect: image.Rect(0, 0, int(), int()), }, nil}
The pages are generated with Goldsv0.8.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.