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