git.lucas.co / go_mono
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 }