Browse Source

Optimize watermark application

DarthSim 2 years ago
parent
commit
85f790fb4b
5 changed files with 37 additions and 18 deletions
  1. 1 0
      CHANGELOG.md
  2. 26 13
      processing/watermark.go
  3. 6 2
      vips/vips.c
  4. 2 2
      vips/vips.go
  5. 2 1
      vips/vips.h

+ 1 - 0
CHANGELOG.md

@@ -7,6 +7,7 @@
 
 ### Change
 - Optimized memory buffers pooling for better performance and memory reusage.
+- Optimized watermarks application.
 
 ## [3.16.1] - 2023-04-26
 ### Fix

+ 26 - 13
processing/watermark.go

@@ -67,17 +67,6 @@ func prepareWatermark(wm *vips.Image, wmData *imagedata.ImageData, opts *options
 		if err := wm.Replicate(imgWidth, imgHeight); err != nil {
 			return err
 		}
-	} else {
-		left, top := calcPosition(imgWidth, imgHeight, wm.Width(), wm.Height(), &opts.Gravity, offsetScale, true)
-		if err := wm.Embed(imgWidth, imgHeight, left, top); err != nil {
-			return err
-		}
-	}
-
-	if framesCount > 1 {
-		if err := wm.Replicate(imgWidth, imgWidth*framesCount); err != nil {
-			return err
-		}
 	}
 
 	wm.RemoveHeader("palette-bit-depth")
@@ -95,14 +84,38 @@ func applyWatermark(img *vips.Image, wmData *imagedata.ImageData, opts *options.
 
 	width := img.Width()
 	height := img.Height()
+	frameHeight := height / framesCount
 
-	if err := prepareWatermark(wm, wmData, opts, width, height/framesCount, offsetScale, framesCount); err != nil {
+	if err := prepareWatermark(wm, wmData, opts, width, frameHeight, offsetScale, framesCount); err != nil {
 		return err
 	}
 
 	opacity := opts.Opacity * config.WatermarkOpacity
 
-	return img.ApplyWatermark(wm, opacity)
+	// If we replicated the watermark and need to apply it to an animated image,
+	// it is faster to replicate the watermark to all the image and apply it single-pass
+	if opts.Replicate && framesCount > 1 {
+		if err := wm.Replicate(width, height); err != nil {
+			return err
+		}
+
+		return img.ApplyWatermark(wm, 0, 0, opacity)
+	}
+
+	left, top := 0, 0
+
+	if !opts.Replicate {
+		left, top = calcPosition(width, frameHeight, wm.Width(), wm.Height(), &opts.Gravity, offsetScale, true)
+	}
+
+	for i := 0; i < framesCount; i++ {
+		if err := img.ApplyWatermark(wm, left, top, opacity); err != nil {
+			return err
+		}
+		top += frameHeight
+	}
+
+	return nil
 }
 
 func watermark(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error {

+ 6 - 2
vips/vips.c

@@ -531,7 +531,7 @@ vips_embed_go(VipsImage *in, VipsImage **out, int x, int y, int width, int heigh
 }
 
 int
-vips_apply_watermark(VipsImage *in, VipsImage *watermark, VipsImage **out, double opacity) {
+vips_apply_watermark(VipsImage *in, VipsImage *watermark, VipsImage **out, int left, int top, double opacity) {
   VipsImage *base = vips_image_new();
   VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 7);
 
@@ -559,7 +559,11 @@ vips_apply_watermark(VipsImage *in, VipsImage *watermark, VipsImage **out, doubl
   int had_alpha = vips_image_hasalpha(in);
 
   if (
-    vips_composite2(in, watermark, &t[5], VIPS_BLEND_MODE_OVER, "compositing_space", in->Type, NULL) ||
+    vips_composite2(
+      in, watermark, &t[5], VIPS_BLEND_MODE_OVER,
+      "x", left, "y", top, "compositing_space", in->Type,
+      NULL
+    ) ||
     vips_cast(t[5], &t[6], vips_image_get_format(in), NULL)
   ) {
     clear_image(&base);

+ 2 - 2
vips/vips.go

@@ -771,10 +771,10 @@ func (img *Image) Embed(width, height int, offX, offY int) error {
 	return nil
 }
 
-func (img *Image) ApplyWatermark(wm *Image, opacity float64) error {
+func (img *Image) ApplyWatermark(wm *Image, left, top int, opacity float64) error {
 	var tmp *C.VipsImage
 
-	if C.vips_apply_watermark(img.VipsImage, wm.VipsImage, &tmp, C.double(opacity)) != 0 {
+	if C.vips_apply_watermark(img.VipsImage, wm.VipsImage, &tmp, C.int(left), C.int(top), C.double(opacity)) != 0 {
 		return Error()
 	}
 	C.swap_and_clear(&img.VipsImage, tmp)

+ 2 - 1
vips/vips.h

@@ -67,7 +67,8 @@ int vips_flatten_go(VipsImage *in, VipsImage **out, double r, double g, double b
 int vips_replicate_go(VipsImage *in, VipsImage **out, int across, int down);
 int vips_embed_go(VipsImage *in, VipsImage **out, int x, int y, int width, int height);
 
-int vips_apply_watermark(VipsImage *in, VipsImage *watermark, VipsImage **out, double opacity);
+int vips_apply_watermark(VipsImage *in, VipsImage *watermark, VipsImage **out,
+                         int left, int top, double opacity);
 
 int vips_arrayjoin_go(VipsImage **in, VipsImage **out, int n);