mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-10-11 04:32:29 +02:00
Better watermarking
This commit is contained in:
74
process.go
74
process.go
@@ -202,6 +202,66 @@ func scaleSize(size int, scale float64) int {
|
|||||||
return roundToInt(float64(size) * scale)
|
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 {
|
func transformImage(ctx context.Context, img *vipsImage, data []byte, po *processingOptions, imgtype imageType) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -380,8 +440,8 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces
|
|||||||
|
|
||||||
checkTimeout(ctx)
|
checkTimeout(ctx)
|
||||||
|
|
||||||
if po.Watermark.Enabled {
|
if po.Watermark.Enabled && watermark != nil {
|
||||||
if err = img.ApplyWatermark(&po.Watermark); err != nil {
|
if err = applyWatermark(img, watermark, &po.Watermark, 1); err != nil {
|
||||||
return err
|
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
|
var errg errgroup.Group
|
||||||
|
|
||||||
for i := 0; i < framesCount; i++ {
|
for i := 0; i < framesCount; i++ {
|
||||||
@@ -477,6 +541,12 @@ func transformAnimated(ctx context.Context, img *vipsImage, data []byte, po *pro
|
|||||||
return err
|
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("page-height", frames[0].Height())
|
||||||
img.SetInt("gif-delay", delay)
|
img.SetInt("gif-delay", delay)
|
||||||
img.SetInt("gif-loop", loop)
|
img.SetInt("gif-loop", loop)
|
||||||
|
43
vips.c
43
vips.c
@@ -385,49 +385,6 @@ vips_ensure_alpha(VipsImage *in, VipsImage **out) {
|
|||||||
return vips_bandjoin_const1(in, out, 255, NULL);
|
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
|
int
|
||||||
vips_apply_watermark(VipsImage *in, VipsImage *watermark, VipsImage **out, double opacity) {
|
vips_apply_watermark(VipsImage *in, VipsImage *watermark, VipsImage **out, double opacity) {
|
||||||
VipsImage *wm, *wm_alpha, *tmp;
|
VipsImage *wm, *wm_alpha, *tmp;
|
||||||
|
109
vips.go
109
vips.go
@@ -25,7 +25,7 @@ var (
|
|||||||
vipsTypeSupportLoad = make(map[imageType]bool)
|
vipsTypeSupportLoad = make(map[imageType]bool)
|
||||||
vipsTypeSupportSave = make(map[imageType]bool)
|
vipsTypeSupportSave = make(map[imageType]bool)
|
||||||
|
|
||||||
watermark *vipsImage
|
watermark *watermarkData
|
||||||
)
|
)
|
||||||
|
|
||||||
var vipsConf struct {
|
var vipsConf struct {
|
||||||
@@ -126,7 +126,7 @@ func initVips() {
|
|||||||
|
|
||||||
vipsConf.WatermarkOpacity = C.double(conf.WatermarkOpacity)
|
vipsConf.WatermarkOpacity = C.double(conf.WatermarkOpacity)
|
||||||
|
|
||||||
if err := vipsPrepareWatermark(); err != nil {
|
if err := vipsLoadWatermark(); err != nil {
|
||||||
logFatal(err.Error())
|
logFatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,10 +134,6 @@ func initVips() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func shutdownVips() {
|
func shutdownVips() {
|
||||||
if watermark != nil {
|
|
||||||
watermark.Clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
C.vips_shutdown()
|
C.vips_shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,61 +157,8 @@ func vipsError() error {
|
|||||||
return newUnexpectedError(C.GoString(C.vips_error_buffer()), 1)
|
return newUnexpectedError(C.GoString(C.vips_error_buffer()), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func vipsPrepareWatermark() error {
|
func vipsLoadWatermark() (err error) {
|
||||||
data, imgtype, cancel, err := watermarkData()
|
watermark, err = getWatermarkData()
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,48 +575,10 @@ func (img *vipsImage) Embed(gravity gravityType, width, height int, offX, offY i
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (img *vipsImage) ApplyWatermark(opts *watermarkOptions) error {
|
func (img *vipsImage) ApplyWatermark(wm *vipsImage, opacity float64) error {
|
||||||
if watermark == nil {
|
var tmp *C.VipsImage
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
if C.vips_apply_watermark(img.VipsImage, wm.VipsImage, &tmp, C.double(opacity)) != 0 {
|
||||||
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 {
|
|
||||||
return vipsError()
|
return vipsError()
|
||||||
}
|
}
|
||||||
C.swap_and_clear(&img.VipsImage, tmp)
|
C.swap_and_clear(&img.VipsImage, tmp)
|
||||||
|
1
vips.h
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_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_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);
|
int vips_apply_watermark(VipsImage *in, VipsImage *watermark, VipsImage **out, double opacity);
|
||||||
|
|
||||||
|
@@ -9,22 +9,47 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func watermarkData() ([]byte, imageType, context.CancelFunc, error) {
|
type watermarkData struct {
|
||||||
|
data []byte
|
||||||
|
imgtype imageType
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWatermarkData() (*watermarkData, error) {
|
||||||
if len(conf.WatermarkData) > 0 {
|
if len(conf.WatermarkData) > 0 {
|
||||||
data, imgtype, err := base64WatermarkData()
|
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 {
|
if len(conf.WatermarkPath) > 0 {
|
||||||
data, imgtype, err := fileWatermarkData()
|
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 {
|
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) {
|
func base64WatermarkData() ([]byte, imageType, error) {
|
||||||
|
Reference in New Issue
Block a user