4 Achegas 52f2991983 ... b3c0f1b33c

Autor SHA1 Mensaxe Data
  Viktor Sokolov b3c0f1b33c fix: condition in matrix_test hai 2 días
  Victor Sokolov ce2bc83ce7 SVG instance (#1551) hai 2 días
  Victor Sokolov 28f55c5b13 Introduced matrix save-load test (#1552) hai 2 días
  Victor Sokolov bec004c652 Use RecordCustomMetrics instead of telemetry sdk (#1550) hai 3 días
Modificáronse 100 ficheiros con 265 adicións e 246 borrados
  1. 6 0
      CHANGELOG.v4.md
  2. 0 1
      go.mod
  3. 0 2
      go.sum
  4. 0 111
      integration/load_test.go
  5. 128 0
      integration/matrix_test.go
  6. 6 2
      integration/processing_handler_test.go
  7. 12 7
      integration/suite.go
  8. 18 98
      monitoring/newrelic/newrelic.go
  9. 9 5
      processing/config.go
  10. 3 15
      processing/processing.go
  11. 3 0
      processing/processor.go
  12. 36 0
      processing/svg/config.go
  13. 0 0
      processing/svg/errors.go
  14. 39 4
      processing/svg/svg.go
  15. 5 1
      processing/svg/svg_test.go
  16. 0 0
      processing/svg/unsafe_attrs.go
  17. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/1-bpp.bmp.hash
  18. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/16-bpp.bmp.hash
  19. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/24-bpp-no-alpha-mask.bmp.hash
  20. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/24-bpp.bmp.hash
  21. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/32-bpp-with-alpha-self-gen.bmp.hash
  22. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/32-bpp-with-alpha.bmp.hash
  23. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/4-bpp.bmp.hash
  24. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/8-bpp-rle-move-to-x.bmp.hash
  25. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/8-bpp-rle-single-color.bmp.hash
  26. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/8-bpp-rle-small.bmp.hash
  27. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/8-bpp-rle.bmp.hash
  28. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/8-bpp.bmp.hash
  29. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/1-bpp.bmp.hash
  30. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/16-bpp.bmp.hash
  31. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/24-bpp-no-alpha-mask.bmp.hash
  32. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/24-bpp.bmp.hash
  33. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/32-bpp-with-alpha-self-gen.bmp.hash
  34. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/32-bpp-with-alpha.bmp.hash
  35. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/4-bpp.bmp.hash
  36. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/8-bpp-rle-move-to-x.bmp.hash
  37. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/8-bpp-rle-single-color.bmp.hash
  38. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/8-bpp-rle-small.bmp.hash
  39. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/8-bpp-rle.bmp.hash
  40. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/8-bpp.bmp.hash
  41. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/1-bpp.bmp.hash
  42. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/16-bpp.bmp.hash
  43. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/24-bpp-no-alpha-mask.bmp.hash
  44. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/24-bpp.bmp.hash
  45. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/32-bpp-with-alpha-self-gen.bmp.hash
  46. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/32-bpp-with-alpha.bmp.hash
  47. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/4-bpp.bmp.hash
  48. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/8-bpp-rle-move-to-x.bmp.hash
  49. 0 0
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/8-bpp-rle-single-color.bmp.hash
  50. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/8-bpp-rle-small.bmp.hash
  51. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/8-bpp-rle.bmp.hash
  52. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/8-bpp.bmp.hash
  53. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/1-bpp.bmp.hash
  54. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/16-bpp.bmp.hash
  55. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/24-bpp-no-alpha-mask.bmp.hash
  56. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/24-bpp.bmp.hash
  57. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/32-bpp-with-alpha-self-gen.bmp.hash
  58. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/32-bpp-with-alpha.bmp.hash
  59. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/4-bpp.bmp.hash
  60. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/8-bpp-rle-move-to-x.bmp.hash
  61. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/8-bpp-rle-single-color.bmp.hash
  62. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/8-bpp-rle-small.bmp.hash
  63. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/8-bpp-rle.bmp.hash
  64. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/8-bpp.bmp.hash
  65. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/1-bpp.bmp.hash
  66. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/16-bpp.bmp.hash
  67. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/24-bpp-no-alpha-mask.bmp.hash
  68. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/24-bpp.bmp.hash
  69. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/32-bpp-with-alpha-self-gen.bmp.hash
  70. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/32-bpp-with-alpha.bmp.hash
  71. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/4-bpp.bmp.hash
  72. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/8-bpp-rle-move-to-x.bmp.hash
  73. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/8-bpp-rle-single-color.bmp.hash
  74. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/8-bpp-rle-small.bmp.hash
  75. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/8-bpp-rle.bmp.hash
  76. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/8-bpp.bmp.hash
  77. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/1-bpp.bmp.hash
  78. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/16-bpp.bmp.hash
  79. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/24-bpp-no-alpha-mask.bmp.hash
  80. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/24-bpp.bmp.hash
  81. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/32-bpp-with-alpha-self-gen.bmp.hash
  82. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/32-bpp-with-alpha.bmp.hash
  83. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/4-bpp.bmp.hash
  84. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/8-bpp-rle-move-to-x.bmp.hash
  85. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/8-bpp-rle-single-color.bmp.hash
  86. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/8-bpp-rle-small.bmp.hash
  87. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/8-bpp-rle.bmp.hash
  88. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/8-bpp.bmp.hash
  89. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/1-bpp.bmp.hash
  90. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/16-bpp.bmp.hash
  91. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/24-bpp-no-alpha-mask.bmp.hash
  92. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/24-bpp.bmp.hash
  93. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/32-bpp-with-alpha-self-gen.bmp.hash
  94. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/32-bpp-with-alpha.bmp.hash
  95. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/4-bpp.bmp.hash
  96. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/8-bpp-rle-move-to-x.bmp.hash
  97. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/8-bpp-rle-single-color.bmp.hash
  98. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/8-bpp-rle-small.bmp.hash
  99. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/8-bpp-rle.bmp.hash
  100. BIN=BIN
      testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/8-bpp.bmp.hash

+ 6 - 0
CHANGELOG.v4.md

@@ -1,5 +1,11 @@
 # 📑 Changelog (version/4 dev)
 
+## 2025-10-02
+
+### 🆕 Added
+
+- Custom metrics are now reported as timeslices (see [New Relic’s documentation](https://docs.newrelic.com/docs/apm/agents/manage-apm-agents/agent-data/collect-custom-metrics/) for details). Metric names have been changed from "imgproxy.X" to "Custom/imgproxy/X".
+
 ## 2025-10-01
 
 ### 🆕 Added

+ 0 - 1
go.mod

@@ -35,7 +35,6 @@ require (
 	github.com/mattn/go-isatty v0.0.20
 	github.com/ncw/swift/v2 v2.0.4
 	github.com/newrelic/go-agent/v3 v3.40.1
-	github.com/newrelic/newrelic-telemetry-sdk-go v0.8.1
 	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.23.2
 	github.com/shirou/gopsutil v3.21.11+incompatible

+ 0 - 2
go.sum

@@ -348,8 +348,6 @@ github.com/ncw/swift/v2 v2.0.4 h1:hHWVFxn5/YaTWAASmn4qyq2p6OyP/Hm3vMLzkjEqR7w=
 github.com/ncw/swift/v2 v2.0.4/go.mod h1:cbAO76/ZwcFrFlHdXPjaqWZ9R7Hdar7HpjRXBfbjigk=
 github.com/newrelic/go-agent/v3 v3.40.1 h1:8nb4R252Fpuc3oySvlHpDwqySqaPWL5nf7ZVEhqtUeA=
 github.com/newrelic/go-agent/v3 v3.40.1/go.mod h1:X0TLXDo+ttefTIue1V96Y5seb8H6wqf6uUq4UpPsYj8=
-github.com/newrelic/newrelic-telemetry-sdk-go v0.8.1 h1:6OX5VXMuj2salqNBc41eXKz6K+nV6OB/hhlGnAKCbwU=
-github.com/newrelic/newrelic-telemetry-sdk-go v0.8.1/go.mod h1:2kY6OeOxrJ+RIQlVjWDc/pZlT3MIf30prs6drzMfJ6E=
 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
 github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=

+ 0 - 111
integration/load_test.go

@@ -1,111 +0,0 @@
-package integration
-
-import (
-	"fmt"
-	"net/http"
-	"os"
-	"path"
-	"path/filepath"
-	"testing"
-
-	"github.com/imgproxy/imgproxy/v3/imagetype"
-	"github.com/imgproxy/imgproxy/v3/testutil"
-	"github.com/imgproxy/imgproxy/v3/vips"
-	"github.com/stretchr/testify/suite"
-)
-
-const (
-	maxDistance = 0 // maximum image distance
-)
-
-type LoadTestSuite struct {
-	Suite
-
-	matcher           *testutil.ImageHashMatcher
-	testImagesPath    string
-	saveTmpImagesPath string
-}
-
-func (s *LoadTestSuite) SetupTest() {
-	s.testImagesPath = s.TestData.Path("test-images")
-	s.saveTmpImagesPath = os.Getenv("TEST_SAVE_TMP_IMAGES")
-	s.matcher = testutil.NewImageHashMatcher(s.TestData)
-
-	s.Config().Security.DefaultOptions.MaxAnimationFrames = 999
-	s.Config().Server.DevelopmentErrorsMode = true
-	s.Config().Fetcher.Transport.Local.Root = s.testImagesPath
-}
-
-// testLoadFolder fetches images iterates over images in the specified folder,
-// runs imgproxy on each image, and compares the result with the reference image
-// which is expected to be in the `integration` folder with the same name
-// but with `.png` extension.
-func (s *LoadTestSuite) testLoadFolder(folder string) {
-	walkPath := path.Join(s.testImagesPath, folder)
-
-	// Iterate over the files in the source folder
-	err := filepath.Walk(walkPath, func(path string, info os.FileInfo, err error) error {
-		s.Require().NoError(err)
-
-		// Skip directories
-		if info.IsDir() {
-			return nil
-		}
-
-		// get the base name of the file (8-bpp.png)
-		baseName := filepath.Base(path)
-
-		// Construct the source URL for imgproxy (no processing)
-		sourceUrl := fmt.Sprintf("/insecure/plain/local:///%s/%s@bmp", folder, baseName)
-
-		// Read source image from imgproxy
-		resp := s.GET(sourceUrl)
-		defer resp.Body.Close()
-
-		s.Require().Equal(http.StatusOK, resp.StatusCode, "expected status code 200 OK, got %d, path: %s", resp.StatusCode, path)
-
-		// Match image to precalculated hash
-		s.matcher.ImageMatches(s.T(), resp.Body, baseName, maxDistance)
-
-		return nil
-	})
-
-	s.Require().NoError(err)
-}
-
-// TestLoadSaveToPng ensures that our load pipeline works,
-// including standard and custom loaders. For each source image
-// in the folder, it does the passthrough request through imgproxy:
-// no processing, just convert format of the source file to png.
-// Then, it compares the result with the reference image.
-func (s *LoadTestSuite) TestLoadSaveToPng() {
-	testCases := []struct {
-		name       string
-		imageType  imagetype.Type
-		folderName string
-	}{
-		{"GIF", imagetype.GIF, "gif"},
-		{"JPEG", imagetype.JPEG, "jpg"},
-		{"HEIC", imagetype.HEIC, "heif"},
-		{"JXL", imagetype.JXL, "jxl"},
-		{"SVG", imagetype.SVG, "svg"},
-		{"TIFF", imagetype.TIFF, "tiff"},
-		{"WEBP", imagetype.WEBP, "webp"},
-		{"BMP", imagetype.BMP, "bmp"},
-		{"ICO", imagetype.ICO, "ico"},
-	}
-
-	for _, tc := range testCases {
-		s.T().Run(tc.name, func(t *testing.T) {
-			if vips.SupportsLoad(tc.imageType) {
-				s.testLoadFolder(tc.folderName)
-			} else {
-				t.Skipf("%s format not supported by VIPS", tc.name)
-			}
-		})
-	}
-}
-
-func TestIntegration(t *testing.T) {
-	suite.Run(t, new(LoadTestSuite))
-}

+ 128 - 0
integration/matrix_test.go

@@ -0,0 +1,128 @@
+package integration
+
+import (
+	"fmt"
+	"net/http"
+	"os"
+	"path"
+	"path/filepath"
+	"testing"
+
+	"github.com/imgproxy/imgproxy/v3/imagetype"
+	"github.com/imgproxy/imgproxy/v3/testutil"
+	"github.com/imgproxy/imgproxy/v3/vips"
+	"github.com/stretchr/testify/suite"
+)
+
+const (
+	// maximum Hamming distance between the source and destination hash
+	// NOTE: 2 here is because HEIC works a bit differently over the platforms, investigate
+	maxDistance = 2
+)
+
+// formats to test
+var formats = []imagetype.Type{
+	imagetype.GIF,
+	imagetype.JPEG,
+	imagetype.HEIC,
+	imagetype.JXL,
+	imagetype.SVG,
+	imagetype.TIFF,
+	imagetype.WEBP,
+	imagetype.BMP,
+	imagetype.ICO,
+}
+
+type MatrixTestSuite struct {
+	Suite
+
+	matcher        *testutil.ImageHashMatcher
+	testImagesPath string
+}
+
+func (s *MatrixTestSuite) SetupTest() {
+	s.testImagesPath = s.TestData.Path("test-images")
+	s.matcher = testutil.NewImageHashMatcher(s.TestData)
+
+	s.Config().Security.DefaultOptions.MaxAnimationFrames = 999
+	s.Config().Server.DevelopmentErrorsMode = true
+	s.Config().Fetcher.Transport.Local.Root = s.testImagesPath
+}
+
+// testLoadFolder fetches images iterates over images in the specified folder,
+// runs imgproxy on each image, and compares the result with the reference image
+// which is expected to be in the `integration` folder with the same name
+// but with `.png` extension.
+func (s *MatrixTestSuite) testFormat(source, target imagetype.Type) {
+	folder := source.String()
+
+	// TODO: rename the folders in test-images repo
+	if folder == "heic" {
+		folder = "heif"
+	}
+
+	if folder == "jpeg" {
+		folder = "jpg"
+	}
+
+	walkPath := path.Join(s.testImagesPath, folder)
+
+	// Iterate over the files in the source folder
+	err := filepath.Walk(walkPath, func(path string, info os.FileInfo, err error) error {
+		s.Require().NoError(err)
+
+		// Skip directories
+		if info.IsDir() {
+			return nil
+		}
+
+		// get the base name of the file (8-bpp.png)
+		baseName := filepath.Base(path)
+
+		// Construct the source URL for imgproxy (no processing)
+		sourceUrl := fmt.Sprintf("/insecure/plain/local:///%s/%s@%s", folder, baseName, target.String())
+
+		// Read source image from imgproxy
+		resp := s.GET(sourceUrl)
+		defer resp.Body.Close()
+
+		s.Require().Equal(http.StatusOK, resp.StatusCode, "expected status code 200 OK, got %d, url: %s", resp.StatusCode, sourceUrl)
+
+		// Match image to precalculated hash
+		s.matcher.ImageMatches(s.T(), resp.Body, baseName, maxDistance)
+
+		return nil
+	})
+
+	s.Require().NoError(err)
+}
+
+func (s *MatrixTestSuite) TestMatrix() {
+	for _, source := range formats {
+		for _, target := range formats {
+			s.Run(fmt.Sprintf("%s/%s", source.String(), target.String()), func() {
+				if !source.IsVector() && target.IsVector() {
+					// we can not vectorize a raster image
+					s.T().Logf("Skipping %s -> %s conversion: we can not vectorize raster image", source.String(), target.String())
+					return
+				}
+
+				if !vips.SupportsLoad(source) {
+					s.T().Logf("Skipping %s -> %s conversion: source format not supported by VIPS", source.String(), target.String())
+					return
+				}
+
+				if !vips.SupportsSave(target) {
+					s.T().Logf("Skipping %s -> %s conversion: target format not supported by VIPS", source.String(), target.String())
+					return
+				}
+
+				s.testFormat(source, target)
+			})
+		}
+	}
+}
+
+func TestMatrix(t *testing.T) {
+	suite.Run(t, new(MatrixTestSuite))
+}

+ 6 - 2
integration/processing_handler_test.go

@@ -13,7 +13,8 @@ import (
 	"github.com/imgproxy/imgproxy/v3/httpheaders"
 	"github.com/imgproxy/imgproxy/v3/imagedata"
 	"github.com/imgproxy/imgproxy/v3/imagetype"
-	"github.com/imgproxy/imgproxy/v3/svg"
+	"github.com/imgproxy/imgproxy/v3/options"
+	"github.com/imgproxy/imgproxy/v3/processing/svg"
 	"github.com/imgproxy/imgproxy/v3/testutil"
 	"github.com/imgproxy/imgproxy/v3/vips"
 	"github.com/stretchr/testify/suite"
@@ -213,7 +214,10 @@ func (s *ProcessingHandlerTestSuite) TestSkipProcessingSVG() {
 	data, err := idf.NewFromBytes(s.TestData.Read("test1.svg"))
 	s.Require().NoError(err)
 
-	expected, err := svg.Sanitize(data)
+	cfg := svg.NewDefaultConfig()
+	svg := svg.New(&cfg)
+
+	expected, err := svg.Process(&options.Options{}, data)
 	s.Require().NoError(err)
 
 	s.Require().True(testutil.ReadersEqual(s.T(), expected.Reader(), res.Body))

+ 12 - 7
integration/suite.go

@@ -44,26 +44,31 @@ func (s *Suite) SetupSuite() {
 	s.TestData = testutil.NewTestDataProvider(s.T)
 
 	s.Config, _ = testutil.NewLazySuiteObj(s, func() (*imgproxy.Config, error) {
-		// TODO: replace with NewDefaultConfig when we get rid of global config
-		c, err := imgproxy.LoadConfigFromEnv(nil)
-		s.Require().NoError(err)
+		c := imgproxy.NewDefaultConfig()
 
 		c.Server.Bind = ":0"
 
 		c.Fetcher.Transport.Local.Root = s.TestData.Root()
 		c.Fetcher.Transport.HTTP.ClientKeepAliveTimeout = 0
 
-		return c, nil
+		return &c, nil
 	})
 
 	s.Imgproxy, _ = testutil.NewLazySuiteObj(s, func() (*imgproxy.Imgproxy, error) {
 		return imgproxy.New(s.T().Context(), s.Config())
 	})
 
+	// NOTE: if we used s.T().Context() in startServer, server would have been stopped
+	// after the first subtest because s.T().Context() is cancelled after subtest.
+	//
+	// If resetLazyObjs is not called in SetupSubTest, the server would shutdown
+	// and won't restart in the second subtest because lazy obj would not be nil.
+	ctx := s.T().Context()
+
 	s.Server, _ = testutil.NewLazySuiteObj(
 		s,
 		func() (*TestServer, error) {
-			return s.startServer(s.Imgproxy()), nil
+			return s.startServer(ctx, s.Imgproxy()), nil
 		},
 		func(s *TestServer) error {
 			s.Shutdown()
@@ -78,8 +83,8 @@ func (s *Suite) TearDownSuite() {
 
 // startServer starts imgproxy instance's server for the tests.
 // Returns [TestServer] that contains the server address and shutdown function
-func (s *Suite) startServer(i *imgproxy.Imgproxy) *TestServer {
-	ctx, cancel := context.WithCancel(s.T().Context())
+func (s *Suite) startServer(ctx context.Context, i *imgproxy.Imgproxy) *TestServer {
+	ctx, cancel := context.WithCancel(ctx)
 
 	addrCh := make(chan net.Addr)
 

+ 18 - 98
monitoring/newrelic/newrelic.go

@@ -3,14 +3,11 @@ package newrelic
 import (
 	"context"
 	"fmt"
-	"log/slog"
 	"net/http"
 	"reflect"
-	"regexp"
 	"time"
 
 	"github.com/newrelic/go-agent/v3/newrelic"
-	"github.com/newrelic/newrelic-telemetry-sdk-go/telemetry"
 
 	"github.com/imgproxy/imgproxy/v3/monitoring/errformat"
 	"github.com/imgproxy/imgproxy/v3/monitoring/stats"
@@ -25,21 +22,14 @@ type attributable interface {
 	AddAttribute(key string, value any)
 }
 
-const (
-	// Metric API endpoints. NOTE: Possibly, this should be configurable?
-	defaultMetricURL = "https://metric-api.newrelic.com/metric/v1"
-	euMetricURL      = "https://metric-api.eu.newrelic.com/metric/v1"
-)
-
 type NewRelic struct {
 	stats  *stats.Stats
 	config *Config
 
-	app       *newrelic.Application
-	harvester *telemetry.Harvester
+	app *newrelic.Application
 
-	harvesterCtx       context.Context
-	harvesterCtxCancel context.CancelFunc
+	metricsCtx       context.Context
+	metricsCtxCancel context.CancelFunc
 }
 
 func New(config *Config, stats *stats.Stats) (*NewRelic, error) {
@@ -69,42 +59,8 @@ func New(config *Config, stats *stats.Stats) (*NewRelic, error) {
 		return nil, fmt.Errorf("can't init New Relic agent: %s", err)
 	}
 
-	// Initialize New Relic Telemetry SDK harvester
-	harvesterAttributes := map[string]any{"appName": config.AppName}
-	for k, v := range config.Labels {
-		harvesterAttributes[k] = v
-	}
-
-	// Choose metrics endpoint based on license key pattern
-	licenseEuRegex := regexp.MustCompile(`(^eu.+?)x`)
-
-	metricsURL := defaultMetricURL
-	if licenseEuRegex.MatchString(config.Key) {
-		metricsURL = euMetricURL
-	}
-
-	// Initialize error logger
-	errLogger := slog.NewLogLogger(
-		slog.With("source", "newrelic").Handler(),
-		slog.LevelWarn,
-	)
-
-	// Create harvester
-	harvester, err := telemetry.NewHarvester(
-		telemetry.ConfigAPIKey(config.Key),
-		telemetry.ConfigCommonAttributes(harvesterAttributes),
-		telemetry.ConfigHarvestPeriod(0), // Don't harvest automatically
-		telemetry.ConfigMetricsURLOverride(metricsURL),
-		telemetry.ConfigBasicErrorLogger(errLogger.Writer()),
-	)
-	if err == nil {
-		// In case, there were no errors while starting the harvester, start the metrics collector
-		nl.harvester = harvester
-		nl.harvesterCtx, nl.harvesterCtxCancel = context.WithCancel(context.Background())
-		go nl.runMetricsCollector()
-	} else {
-		slog.Warn(fmt.Sprintf("Can't init New Relic telemetry harvester: %s", err))
-	}
+	nl.metricsCtx, nl.metricsCtxCancel = context.WithCancel(context.Background())
+	go nl.runMetricsCollector()
 
 	return nl, nil
 }
@@ -116,13 +72,12 @@ func (nl *NewRelic) Enabled() bool {
 
 // Stop stops the New Relic APM agent and Telemetry SDK harvester
 func (nl *NewRelic) Stop(ctx context.Context) {
-	if nl.app != nil {
-		nl.app.Shutdown(5 * time.Second)
+	if nl.metricsCtxCancel != nil {
+		nl.metricsCtxCancel()
 	}
 
-	if nl.harvester != nil {
-		nl.harvesterCtxCancel()
-		nl.harvester.HarvestNow(ctx)
+	if nl.app != nil {
+		nl.app.Shutdown(5 * time.Second)
 	}
 }
 
@@ -215,50 +170,15 @@ func (nl *NewRelic) runMetricsCollector() {
 	for {
 		select {
 		case <-tick.C:
-			nl.harvester.RecordMetric(telemetry.Gauge{
-				Name:      "imgproxy.workers",
-				Value:     float64(nl.stats.WorkersNumber),
-				Timestamp: time.Now(),
-			})
-
-			nl.harvester.RecordMetric(telemetry.Gauge{
-				Name:      "imgproxy.requests_in_progress",
-				Value:     nl.stats.RequestsInProgress(),
-				Timestamp: time.Now(),
-			})
-
-			nl.harvester.RecordMetric(telemetry.Gauge{
-				Name:      "imgproxy.images_in_progress",
-				Value:     nl.stats.ImagesInProgress(),
-				Timestamp: time.Now(),
-			})
-
-			nl.harvester.RecordMetric(telemetry.Gauge{
-				Name:      "imgproxy.workers_utilization",
-				Value:     nl.stats.WorkersUtilization(),
-				Timestamp: time.Now(),
-			})
-
-			nl.harvester.RecordMetric(telemetry.Gauge{
-				Name:      "imgproxy.vips.memory",
-				Value:     vips.GetMem(),
-				Timestamp: time.Now(),
-			})
-
-			nl.harvester.RecordMetric(telemetry.Gauge{
-				Name:      "imgproxy.vips.max_memory",
-				Value:     vips.GetMemHighwater(),
-				Timestamp: time.Now(),
-			})
-
-			nl.harvester.RecordMetric(telemetry.Gauge{
-				Name:      "imgproxy.vips.allocs",
-				Value:     vips.GetAllocs(),
-				Timestamp: time.Now(),
-			})
-
-			nl.harvester.HarvestNow(nl.harvesterCtx)
-		case <-nl.harvesterCtx.Done():
+			nl.app.RecordCustomMetric("imgproxy/workers", float64(nl.stats.WorkersNumber))
+			nl.app.RecordCustomMetric("imgproxy/requests_in_progress", float64(nl.stats.RequestsInProgress()))
+			nl.app.RecordCustomMetric("imgproxy/images_in_progress", float64(nl.stats.ImagesInProgress()))
+			nl.app.RecordCustomMetric("imgproxy/workers_utilization", nl.stats.WorkersUtilization())
+
+			nl.app.RecordCustomMetric("imgproxy/vips/memory", float64(vips.GetMem()))
+			nl.app.RecordCustomMetric("imgproxy/vips/max_memory", float64(vips.GetMemHighwater()))
+			nl.app.RecordCustomMetric("imgproxy/vips/allocs", float64(vips.GetAllocs()))
+		case <-nl.metricsCtx.Done():
 			return
 		}
 	}

+ 9 - 5
processing/config.go

@@ -6,6 +6,7 @@ import (
 	"github.com/imgproxy/imgproxy/v3/ensure"
 	"github.com/imgproxy/imgproxy/v3/env"
 	"github.com/imgproxy/imgproxy/v3/imagetype"
+	"github.com/imgproxy/imgproxy/v3/processing/svg"
 	"github.com/imgproxy/imgproxy/v3/vips"
 )
 
@@ -15,7 +16,6 @@ var (
 	IMGPROXY_WATERMARK_OPACITY       = env.Describe("IMGPROXY_WATERMARK_OPACITY", "number between 0..1")
 	IMGPROXY_DISABLE_SHRINK_ON_LOAD  = env.Describe("IMGPROXY_DISABLE_SHRINK_ON_LOAD", "boolean")
 	IMGPROXY_USE_LINEAR_COLORSPACE   = env.Describe("IMGPROXY_USE_LINEAR_COLORSPACE", "boolean")
-	IMGPROXY_SANITIZE_SVG            = env.Describe("IMGPROXY_SANITIZE_SVG", "boolean")
 	IMGPROXY_ALWAYS_RASTERIZE_SVG    = env.Describe("IMGPROXY_ALWAYS_RASTERIZE_SVG", "boolean")
 	IMGPROXY_QUALITY                 = env.Describe("IMGPROXY_QUALITY", "number between 0..100")
 	IMGPROXY_FORMAT_QUALITY          = env.Describe("IMGPROXY_FORMAT_QUALITY", "comma-separated list of format=quality pairs where quality is between 0..100")
@@ -33,7 +33,6 @@ type Config struct {
 	WatermarkOpacity      float64
 	DisableShrinkOnLoad   bool
 	UseLinearColorspace   bool
-	SanitizeSvg           bool
 	AlwaysRasterizeSvg    bool
 	Quality               int
 	FormatQuality         map[imagetype.Type]int
@@ -42,6 +41,8 @@ type Config struct {
 	StripColorProfile     bool
 	AutoRotate            bool
 	EnforceThumbnail      bool
+
+	Svg svg.Config
 }
 
 // NewConfig creates a new Config instance with the given parameters.
@@ -53,8 +54,7 @@ func NewDefaultConfig() Config {
 			imagetype.PNG,
 			imagetype.GIF,
 		},
-		SanitizeSvg: true,
-		Quality:     80,
+		Quality: 80,
 		FormatQuality: map[imagetype.Type]int{
 			imagetype.WEBP: 79,
 			imagetype.AVIF: 63,
@@ -65,6 +65,8 @@ func NewDefaultConfig() Config {
 		StripColorProfile: true,
 		AutoRotate:        true,
 		EnforceThumbnail:  false,
+
+		Svg: svg.NewDefaultConfig(),
 	}
 }
 
@@ -72,11 +74,13 @@ func NewDefaultConfig() Config {
 func LoadConfigFromEnv(c *Config) (*Config, error) {
 	c = ensure.Ensure(c, NewDefaultConfig)
 
+	_, svgErr := svg.LoadConfigFromEnv(&c.Svg)
+
 	err := errors.Join(
+		svgErr,
 		env.Float(&c.WatermarkOpacity, IMGPROXY_WATERMARK_OPACITY),
 		env.Bool(&c.DisableShrinkOnLoad, IMGPROXY_DISABLE_SHRINK_ON_LOAD),
 		env.Bool(&c.UseLinearColorspace, IMGPROXY_USE_LINEAR_COLORSPACE),
-		env.Bool(&c.SanitizeSvg, IMGPROXY_SANITIZE_SVG),
 		env.Bool(&c.AlwaysRasterizeSvg, IMGPROXY_ALWAYS_RASTERIZE_SVG),
 		env.Int(&c.Quality, IMGPROXY_QUALITY),
 		env.ImageTypesQuality(c.FormatQuality, IMGPROXY_FORMAT_QUALITY),

+ 3 - 15
processing/processing.go

@@ -12,7 +12,6 @@ import (
 	"github.com/imgproxy/imgproxy/v3/options"
 	"github.com/imgproxy/imgproxy/v3/security"
 	"github.com/imgproxy/imgproxy/v3/server"
-	"github.com/imgproxy/imgproxy/v3/svg"
 	"github.com/imgproxy/imgproxy/v3/vips"
 )
 
@@ -268,20 +267,9 @@ func (p *Processor) skipStandardProcessing(
 		return nil, err
 	}
 
-	// Even in this case, SVG is an exception
-	if imgdata.Format() == imagetype.SVG && p.config.SanitizeSvg {
-		sanitized, err := svg.Sanitize(imgdata)
-		if err != nil {
-			return nil, err
-		}
-
-		return &Result{
-			OutData:      sanitized,
-			OriginWidth:  originWidth,
-			OriginHeight: originHeight,
-			ResultWidth:  originWidth,
-			ResultHeight: originHeight,
-		}, nil
+	imgdata, err = p.svg.Process(po.Options, imgdata)
+	if err != nil {
+		return nil, err
 	}
 
 	// Return the original image

+ 3 - 0
processing/processor.go

@@ -2,12 +2,14 @@ package processing
 
 import (
 	"github.com/imgproxy/imgproxy/v3/auximageprovider"
+	"github.com/imgproxy/imgproxy/v3/processing/svg"
 )
 
 // Processor is responsible for processing images according to the given configuration.
 type Processor struct {
 	config            *Config
 	watermarkProvider auximageprovider.Provider
+	svg               *svg.Processor
 }
 
 // New creates a new Processor instance with the given configuration and watermark provider
@@ -19,5 +21,6 @@ func New(config *Config, watermark auximageprovider.Provider) (*Processor, error
 	return &Processor{
 		config:            config,
 		watermarkProvider: watermark,
+		svg:               svg.New(&config.Svg),
 	}, nil
 }

+ 36 - 0
processing/svg/config.go

@@ -0,0 +1,36 @@
+package svg
+
+import (
+	"github.com/imgproxy/imgproxy/v3/ensure"
+	"github.com/imgproxy/imgproxy/v3/env"
+)
+
+var (
+	IMGPROXY_SANITIZE_SVG = env.Describe("IMGPROXY_SANITIZE_SVG", "boolean")
+)
+
+// Config holds SVG-specific configuration
+type Config struct {
+	Sanitize bool // Sanitize SVG content for security
+}
+
+// NewDefaultConfig creates a new Config instance with default values
+func NewDefaultConfig() Config {
+	return Config{
+		Sanitize: true, // By default, sanitize SVG for security
+	}
+}
+
+// LoadConfigFromEnv loads configuration from environment variables
+func LoadConfigFromEnv(c *Config) (*Config, error) {
+	c = ensure.Ensure(c, NewDefaultConfig)
+
+	err := env.Bool(&c.Sanitize, IMGPROXY_SANITIZE_SVG)
+
+	return c, err
+}
+
+// Validate checks if the configuration is valid
+func (c *Config) Validate() error {
+	return nil
+}

+ 0 - 0
svg/errors.go → processing/svg/errors.go


+ 39 - 4
svg/svg.go → processing/svg/svg.go

@@ -7,20 +7,55 @@ import (
 	"strings"
 	"sync"
 
-	"github.com/tdewolff/parse/v2"
-	"github.com/tdewolff/parse/v2/xml"
-
 	"github.com/imgproxy/imgproxy/v3/imagedata"
 	"github.com/imgproxy/imgproxy/v3/imagetype"
+	"github.com/imgproxy/imgproxy/v3/options"
+	"github.com/tdewolff/parse/v2"
+	"github.com/tdewolff/parse/v2/xml"
 )
 
+// pool represents temorary pool for svg sanitized data
 var pool = sync.Pool{
 	New: func() any {
 		return bytes.NewBuffer(nil)
 	},
 }
 
-func Sanitize(data imagedata.ImageData) (imagedata.ImageData, error) {
+// Processor provides SVG processing capabilities
+type Processor struct {
+	config *Config
+}
+
+// New creates a new SVG processor instance
+func New(config *Config) *Processor {
+	return &Processor{
+		config: config,
+	}
+}
+
+// Process processes the given image data
+func (p *Processor) Process(o *options.Options, data imagedata.ImageData) (imagedata.ImageData, error) {
+	if data.Format() != imagetype.SVG {
+		return data, nil
+	}
+
+	var err error
+
+	data, err = p.sanitize(data)
+	if err != nil {
+		return data, err
+	}
+
+	return data, nil
+}
+
+// sanitize sanitizes the SVG data.
+// It strips <script> and unsafe attributes (on* events).
+func (p *Processor) sanitize(data imagedata.ImageData) (imagedata.ImageData, error) {
+	if !p.config.Sanitize {
+		return data, nil
+	}
+
 	r := data.Reader()
 	l := xml.NewLexer(parse.NewInput(r))
 

+ 5 - 1
svg/svg_test.go → processing/svg/svg_test.go

@@ -9,6 +9,7 @@ import (
 
 	"github.com/imgproxy/imgproxy/v3/imagedata"
 	"github.com/imgproxy/imgproxy/v3/imagetype"
+	"github.com/imgproxy/imgproxy/v3/options"
 	"github.com/imgproxy/imgproxy/v3/testutil"
 )
 
@@ -45,7 +46,10 @@ func (s *SvgTestSuite) TestSanitize() {
 	origin := s.readTestFile("test1.svg")
 	expected := s.readTestFile("test1.sanitized.svg")
 
-	actual, err := Sanitize(origin)
+	config := NewDefaultConfig()
+	svg := New(&config)
+
+	actual, err := svg.Process(options.New(), origin)
 	s.Require().NoError(err)
 
 	s.compare(expected, actual)

+ 0 - 0
svg/unsafe_attrs.go → processing/svg/unsafe_attrs.go


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/1-bpp.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/1-bpp.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/16-bpp.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/16-bpp.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/24-bpp-no-alpha-mask.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/24-bpp-no-alpha-mask.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/24-bpp.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/24-bpp.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/32-bpp-with-alpha-self-gen.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/32-bpp-with-alpha-self-gen.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/32-bpp-with-alpha.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/32-bpp-with-alpha.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/4-bpp.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/4-bpp.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/8-bpp-rle-move-to-x.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/8-bpp-rle-move-to-x.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/8-bpp-rle-single-color.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/8-bpp-rle-single-color.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/8-bpp-rle-small.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/8-bpp-rle-small.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/8-bpp-rle.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/8-bpp-rle.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/8-bpp.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/bmp/8-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/1-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/16-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/24-bpp-no-alpha-mask.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/24-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/32-bpp-with-alpha-self-gen.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/32-bpp-with-alpha.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/4-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/8-bpp-rle-move-to-x.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/multi-bmp.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/8-bpp-rle-single-color.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/8-bpp-rle-small.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/8-bpp-rle.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/gif/8-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/1-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/16-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/24-bpp-no-alpha-mask.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/24-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/32-bpp-with-alpha-self-gen.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/32-bpp-with-alpha.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/4-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/8-bpp-rle-move-to-x.bmp.hash


+ 0 - 0
testdata/test-hashes/TestIntegration/TestLoadSaveToPng/multi-png.hash → testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/8-bpp-rle-single-color.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/8-bpp-rle-small.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/8-bpp-rle.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/heic/8-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/1-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/16-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/24-bpp-no-alpha-mask.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/24-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/32-bpp-with-alpha-self-gen.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/32-bpp-with-alpha.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/4-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/8-bpp-rle-move-to-x.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/8-bpp-rle-single-color.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/8-bpp-rle-small.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/8-bpp-rle.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/ico/8-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/1-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/16-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/24-bpp-no-alpha-mask.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/24-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/32-bpp-with-alpha-self-gen.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/32-bpp-with-alpha.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/4-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/8-bpp-rle-move-to-x.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/8-bpp-rle-single-color.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/8-bpp-rle-small.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/8-bpp-rle.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jpeg/8-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/1-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/16-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/24-bpp-no-alpha-mask.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/24-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/32-bpp-with-alpha-self-gen.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/32-bpp-with-alpha.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/4-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/8-bpp-rle-move-to-x.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/8-bpp-rle-single-color.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/8-bpp-rle-small.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/8-bpp-rle.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/jxl/8-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/1-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/16-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/24-bpp-no-alpha-mask.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/24-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/32-bpp-with-alpha-self-gen.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/32-bpp-with-alpha.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/4-bpp.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/8-bpp-rle-move-to-x.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/8-bpp-rle-single-color.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/8-bpp-rle-small.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/8-bpp-rle.bmp.hash


BIN=BIN
testdata/test-hashes/TestMatrix/TestMatrix/bmp/tiff/8-bpp.bmp.hash


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio