mirror of
https://github.com/imgproxy/imgproxy.git
synced 2024-11-29 08:22:11 +02:00
76897dbf1b
* Support arithmetic encoded jpeg files * Skip special markers within the SOF markers range * Refactor imagemeta.DecodeJpegMeta()
140 lines
3.6 KiB
Go
140 lines
3.6 KiB
Go
package imagemeta
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/imagetype"
|
|
)
|
|
|
|
const (
|
|
// https://www.disktuna.com/list-of-jpeg-markers/
|
|
jpegSof0Marker = 0xc0 // Start Of Frame (Baseline Sequential).
|
|
jpegSof1Marker = 0xc1 // Start Of Frame (Extended Sequential DCT)
|
|
jpegSof2Marker = 0xc2 // Start Of Frame (Progressive DCT )
|
|
jpegSof3Marker = 0xc3 // Start Of Frame (Lossless sequential)
|
|
jpegSof5Marker = 0xc5 // Start Of Frame (Differential sequential DCT)
|
|
jpegSof6Marker = 0xc6 // Start Of Frame (Differential progressive DCT)
|
|
jpegSof7Marker = 0xc7 // Start Of Frame (Differential lossless sequential)
|
|
jpegSof9Marker = 0xc9 // Start Of Frame (Extended sequential DCT, Arithmetic coding)
|
|
jpegSof10Marker = 0xca // Start Of Frame (Progressive DCT, Arithmetic coding)
|
|
jpegSof11Marker = 0xcb // Start Of Frame (Lossless sequential, Arithmetic coding)
|
|
jpegSof13Marker = 0xcd // Start Of Frame (Differential sequential DCT, Arithmetic coding)
|
|
jpegSof14Marker = 0xce // Start Of Frame (Differential progressive DCT, Arithmetic coding)
|
|
jpegSof15Marker = 0xcf // Start Of Frame (Differential lossless sequential, Arithmetic coding).
|
|
jpegRst0Marker = 0xd0 // ReSTart (0).
|
|
jpegRst7Marker = 0xd7 // ReSTart (7).
|
|
jpegSoiMarker = 0xd8 // Start Of Image.
|
|
jpegEoiMarker = 0xd9 // End Of Image.
|
|
jpegSosMarker = 0xda // Start Of Scan.
|
|
)
|
|
|
|
type jpegReader interface {
|
|
io.Reader
|
|
ReadByte() (byte, error)
|
|
Discard(n int) (discarded int, err error)
|
|
}
|
|
|
|
func asJpegReader(r io.Reader) jpegReader {
|
|
if rr, ok := r.(jpegReader); ok {
|
|
return rr
|
|
}
|
|
return bufio.NewReader(r)
|
|
}
|
|
|
|
type JpegFormatError string
|
|
|
|
func (e JpegFormatError) Error() string { return "invalid JPEG format: " + string(e) }
|
|
|
|
func DecodeJpegMeta(rr io.Reader) (Meta, error) {
|
|
var tmp [512]byte
|
|
|
|
r := asJpegReader(rr)
|
|
|
|
if _, err := io.ReadFull(r, tmp[:2]); err != nil {
|
|
return nil, err
|
|
}
|
|
if tmp[0] != 0xff || tmp[1] != jpegSoiMarker {
|
|
return nil, JpegFormatError("missing SOI marker")
|
|
}
|
|
|
|
for {
|
|
_, err := io.ReadFull(r, tmp[:2])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// This is not a segment, continue searching
|
|
for tmp[0] != 0xff {
|
|
tmp[0] = tmp[1]
|
|
tmp[1], err = r.ReadByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
marker := tmp[1]
|
|
|
|
if marker == 0 {
|
|
// Treat "\xff\x00" as extraneous data.
|
|
continue
|
|
}
|
|
|
|
// Marker can be preceded by fill bytes
|
|
for marker == 0xff {
|
|
marker, err = r.ReadByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if marker == jpegEoiMarker { // End Of Image.
|
|
return nil, JpegFormatError("missing SOF marker")
|
|
}
|
|
|
|
if jpegRst0Marker <= marker && marker <= jpegRst7Marker {
|
|
continue
|
|
}
|
|
|
|
if _, err = io.ReadFull(r, tmp[:2]); err != nil {
|
|
return nil, err
|
|
}
|
|
n := int(tmp[0])<<8 + int(tmp[1]) - 2
|
|
if n <= 0 {
|
|
// We should fail here, but libvips is more tolerant to this, so, continue
|
|
continue
|
|
}
|
|
|
|
switch marker {
|
|
case jpegSof0Marker, jpegSof1Marker, jpegSof2Marker, jpegSof3Marker, jpegSof5Marker,
|
|
jpegSof6Marker, jpegSof7Marker, jpegSof9Marker, jpegSof10Marker, jpegSof11Marker,
|
|
jpegSof13Marker, jpegSof14Marker, jpegSof15Marker:
|
|
if _, err := io.ReadFull(r, tmp[:5]); err != nil {
|
|
return nil, err
|
|
}
|
|
// We only support 8-bit precision.
|
|
if tmp[0] != 8 {
|
|
return nil, JpegFormatError("unsupported precision")
|
|
}
|
|
|
|
return &meta{
|
|
format: imagetype.JPEG,
|
|
width: int(tmp[3])<<8 + int(tmp[4]),
|
|
height: int(tmp[1])<<8 + int(tmp[2]),
|
|
}, nil
|
|
|
|
case jpegSosMarker:
|
|
return nil, JpegFormatError("missing SOF marker")
|
|
}
|
|
|
|
// Skip any other uninteresting segments
|
|
if _, err := r.Discard(n); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
RegisterFormat("\xff\xd8", DecodeJpegMeta)
|
|
}
|