webp.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  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. // Original code was cropped and fixed by @DarthSim for imgproxy needs
  5. package main
  6. import (
  7. "errors"
  8. "image"
  9. "image/color"
  10. "io"
  11. "golang.org/x/image/riff"
  12. "golang.org/x/image/vp8"
  13. "golang.org/x/image/vp8l"
  14. )
  15. var errInvalidFormat = errors.New("webp: invalid format")
  16. var (
  17. fccALPH = riff.FourCC{'A', 'L', 'P', 'H'}
  18. fccVP8 = riff.FourCC{'V', 'P', '8', ' '}
  19. fccVP8L = riff.FourCC{'V', 'P', '8', 'L'}
  20. fccVP8X = riff.FourCC{'V', 'P', '8', 'X'}
  21. fccWEBP = riff.FourCC{'W', 'E', 'B', 'P'}
  22. )
  23. // Since we need this only for type detecting, we can return fake image
  24. func decodeWebp(r io.Reader) (image.Image, error) {
  25. return image.NewRGBA(image.Rect(0, 0, 1, 1)), nil
  26. }
  27. func decodeWebpConfig(r io.Reader) (image.Config, error) {
  28. formType, riffReader, err := riff.NewReader(r)
  29. if err != nil {
  30. return image.Config{}, err
  31. }
  32. if formType != fccWEBP {
  33. return image.Config{}, errInvalidFormat
  34. }
  35. var (
  36. wantAlpha bool
  37. buf [10]byte
  38. )
  39. for {
  40. chunkID, chunkLen, chunkData, err := riffReader.Next()
  41. if err == io.EOF {
  42. err = errInvalidFormat
  43. }
  44. if err != nil {
  45. return image.Config{}, err
  46. }
  47. switch chunkID {
  48. case fccALPH:
  49. // Ignore
  50. case fccVP8:
  51. if wantAlpha || int32(chunkLen) < 0 {
  52. return image.Config{}, errInvalidFormat
  53. }
  54. d := vp8.NewDecoder()
  55. d.Init(chunkData, int(chunkLen))
  56. fh, err := d.DecodeFrameHeader()
  57. return image.Config{
  58. ColorModel: color.YCbCrModel,
  59. Width: fh.Width,
  60. Height: fh.Height,
  61. }, err
  62. case fccVP8L:
  63. if wantAlpha {
  64. return image.Config{}, errInvalidFormat
  65. }
  66. return vp8l.DecodeConfig(chunkData)
  67. case fccVP8X:
  68. if chunkLen != 10 {
  69. return image.Config{}, errInvalidFormat
  70. }
  71. if _, err := io.ReadFull(chunkData, buf[:10]); err != nil {
  72. return image.Config{}, err
  73. }
  74. const alphaBit = 1 << 4
  75. wantAlpha = buf[0] != alphaBit
  76. widthMinusOne := uint32(buf[4]) | uint32(buf[5])<<8 | uint32(buf[6])<<16
  77. heightMinusOne := uint32(buf[7]) | uint32(buf[8])<<8 | uint32(buf[9])<<16
  78. return image.Config{
  79. ColorModel: color.NYCbCrAModel,
  80. Width: int(widthMinusOne) + 1,
  81. Height: int(heightMinusOne) + 1,
  82. }, nil
  83. default:
  84. return image.Config{}, errInvalidFormat
  85. }
  86. }
  87. }
  88. func init() {
  89. image.RegisterFormat("webp", "RIFF????WEBPVP8", decodeWebp, decodeWebpConfig)
  90. }