Browse Source

Better watermarking

DarthSim 5 years ago
parent
commit
8cef2ee3b2
5 changed files with 109 additions and 153 deletions
  1. 72 2
      process.go
  2. 0 43
      vips.c
  3. 7 102
      vips.go
  4. 0 1
      vips.h
  5. 30 5
      watermark_data.go

+ 72 - 2
process.go

@@ -202,6 +202,66 @@ func scaleSize(size int, scale float64) int {
 	return roundToInt(float64(size) * scale)
 }
 
+func prepareWatermark(wm *vipsImage, wmData *watermarkData, opts *watermarkOptions, imgWidth, imgHeight int) error {
+	if err := wm.Load(wmData.data, wmData.imgtype, 1, 1.0, 1); err != nil {
+		return err
+	}
+
+	po := processingOptions{
+		Resize:  resizeFit,
+		Dpr:     1,
+		Enlarge: true,
+		Format:  wmData.imgtype,
+	}
+
+	if opts.Scale > 0 {
+		po.Width = maxInt(int(float64(imgWidth)*opts.Scale), 1)
+		po.Height = maxInt(int(float64(imgHeight)*opts.Scale), 1)
+	}
+
+	if err := transformImage(context.Background(), wm, wmData.data, &po, wmData.imgtype); err != nil {
+		return err
+	}
+
+	if err := wm.EnsureAlpha(); err != nil {
+		return nil
+	}
+
+	if opts.Replicate {
+		if err := wm.Replicate(imgWidth, imgHeight); err != nil {
+			return err
+		}
+	} else {
+		if err := wm.Embed(opts.Gravity, imgWidth, imgHeight, opts.OffsetX, opts.OffsetY, rgbColor{0, 0, 0}); err != nil {
+			return err
+		}
+	}
+
+	return wm.CopyMemory()
+}
+
+func applyWatermark(img *vipsImage, wmData *watermarkData, opts *watermarkOptions, framesCount int) error {
+	wm := new(vipsImage)
+	defer wm.Clear()
+
+	width := img.Width()
+	height := img.Height()
+
+	if err := prepareWatermark(wm, wmData, opts, width, height/framesCount); err != nil {
+		return err
+	}
+
+	if framesCount > 1 {
+		if err := wm.Replicate(width, height); err != nil {
+			return err
+		}
+	}
+
+	opacity := opts.Opacity * conf.WatermarkOpacity
+
+	return img.ApplyWatermark(wm, opacity)
+}
+
 func transformImage(ctx context.Context, img *vipsImage, data []byte, po *processingOptions, imgtype imageType) error {
 	var err error
 
@@ -380,8 +440,8 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces
 
 	checkTimeout(ctx)
 
-	if po.Watermark.Enabled {
-		if err = img.ApplyWatermark(&po.Watermark); err != nil {
+	if po.Watermark.Enabled && watermark != nil {
+		if err = applyWatermark(img, watermark, &po.Watermark, 1); err != nil {
 			return err
 		}
 	}
@@ -446,6 +506,10 @@ func transformAnimated(ctx context.Context, img *vipsImage, data []byte, po *pro
 		}
 	}()
 
+	watermarkEnabled := po.Watermark.Enabled
+	po.Watermark.Enabled = false
+	defer func() { po.Watermark.Enabled = watermarkEnabled }()
+
 	var errg errgroup.Group
 
 	for i := 0; i < framesCount; i++ {
@@ -477,6 +541,12 @@ func transformAnimated(ctx context.Context, img *vipsImage, data []byte, po *pro
 		return err
 	}
 
+	if watermarkEnabled && watermark != nil {
+		if err = applyWatermark(img, watermark, &po.Watermark, framesCount); err != nil {
+			return err
+		}
+	}
+
 	img.SetInt("page-height", frames[0].Height())
 	img.SetInt("gif-delay", delay)
 	img.SetInt("gif-loop", loop)

+ 0 - 43
vips.c

@@ -385,49 +385,6 @@ vips_ensure_alpha(VipsImage *in, VipsImage **out) {
   return vips_bandjoin_const1(in, out, 255, NULL);
 }
 
-int
-vips_apply_opacity(VipsImage *in, VipsImage **out, double opacity){
-  if (vips_image_hasalpha_go(in)) {
-    if (opacity < 1) {
-      VipsImage *img, *img_alpha, *tmp;
-
-			if (vips_extract_band(in, &img, 0, "n", in->Bands - 1, NULL))
-				return 1;
-
-      if (vips_extract_band(in, &img_alpha, in->Bands - 1, "n", 1, NULL)) {
-        clear_image(&img);
-				return 1;
-			}
-
-			if (vips_linear1(img_alpha, &tmp, opacity, 0, NULL)) {
-        clear_image(&img);
-        clear_image(&img_alpha);
-				return 1;
-			}
-			swap_and_clear(&img_alpha, tmp);
-
-			if (vips_bandjoin2(img, img_alpha, out, NULL)) {
-        clear_image(&img);
-        clear_image(&img_alpha);
-				return 1;
-			}
-
-      clear_image(&img);
-      clear_image(&img_alpha);
-    } else {
-      if (vips_copy(in, out, NULL)) {
-        return 1;
-      }
-    }
-  } else {
-    if (vips_bandjoin_const1(in, out, opacity * 255, NULL)) {
-      return 1;
-    }
-  }
-
-  return 0;
-}
-
 int
 vips_apply_watermark(VipsImage *in, VipsImage *watermark, VipsImage **out, double opacity) {
   VipsImage *wm, *wm_alpha, *tmp;

+ 7 - 102
vips.go

@@ -25,7 +25,7 @@ var (
 	vipsTypeSupportLoad  = make(map[imageType]bool)
 	vipsTypeSupportSave  = make(map[imageType]bool)
 
-	watermark *vipsImage
+	watermark *watermarkData
 )
 
 var vipsConf struct {
@@ -126,7 +126,7 @@ func initVips() {
 
 	vipsConf.WatermarkOpacity = C.double(conf.WatermarkOpacity)
 
-	if err := vipsPrepareWatermark(); err != nil {
+	if err := vipsLoadWatermark(); err != nil {
 		logFatal(err.Error())
 	}
 
@@ -134,10 +134,6 @@ func initVips() {
 }
 
 func shutdownVips() {
-	if watermark != nil {
-		watermark.Clear()
-	}
-
 	C.vips_shutdown()
 }
 
@@ -161,61 +157,8 @@ func vipsError() error {
 	return newUnexpectedError(C.GoString(C.vips_error_buffer()), 1)
 }
 
-func vipsPrepareWatermark() error {
-	data, imgtype, cancel, err := watermarkData()
-	defer cancel()
-
-	if err != nil {
-		return err
-	}
-
-	if data == nil {
-		return nil
-	}
-
-	watermark = new(vipsImage)
-
-	if err = watermark.Load(data, imgtype, 1, 1.0, 1); err != nil {
-		return err
-	}
-
-	var tmp *C.VipsImage
-
-	if C.vips_apply_opacity(watermark.VipsImage, &tmp, C.double(conf.WatermarkOpacity)) != 0 {
-		return vipsError()
-	}
-	C.swap_and_clear(&watermark.VipsImage, tmp)
-
-	if err = watermark.CopyMemory(); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func vipsResizeWatermark(width, height int) (wm *vipsImage, err error) {
-	wmW := float64(watermark.VipsImage.Xsize)
-	wmH := float64(watermark.VipsImage.Ysize)
-
-	wr := float64(width) / wmW
-	hr := float64(height) / wmH
-
-	scale := math.Min(wr, hr)
-
-	if wmW*scale < 1 {
-		scale = 1 / wmW
-	}
-
-	if wmH*scale < 1 {
-		scale = 1 / wmH
-	}
-
-	wm = new(vipsImage)
-
-	if C.vips_resize_with_premultiply(watermark.VipsImage, &wm.VipsImage, C.double(scale)) != 0 {
-		err = vipsError()
-	}
-
+func vipsLoadWatermark() (err error) {
+	watermark, err = getWatermarkData()
 	return
 }
 
@@ -632,48 +575,10 @@ func (img *vipsImage) Embed(gravity gravityType, width, height int, offX, offY i
 	return nil
 }
 
-func (img *vipsImage) ApplyWatermark(opts *watermarkOptions) error {
-	if watermark == nil {
-		return nil
-	}
-
-	var (
-		wm  *vipsImage
-		tmp *C.VipsImage
-	)
-	defer func() { wm.Clear() }()
-
-	var err error
-
-	imgW := img.Width()
-	imgH := img.Height()
-
-	if opts.Scale == 0 {
-		wm = new(vipsImage)
-
-		if C.vips_copy_go(watermark.VipsImage, &wm.VipsImage) != 0 {
-			return vipsError()
-		}
-	} else {
-		wmW := maxInt(int(float64(imgW)*opts.Scale), 1)
-		wmH := maxInt(int(float64(imgH)*opts.Scale), 1)
-
-		if wm, err = vipsResizeWatermark(wmW, wmH); err != nil {
-			return err
-		}
-	}
-
-	if opts.Replicate {
-		if err = wm.Replicate(imgW, imgH); err != nil {
-			return err
-		}
-	} else {
-		if err = wm.Embed(opts.Gravity, imgW, imgH, opts.OffsetX, opts.OffsetY, rgbColor{0, 0, 0}); err != nil {
-			return err
-		}
-	}
+func (img *vipsImage) ApplyWatermark(wm *vipsImage, opacity float64) error {
+	var tmp *C.VipsImage
 
-	if C.vips_apply_watermark(img.VipsImage, wm.VipsImage, &tmp, C.double(opts.Opacity)) != 0 {
+	if C.vips_apply_watermark(img.VipsImage, wm.VipsImage, &tmp, C.double(opacity)) != 0 {
 		return vipsError()
 	}
 	C.swap_and_clear(&img.VipsImage, tmp)

+ 0 - 1
vips.h

@@ -71,7 +71,6 @@ 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, double *bg, int bgn);
 
 int vips_ensure_alpha(VipsImage *in, VipsImage **out);
-int vips_apply_opacity(VipsImage *in, VipsImage **out, double opacity);
 
 int vips_apply_watermark(VipsImage *in, VipsImage *watermark, VipsImage **out, double opacity);
 

+ 30 - 5
watermark_data.go

@@ -9,22 +9,47 @@ import (
 	"os"
 )
 
-func watermarkData() ([]byte, imageType, context.CancelFunc, error) {
+type watermarkData struct {
+	data    []byte
+	imgtype imageType
+}
+
+func getWatermarkData() (*watermarkData, error) {
 	if len(conf.WatermarkData) > 0 {
 		data, imgtype, err := base64WatermarkData()
-		return data, imgtype, func() {}, err
+
+		if err != nil {
+			return nil, err
+		}
+
+		return &watermarkData{data, imgtype}, err
 	}
 
 	if len(conf.WatermarkPath) > 0 {
 		data, imgtype, err := fileWatermarkData()
-		return data, imgtype, func() {}, err
+
+		if err != nil {
+			return nil, err
+		}
+
+		return &watermarkData{data, imgtype}, err
 	}
 
 	if len(conf.WatermarkURL) > 0 {
-		return remoteWatermarkData()
+		b, imgtype, cancel, err := remoteWatermarkData()
+		defer cancel()
+
+		if err != nil {
+			return nil, err
+		}
+
+		data := make([]byte, len(b))
+		copy(data, b)
+
+		return &watermarkData{data, imgtype}, err
 	}
 
-	return nil, imageTypeUnknown, func() {}, nil
+	return nil, nil
 }
 
 func base64WatermarkData() ([]byte, imageType, error) {