jpeg.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package imagemeta
  2. import (
  3. "bufio"
  4. "io"
  5. "github.com/imgproxy/imgproxy/v3/imagetype"
  6. )
  7. const (
  8. jpegSof0Marker = 0xc0 // Start Of Frame (Baseline Sequential).
  9. jpegSof2Marker = 0xc2 // Start Of Frame (Progressive).
  10. jpegRst0Marker = 0xd0 // ReSTart (0).
  11. jpegRst7Marker = 0xd7 // ReSTart (7).
  12. jpegSoiMarker = 0xd8 // Start Of Image.
  13. jpegEoiMarker = 0xd9 // End Of Image.
  14. jpegSosMarker = 0xda // Start Of Scan.
  15. )
  16. type jpegReader interface {
  17. io.Reader
  18. ReadByte() (byte, error)
  19. Discard(n int) (discarded int, err error)
  20. }
  21. func asJpegReader(r io.Reader) jpegReader {
  22. if rr, ok := r.(jpegReader); ok {
  23. return rr
  24. }
  25. return bufio.NewReader(r)
  26. }
  27. type JpegFormatError string
  28. func (e JpegFormatError) Error() string { return "invalid JPEG format: " + string(e) }
  29. func DecodeJpegMeta(rr io.Reader) (Meta, error) {
  30. var tmp [512]byte
  31. r := asJpegReader(rr)
  32. if _, err := io.ReadFull(r, tmp[:2]); err != nil {
  33. return nil, err
  34. }
  35. if tmp[0] != 0xff || tmp[1] != jpegSoiMarker {
  36. return nil, JpegFormatError("missing SOI marker")
  37. }
  38. for {
  39. _, err := io.ReadFull(r, tmp[:2])
  40. if err != nil {
  41. return nil, err
  42. }
  43. // This is not a segment, continue searching
  44. for tmp[0] != 0xff {
  45. tmp[0] = tmp[1]
  46. tmp[1], err = r.ReadByte()
  47. if err != nil {
  48. return nil, err
  49. }
  50. }
  51. marker := tmp[1]
  52. if marker == 0 {
  53. // Treat "\xff\x00" as extraneous data.
  54. continue
  55. }
  56. // Marker can be preceded by fill bytes
  57. for marker == 0xff {
  58. marker, err = r.ReadByte()
  59. if err != nil {
  60. return nil, err
  61. }
  62. }
  63. if marker == jpegEoiMarker { // End Of Image.
  64. return nil, JpegFormatError("missing SOF marker")
  65. }
  66. if jpegRst0Marker <= marker && marker <= jpegRst7Marker {
  67. continue
  68. }
  69. if _, err = io.ReadFull(r, tmp[:2]); err != nil {
  70. return nil, err
  71. }
  72. n := int(tmp[0])<<8 + int(tmp[1]) - 2
  73. if n <= 0 {
  74. // We should fail here, but libvips if more tolerant to this, so, contunue
  75. continue
  76. }
  77. if marker >= jpegSof0Marker && marker <= jpegSof2Marker {
  78. if _, err := io.ReadFull(r, tmp[:5]); err != nil {
  79. return nil, err
  80. }
  81. // We only support 8-bit precision.
  82. if tmp[0] != 8 {
  83. return nil, JpegFormatError("unsupported precision")
  84. }
  85. return &meta{
  86. format: imagetype.JPEG,
  87. width: int(tmp[3])<<8 + int(tmp[4]),
  88. height: int(tmp[1])<<8 + int(tmp[2]),
  89. }, nil
  90. }
  91. if marker == jpegSosMarker {
  92. return nil, JpegFormatError("missing SOF marker")
  93. }
  94. if n > 0 {
  95. if _, err := r.Discard(n); err != nil {
  96. return nil, err
  97. }
  98. }
  99. }
  100. }
  101. func init() {
  102. RegisterFormat("\xff\xd8", DecodeJpegMeta)
  103. }