1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-06-12 22:27:32 +02:00
Files
imgproxy/processing/watermark.go

172 lines
4.3 KiB
Go
Raw Normal View History

2021-04-26 17:52:50 +06:00
package processing
import (
"context"
"math"
2021-04-26 17:52:50 +06:00
2021-09-30 20:23:30 +06:00
"github.com/imgproxy/imgproxy/v3/config"
"github.com/imgproxy/imgproxy/v3/imagedata"
"github.com/imgproxy/imgproxy/v3/imath"
"github.com/imgproxy/imgproxy/v3/options"
"github.com/imgproxy/imgproxy/v3/vips"
2021-04-26 17:52:50 +06:00
)
var watermarkPipeline = pipeline{
prepare,
scaleOnLoad,
importColorProfile,
scale,
rotateAndFlip,
2021-12-07 19:04:21 +06:00
padding,
2021-04-26 17:52:50 +06:00
}
func prepareWatermark(wm *vips.Image, wmData *imagedata.ImageData, opts *options.WatermarkOptions, imgWidth, imgHeight int, offsetScale float64, framesCount int) error {
2021-04-26 17:52:50 +06:00
if err := wm.Load(wmData, 1, 1.0, 1); err != nil {
return err
}
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFit
po.Dpr = 1
po.Enlarge = true
po.Format = wmData.Type
if opts.Scale > 0 {
po.Width = imath.Max(imath.ScaleToEven(imgWidth, opts.Scale), 1)
po.Height = imath.Max(imath.ScaleToEven(imgHeight, opts.Scale), 1)
2021-04-26 17:52:50 +06:00
}
if opts.ShouldReplicate() {
var offX, offY int
2024-11-20 19:42:55 +03:00
if math.Abs(opts.Position.X) >= 1.0 {
offX = imath.RoundToEven(opts.Position.X * offsetScale)
} else {
2024-11-20 19:42:55 +03:00
offX = imath.ScaleToEven(imgWidth, opts.Position.X)
}
2024-11-20 19:42:55 +03:00
if math.Abs(opts.Position.Y) >= 1.0 {
offY = imath.RoundToEven(opts.Position.Y * offsetScale)
} else {
2024-11-20 19:42:55 +03:00
offY = imath.ScaleToEven(imgHeight, opts.Position.Y)
}
2021-12-07 19:04:21 +06:00
po.Padding.Enabled = true
po.Padding.Left = offX / 2
po.Padding.Right = offX - po.Padding.Left
po.Padding.Top = offY / 2
po.Padding.Bottom = offY - po.Padding.Top
2021-12-07 19:04:21 +06:00
}
2021-04-26 17:52:50 +06:00
if err := watermarkPipeline.Run(context.Background(), wm, po, wmData); err != nil {
return err
}
if opts.ShouldReplicate() || framesCount > 1 {
2023-01-16 20:31:11 +03:00
// We need to copy image if we're going to replicate.
// Replication requires image to be read several times, and this requires
// random access to pixels
if err := wm.CopyMemory(); err != nil {
return err
}
}
if opts.ShouldReplicate() {
if err := wm.Replicate(imgWidth, imgHeight, true); err != nil {
2023-01-16 20:31:11 +03:00
return err
}
}
2021-04-26 17:52:50 +06:00
// We don't want any headers to be copied from the watermark to the image
2024-11-22 01:36:58 +03:00
return wm.StripAll()
2021-04-26 17:52:50 +06:00
}
func applyWatermark(img *vips.Image, wmData *imagedata.ImageData, opts *options.WatermarkOptions, offsetScale float64, framesCount int) error {
2021-04-26 17:52:50 +06:00
wm := new(vips.Image)
defer wm.Clear()
width := img.Width()
height := img.Height()
2023-05-10 17:46:02 +03:00
frameHeight := height / framesCount
2021-04-26 17:52:50 +06:00
2023-05-10 17:46:02 +03:00
if err := prepareWatermark(wm, wmData, opts, width, frameHeight, offsetScale, framesCount); err != nil {
2021-04-26 17:52:50 +06:00
return err
}
if !img.ColourProfileImported() {
if err := img.ImportColourProfile(); err != nil {
return err
}
}
if err := img.RgbColourspace(); err != nil {
return err
}
2021-04-26 17:52:50 +06:00
opacity := opts.Opacity * config.WatermarkOpacity
2023-05-10 17:46:02 +03:00
// 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.ShouldReplicate() && framesCount > 1 {
if err := wm.Replicate(width, height, false); err != nil {
2023-05-10 17:46:02 +03:00
return err
}
return img.ApplyWatermark(wm, 0, 0, opacity)
}
left, top := 0, 0
wmWidth := wm.Width()
wmHeight := wm.Height()
2023-05-10 17:46:02 +03:00
if !opts.ShouldReplicate() {
2024-11-20 19:42:55 +03:00
left, top = calcPosition(width, frameHeight, wmWidth, wmHeight, &opts.Position, offsetScale, true)
}
if left >= width || top >= height || -left >= wmWidth || -top >= wmHeight {
// Watermark is completely outside the image
return nil
}
// if watermark is partially outside the image, it may partially be visible
// on the next frame. We need to crop it vertically.
// We don't care about horizontal overlap, as frames are stacked vertically
if framesCount > 1 {
cropTop := 0
cropHeight := wmHeight
if top < 0 {
cropTop = -top
cropHeight -= cropTop
top = 0
}
if top+cropHeight > frameHeight {
cropHeight = frameHeight - top
}
if cropTop > 0 || cropHeight < wmHeight {
if err := wm.Crop(0, cropTop, wmWidth, cropHeight); err != nil {
return err
}
}
2023-05-10 17:46:02 +03:00
}
for i := 0; i < framesCount; i++ {
if err := img.ApplyWatermark(wm, left, top, opacity); err != nil {
return err
}
top += frameHeight
}
return nil
2021-04-26 17:52:50 +06:00
}
func watermark(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error {
if !po.Watermark.Enabled || imagedata.Watermark == nil {
return nil
}
return applyWatermark(img, imagedata.Watermark, &po.Watermark, pctx.dprScale, 1)
2021-04-26 17:52:50 +06:00
}