gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }