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