mirror of
				https://github.com/imgproxy/imgproxy.git
				synced 2025-10-30 23:08:02 +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:
		| @@ -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. | ||||
|   | ||||
| @@ -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
									
								
							
							
						
						
									
										53
									
								
								imagemeta/jpeg_test.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										
											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
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testdata/test1.jpg
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.5 KiB | 
		Reference in New Issue
	
	Block a user