github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/image/webp/decode.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package webp implements a decoder for WEBP images. 6 // 7 // WEBP is defined at: 8 // https://developers.google.com/speed/webp/docs/riff_container 9 package webp // import "golang.org/x/image/webp" 10 11 import ( 12 "bytes" 13 "errors" 14 "image" 15 "image/color" 16 "io" 17 18 "golang.org/x/image/riff" 19 "golang.org/x/image/vp8" 20 "golang.org/x/image/vp8l" 21 "golang.org/x/image/webp/nycbcra" 22 ) 23 24 var errInvalidFormat = errors.New("webp: invalid format") 25 26 var ( 27 fccALPH = riff.FourCC{'A', 'L', 'P', 'H'} 28 fccVP8 = riff.FourCC{'V', 'P', '8', ' '} 29 fccVP8L = riff.FourCC{'V', 'P', '8', 'L'} 30 fccVP8X = riff.FourCC{'V', 'P', '8', 'X'} 31 fccWEBP = riff.FourCC{'W', 'E', 'B', 'P'} 32 ) 33 34 func decode(r io.Reader, configOnly bool) (image.Image, image.Config, error) { 35 formType, riffReader, err := riff.NewReader(r) 36 if err != nil { 37 return nil, image.Config{}, err 38 } 39 if formType != fccWEBP { 40 return nil, image.Config{}, errInvalidFormat 41 } 42 43 var ( 44 alpha []byte 45 alphaStride int 46 wantAlpha bool 47 widthMinusOne uint32 48 heightMinusOne uint32 49 buf [10]byte 50 ) 51 for { 52 chunkID, chunkLen, chunkData, err := riffReader.Next() 53 if err == io.EOF { 54 err = errInvalidFormat 55 } 56 if err != nil { 57 return nil, image.Config{}, err 58 } 59 60 switch chunkID { 61 case fccALPH: 62 if !wantAlpha { 63 return nil, image.Config{}, errInvalidFormat 64 } 65 wantAlpha = false 66 // Read the Pre-processing | Filter | Compression byte. 67 if _, err := io.ReadFull(chunkData, buf[:1]); err != nil { 68 if err == io.EOF { 69 err = errInvalidFormat 70 } 71 return nil, image.Config{}, err 72 } 73 alpha, alphaStride, err = readAlpha(chunkData, widthMinusOne, heightMinusOne, buf[0]&0x03) 74 if err != nil { 75 return nil, image.Config{}, err 76 } 77 unfilterAlpha(alpha, alphaStride, (buf[0]>>2)&0x03) 78 79 case fccVP8: 80 if wantAlpha || int32(chunkLen) < 0 { 81 return nil, image.Config{}, errInvalidFormat 82 } 83 d := vp8.NewDecoder() 84 d.Init(chunkData, int(chunkLen)) 85 fh, err := d.DecodeFrameHeader() 86 if err != nil { 87 return nil, image.Config{}, err 88 } 89 if configOnly { 90 return nil, image.Config{ 91 ColorModel: color.YCbCrModel, 92 Width: fh.Width, 93 Height: fh.Height, 94 }, nil 95 } 96 m, err := d.DecodeFrame() 97 if err != nil { 98 return nil, image.Config{}, err 99 } 100 if alpha != nil { 101 return &nycbcra.Image{ 102 YCbCr: *m, 103 A: alpha, 104 AStride: alphaStride, 105 }, image.Config{}, nil 106 } 107 return m, image.Config{}, nil 108 109 case fccVP8L: 110 if wantAlpha || alpha != nil { 111 return nil, image.Config{}, errInvalidFormat 112 } 113 if configOnly { 114 c, err := vp8l.DecodeConfig(chunkData) 115 return nil, c, err 116 } 117 m, err := vp8l.Decode(chunkData) 118 return m, image.Config{}, err 119 120 case fccVP8X: 121 if chunkLen != 10 { 122 return nil, image.Config{}, errInvalidFormat 123 } 124 if _, err := io.ReadFull(chunkData, buf[:10]); err != nil { 125 return nil, image.Config{}, err 126 } 127 const ( 128 animationBit = 1 << 1 129 xmpMetadataBit = 1 << 2 130 exifMetadataBit = 1 << 3 131 alphaBit = 1 << 4 132 iccProfileBit = 1 << 5 133 ) 134 if buf[0] != alphaBit { 135 return nil, image.Config{}, errors.New("webp: non-Alpha VP8X is not implemented") 136 } 137 widthMinusOne = uint32(buf[4]) | uint32(buf[5])<<8 | uint32(buf[6])<<16 138 heightMinusOne = uint32(buf[7]) | uint32(buf[8])<<8 | uint32(buf[9])<<16 139 if configOnly { 140 return nil, image.Config{ 141 ColorModel: nycbcra.ColorModel, 142 Width: int(widthMinusOne) + 1, 143 Height: int(heightMinusOne) + 1, 144 }, nil 145 } 146 wantAlpha = true 147 148 default: 149 return nil, image.Config{}, errInvalidFormat 150 } 151 } 152 } 153 154 func readAlpha(chunkData io.Reader, widthMinusOne, heightMinusOne uint32, compression byte) ( 155 alpha []byte, alphaStride int, err error) { 156 157 switch compression { 158 case 0: 159 w := int(widthMinusOne) + 1 160 h := int(heightMinusOne) + 1 161 alpha = make([]byte, w*h) 162 if _, err := io.ReadFull(chunkData, alpha); err != nil { 163 return nil, 0, err 164 } 165 return alpha, w, nil 166 167 case 1: 168 // Read the VP8L-compressed alpha values. First, synthesize a 5-byte VP8L header: 169 // a 1-byte magic number, a 14-bit widthMinusOne, a 14-bit heightMinusOne, 170 // a 1-bit (ignored, zero) alphaIsUsed and a 3-bit (zero) version. 171 // TODO(nigeltao): be more efficient than decoding an *image.NRGBA just to 172 // extract the green values to a separately allocated []byte. Fixing this 173 // will require changes to the vp8l package's API. 174 if widthMinusOne > 0x3fff || heightMinusOne > 0x3fff { 175 return nil, 0, errors.New("webp: invalid format") 176 } 177 alphaImage, err := vp8l.Decode(io.MultiReader( 178 bytes.NewReader([]byte{ 179 0x2f, // VP8L magic number. 180 uint8(widthMinusOne), 181 uint8(widthMinusOne>>8) | uint8(heightMinusOne<<6), 182 uint8(heightMinusOne >> 2), 183 uint8(heightMinusOne >> 10), 184 }), 185 chunkData, 186 )) 187 if err != nil { 188 return nil, 0, err 189 } 190 // The green values of the inner NRGBA image are the alpha values of the 191 // outer NYCbCrA image. 192 pix := alphaImage.(*image.NRGBA).Pix 193 alpha = make([]byte, len(pix)/4) 194 for i := range alpha { 195 alpha[i] = pix[4*i+1] 196 } 197 return alpha, int(widthMinusOne) + 1, nil 198 } 199 return nil, 0, errInvalidFormat 200 } 201 202 func unfilterAlpha(alpha []byte, alphaStride int, filter byte) { 203 if len(alpha) == 0 || alphaStride == 0 { 204 return 205 } 206 switch filter { 207 case 1: // Horizontal filter. 208 for i := 1; i < alphaStride; i++ { 209 alpha[i] += alpha[i-1] 210 } 211 for i := alphaStride; i < len(alpha); i += alphaStride { 212 // The first column is equivalent to the vertical filter. 213 alpha[i] += alpha[i-alphaStride] 214 215 for j := 1; j < alphaStride; j++ { 216 alpha[i+j] += alpha[i+j-1] 217 } 218 } 219 220 case 2: // Vertical filter. 221 // The first row is equivalent to the horizontal filter. 222 for i := 1; i < alphaStride; i++ { 223 alpha[i] += alpha[i-1] 224 } 225 226 for i := alphaStride; i < len(alpha); i++ { 227 alpha[i] += alpha[i-alphaStride] 228 } 229 230 case 3: // Gradient filter. 231 // The first row is equivalent to the horizontal filter. 232 for i := 1; i < alphaStride; i++ { 233 alpha[i] += alpha[i-1] 234 } 235 236 for i := alphaStride; i < len(alpha); i += alphaStride { 237 // The first column is equivalent to the vertical filter. 238 alpha[i] += alpha[i-alphaStride] 239 240 // The interior is predicted on the three top/left pixels. 241 for j := 1; j < alphaStride; j++ { 242 c := int(alpha[i+j-alphaStride-1]) 243 b := int(alpha[i+j-alphaStride]) 244 a := int(alpha[i+j-1]) 245 x := a + b - c 246 if x < 0 { 247 x = 0 248 } else if x > 255 { 249 x = 255 250 } 251 alpha[i+j] += uint8(x) 252 } 253 } 254 } 255 } 256 257 // Decode reads a WEBP image from r and returns it as an image.Image. 258 func Decode(r io.Reader) (image.Image, error) { 259 m, _, err := decode(r, false) 260 if err != nil { 261 return nil, err 262 } 263 return m, err 264 } 265 266 // DecodeConfig returns the color model and dimensions of a WEBP image without 267 // decoding the entire image. 268 func DecodeConfig(r io.Reader) (image.Config, error) { 269 _, c, err := decode(r, true) 270 return c, err 271 } 272 273 func init() { 274 image.RegisterFormat("webp", "RIFF????WEBPVP8", Decode, DecodeConfig) 275 }