1
2
3
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
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
165
166
167
168
169
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,
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
187
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:
204 for i := 1; i < alphaStride; i++ {
205 alpha[i] += alpha[i-1]
206 }
207 for i := alphaStride; i < len(alpha); i += alphaStride {
208
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:
217
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:
227
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
234 alpha[i] += alpha[i-alphaStride]
235
236
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
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
263
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 }
272
View as plain text