...

Source file src/golang.org/x/image/webp/decode.go

Documentation: golang.org/x/image/webp

     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  }
   272  

View as plain text