diff --git a/process.go b/process.go index bdd6119f..40362ab6 100644 --- a/process.go +++ b/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) diff --git a/vips.c b/vips.c index 3e08e5d6..1a94373b 100644 --- a/vips.c +++ b/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; diff --git a/vips.go b/vips.go index 8d979236..6172bfd4 100644 --- a/vips.go +++ b/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 - } +func (img *vipsImage) ApplyWatermark(wm *vipsImage, opacity float64) error { + var tmp *C.VipsImage - 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 - } - } - - 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) diff --git a/vips.h b/vips.h index 5e42ca95..281cc20b 100644 --- a/vips.h +++ b/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); diff --git a/watermark_data.go b/watermark_data.go index 95e9b936..7c54d952 100644 --- a/watermark_data.go +++ b/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) {