1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-01-03 10:43:58 +02:00

Support arithmetic encoded jpeg files (#909)

* Support arithmetic encoded jpeg files

* Skip special markers within the SOF markers range

* Refactor imagemeta.DecodeJpegMeta()
This commit is contained in:
Joe Cai 2022-07-12 22:53:03 +10:00 committed by GitHub
parent e7d1dde5ea
commit 76897dbf1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 82 additions and 15 deletions

View File

@ -4,6 +4,7 @@
### Add
- Add support of 16-bit BMP.
- Add `IMGPROXY_NEW_RELIC_LABELS` config.
- Add support of JPEG files with differential Huffman coding or arithmetic coding.
### Fix
- Fix trimming of CMYK images.

View File

@ -8,13 +8,25 @@ import (
)
const (
jpegSof0Marker = 0xc0 // Start Of Frame (Baseline Sequential).
jpegSof2Marker = 0xc2 // Start Of Frame (Progressive).
jpegRst0Marker = 0xd0 // ReSTart (0).
jpegRst7Marker = 0xd7 // ReSTart (7).
jpegSoiMarker = 0xd8 // Start Of Image.
jpegEoiMarker = 0xd9 // End Of Image.
jpegSosMarker = 0xda // Start Of Scan.
// 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 {
@ -89,11 +101,14 @@ func DecodeJpegMeta(rr io.Reader) (Meta, error) {
}
n := int(tmp[0])<<8 + int(tmp[1]) - 2
if n <= 0 {
// We should fail here, but libvips if more tolerant to this, so, contunue
// We should fail here, but libvips is more tolerant to this, so, continue
continue
}
if marker >= jpegSof0Marker && marker <= jpegSof2Marker {
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
}
@ -107,16 +122,14 @@ func DecodeJpegMeta(rr io.Reader) (Meta, error) {
width: int(tmp[3])<<8 + int(tmp[4]),
height: int(tmp[1])<<8 + int(tmp[2]),
}, nil
}
if marker == jpegSosMarker {
case jpegSosMarker:
return nil, JpegFormatError("missing SOF marker")
}
if n > 0 {
if _, err := r.Discard(n); err != nil {
return nil, err
}
// Skip any other uninteresting segments
if _, err := r.Discard(n); err != nil {
return nil, err
}
}
}

53
imagemeta/jpeg_test.go Normal file
View File

@ -0,0 +1,53 @@
package imagemeta
import (
"os"
"path/filepath"
"testing"
"github.com/imgproxy/imgproxy/v3/imagetype"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
type JpegTestSuite struct {
suite.Suite
}
func (s *JpegTestSuite) openFile(name string) *os.File {
wd, err := os.Getwd()
require.Nil(s.T(), err)
path := filepath.Join(wd, "..", "testdata", name)
f, err := os.Open(path)
require.Nil(s.T(), err)
return f
}
func (s *JpegTestSuite) TestDecodeJpegMeta() {
files := []string{
"test1.jpg",
"test1.arith.jpg",
}
expectedMeta := &meta{
format: imagetype.JPEG,
width: 10,
height: 10,
}
for _, file := range files {
func() {
f := s.openFile(file)
defer f.Close()
metadata, err := DecodeJpegMeta(f)
assert.Nil(s.T(), err)
assert.Equal(s.T(), expectedMeta, metadata)
}()
}
}
func TestJpeg(t *testing.T) {
suite.Run(t, new(JpegTestSuite))
}

BIN
testdata/test1.arith.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

BIN
testdata/test1.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB