github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/image/webp/decode.go (about)

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