Ver código fonte

Use heic/avif embedded thumbnails

DarthSim 3 anos atrás
pai
commit
9b146991c5
6 arquivos alterados com 81 adições e 17 exclusões
  1. 4 1
      CHANGELOG.md
  2. 4 0
      imagetype/imagetype.go
  3. 41 12
      processing/scale_on_load.go
  4. 7 2
      vips/vips.c
  5. 24 1
      vips/vips.go
  6. 1 1
      vips/vips.h

+ 4 - 1
CHANGELOG.md

@@ -1,9 +1,12 @@
 # Changelog
 
 ## [Unreleased]
-## Add
+### Add
 - Add support of RLE-encoded BMP.
 
+### Change
+- Use thumbnail embedded to HEIC/AVIF if its size is larger than or equal to the requested.
+
 ## [3.4.0] - 2022-04-07
 ### Add
 - Add `IMGPROXY_FALLBACK_IMAGE_TTL` config.

+ 4 - 0
imagetype/imagetype.go

@@ -130,3 +130,7 @@ func (it Type) SupportsColourProfile() bool {
 		it == WEBP ||
 		it == AVIF
 }
+
+func (it Type) SupportsThumbnail() bool {
+	return it == HEIC || it == AVIF
+}

+ 41 - 12
processing/scale_on_load.go

@@ -11,8 +11,12 @@ import (
 	"github.com/imgproxy/imgproxy/v3/vips"
 )
 
-func canScaleOnLoad(imgtype imagetype.Type, scale float64) bool {
-	if imgtype == imagetype.SVG {
+func canScaleOnLoad(pctx *pipelineContext, imgdata *imagedata.ImageData, scale float64) bool {
+	if imgdata == nil || pctx.trimmed || scale == 1 {
+		return false
+	}
+
+	if imgdata.Type == imagetype.SVG {
 		return true
 	}
 
@@ -20,7 +24,10 @@ func canScaleOnLoad(imgtype imagetype.Type, scale float64) bool {
 		return false
 	}
 
-	return imgtype == imagetype.JPEG || imgtype == imagetype.WEBP
+	return imgdata.Type == imagetype.JPEG ||
+		imgdata.Type == imagetype.WEBP ||
+		imgdata.Type == imagetype.HEIC ||
+		imgdata.Type == imagetype.AVIF
 }
 
 func calcJpegShink(scale float64, imgtype imagetype.Type) int {
@@ -41,23 +48,45 @@ func calcJpegShink(scale float64, imgtype imagetype.Type) int {
 func scaleOnLoad(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error {
 	prescale := math.Max(pctx.wscale, pctx.hscale)
 
-	if pctx.trimmed || prescale == 1 || imgdata == nil || !canScaleOnLoad(pctx.imgtype, prescale) {
+	if !canScaleOnLoad(pctx, imgdata, prescale) {
 		return nil
 	}
 
-	jpegShrink := calcJpegShink(prescale, pctx.imgtype)
+	var newWidth, newHeight int
 
-	if pctx.imgtype == imagetype.JPEG && jpegShrink == 1 {
-		return nil
-	}
+	if imgdata.Type.SupportsThumbnail() {
+		thumbnail := new(vips.Image)
+		defer thumbnail.Clear()
+
+		if err := thumbnail.LoadThumbnail(imgdata); err != nil {
+			return err
+		}
 
-	if err := img.Load(imgdata, jpegShrink, prescale, 1); err != nil {
-		return err
+		angle, flip := 0, false
+		newWidth, newHeight, angle, flip = extractMeta(thumbnail, po.Rotate, po.AutoRotate)
+
+		if newWidth >= pctx.srcWidth || float64(newWidth)/float64(pctx.srcWidth) < prescale {
+			return nil
+		}
+
+		img.Swap(thumbnail)
+		pctx.angle = angle
+		pctx.flip = flip
+	} else {
+		jpegShrink := calcJpegShink(prescale, pctx.imgtype)
+
+		if pctx.imgtype == imagetype.JPEG && jpegShrink == 1 {
+			return nil
+		}
+
+		if err := img.Load(imgdata, jpegShrink, prescale, 1); err != nil {
+			return err
+		}
+
+		newWidth, newHeight, _, _ = extractMeta(img, po.Rotate, po.AutoRotate)
 	}
 
 	// Update scales after scale-on-load
-	newWidth, newHeight, _, _ := extractMeta(img, po.Rotate, po.AutoRotate)
-
 	wpreshrink := float64(pctx.srcWidth) / float64(newWidth)
 	hpreshrink := float64(pctx.srcHeight) / float64(newHeight)
 

+ 7 - 2
vips/vips.c

@@ -82,8 +82,13 @@ vips_svgload_go(void *buf, size_t len, double scale, VipsImage **out) {
 }
 
 int
-vips_heifload_go(void *buf, size_t len, VipsImage **out) {
-  return vips_heifload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
+vips_heifload_go(void *buf, size_t len, VipsImage **out, int thumbnail) {
+  return vips_heifload_buffer(
+    buf, len, out,
+    "access", VIPS_ACCESS_SEQUENTIAL,
+    "thumbnail", thumbnail,
+    NULL
+  );
 }
 
 int

+ 24 - 1
vips/vips.go

@@ -229,7 +229,7 @@ func (img *Image) Load(imgdata *imagedata.ImageData, shrink int, scale float64,
 	case imagetype.SVG:
 		err = C.vips_svgload_go(data, dataSize, C.double(scale), &tmp)
 	case imagetype.HEIC, imagetype.AVIF:
-		err = C.vips_heifload_go(data, dataSize, &tmp)
+		err = C.vips_heifload_go(data, dataSize, &tmp, C.int(0))
 	case imagetype.TIFF:
 		err = C.vips_tiffload_go(data, dataSize, &tmp)
 	default:
@@ -244,6 +244,25 @@ func (img *Image) Load(imgdata *imagedata.ImageData, shrink int, scale float64,
 	return nil
 }
 
+func (img *Image) LoadThumbnail(imgdata *imagedata.ImageData) error {
+	if imgdata.Type != imagetype.HEIC && imgdata.Type != imagetype.AVIF {
+		return errors.New("Usupported image type to load thumbnail")
+	}
+
+	var tmp *C.VipsImage
+
+	data := unsafe.Pointer(&imgdata.Data[0])
+	dataSize := C.size_t(len(imgdata.Data))
+
+	if err := C.vips_heifload_go(data, dataSize, &tmp, C.int(1)); err != 0 {
+		return Error()
+	}
+
+	C.swap_and_clear(&img.VipsImage, tmp)
+
+	return nil
+}
+
 func (img *Image) Save(imgtype imagetype.Type, quality int) (*imagedata.ImageData, error) {
 	if imgtype == imagetype.ICO {
 		return img.saveAsIco()
@@ -314,6 +333,10 @@ func (img *Image) Arrayjoin(in []*Image) error {
 	return nil
 }
 
+func (img *Image) Swap(in *Image) {
+	img.VipsImage, in.VipsImage = in.VipsImage, img.VipsImage
+}
+
 func (img *Image) IsAnimated() bool {
 	return C.vips_is_animated(img.VipsImage) > 0
 }

+ 1 - 1
vips/vips.h

@@ -19,7 +19,7 @@ int vips_pngload_go(void *buf, size_t len, VipsImage **out);
 int vips_webpload_go(void *buf, size_t len, double scale, int pages, VipsImage **out);
 int vips_gifload_go(void *buf, size_t len, int pages, VipsImage **out);
 int vips_svgload_go(void *buf, size_t len, double scale, VipsImage **out);
-int vips_heifload_go(void *buf, size_t len, VipsImage **out);
+int vips_heifload_go(void *buf, size_t len, VipsImage **out, int thumbnail);
 int vips_tiffload_go(void *buf, size_t len, VipsImage **out);
 
 int vips_black_go(VipsImage **out, int width, int height, int bands);