From 6ca6dd12de6e579f92038d868c5258f15d461974 Mon Sep 17 00:00:00 2001 From: Albert-Jan de Vries Date: Wed, 5 Apr 2017 16:33:34 +0200 Subject: [PATCH 1/2] Second try, watermarking memory issue fix --- resize.go | 8 +-- vips.go | 11 ++-- vips.h | 155 ++++++++++++++++----------------------------------- vips_test.go | 6 +- 4 files changed, 55 insertions(+), 125 deletions(-) diff --git a/resize.go b/resize.go index 6a1016c..1f4dedc 100644 --- a/resize.go +++ b/resize.go @@ -354,13 +354,7 @@ func watermarkImageWithAnotherImage(image *C.VipsImage, w WatermarkImage) (*C.Vi w.Opacity = 1.0 } - watermark, _, err := loadImage(w.Buf) - - if err != nil { - return nil, err - } - - image, err = vipsDrawWatermark(image, watermark, w) + image, err := vipsDrawWatermark(image, w) if err != nil { return nil, err diff --git a/vips.go b/vips.go index d826bbf..e0b0e43 100644 --- a/vips.go +++ b/vips.go @@ -596,15 +596,12 @@ func max(x int) int { return int(math.Max(float64(x), 0)) } -func vipsDrawWatermark(image *C.VipsImage, watermark *C.VipsImage, o WatermarkImage) (*C.VipsImage, error) { +func vipsDrawWatermark(image *C.VipsImage, o WatermarkImage) (*C.VipsImage, error) { var out *C.VipsImage - if !vipsHasAlpha(image) { - C.vips_add_band(image, &image, C.double(255.0)) - } - - if !vipsHasAlpha(watermark) { - C.vips_add_band(watermark, &watermark, C.double(255.0)) + watermark, _, e := vipsRead(o.Buf) + if e != nil { + return nil, e } opts := vipsWatermarkImageOptions{C.int(o.Left), C.int(o.Top), C.float(o.Opacity)} diff --git a/vips.h b/vips.h index febb516..1ac370b 100644 --- a/vips.h +++ b/vips.h @@ -463,112 +463,51 @@ vips_add_band(VipsImage *in, VipsImage **out, double c) { int vips_watermark_image(VipsImage *in, VipsImage *sub, VipsImage **out, WatermarkImageOptions *o) { - int bands = in->Bands; - double background[4] = {0.0, 0.0, 0.0, 0.0}; - - VipsArrayDouble *vipsBackground = vips_array_double_new(background, 4); - - VipsImage *base = vips_image_new(); - VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 10); - - t[0] = in; - t[1] = sub; - - if (vips_embed(t[1], &t[2], o->Left, o->Top, t[0]->Xsize, t[0]->Ysize, "extend", VIPS_EXTEND_BACKGROUND, "background", vipsBackground, NULL)) { - g_object_unref(base); - return 1; - } - - //Get Sub bands without alpha - if (vips_extract_band(t[2], &t[3], 0, "n", t[2]->Bands - 1, NULL)) { - g_object_unref(base); - return 1; - } - - //Get Sub Image alpha - if ( - vips_extract_band(t[2], &t[4], t[2]->Bands - 1, "n", 1, NULL) || - vips_linear1(t[4], &t[4], o->Opacity / 255.0, 0.0, NULL) - ) { - g_object_unref(base); - return 1; - } - - //Apply alpha to other sub bands to remove unwanted pixels - if (vips_multiply(t[3], t[4], &t[3], NULL)) { - g_object_unref(base); - return 1; - } - - //Get in bands without alpha - if (vips_extract_band(t[0], &t[5], 0, "n", t[0]->Bands - 1, NULL)) { - g_object_unref(base); - return 1; - } - - //Get in alpha - if ( - vips_extract_band(t[0], &t[6], t[0]->Bands - 1, "n", 1, NULL) || - vips_linear1(t[6], &t[6], 1.0 / 255.0, 0.0, NULL) - ) { - g_object_unref(base); - return 1; - } - - - // Compute normalized output alpha channel: - // - // References: - // - http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending - // - https://github.com/jcupitt/ruby-vips/issues/28#issuecomment-9014826 - // - // out_a = src_a + dst_a * (1 - src_a) - // ^^^^^^^^^^^ - - if ( - vips_linear1(t[4], &t[7], -1.0, 1.0, NULL) || - vips_multiply(t[6], t[7], &t[8], NULL) - ) { - g_object_unref(base); - return 1; - } - - //outAlphaNormalized in t[8] - if (vips_add(t[4], t[8], &t[8], NULL)) { - g_object_unref(base); - return 1; - } - - // Compute output RGB channels: - // - // Wikipedia: - // out_rgb = (src_rgb * src_a + dst_rgb * dst_a * (1 - src_a)) / out_a - // ^^^^^^^^^^^ - // t0 - // - // Omit division by `out_a` since `Compose` is supposed to output a - // premultiplied RGBA image as reversal of premultiplication is handled - // externally. - - if (vips_multiply(t[5], t[7], &t[9], NULL)) { - g_object_unref(base); - return 1; - } - - //outRGBPremultiplied in t[9] - if (vips_add(t[3], t[9], &t[9], NULL)) { - g_object_unref(base); - return 1; - } - - if ( - vips_linear1(t[8], &t[8], 255.0, 0.0, NULL) || - vips_bandjoin2(t[9], t[8], out, NULL) - ) { - g_object_unref(base); - return 1; - } - - g_object_unref(base); - return 0; + VipsImage *base = vips_image_new(); + VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 10); + + // add in and sub for unreffing and later use + t[0] = in; + t[1] = sub; + + if (has_alpha_channel(in) == 0) { + vips_bandjoin_const1(in, &t[0], 255.0, NULL); + // in is no longer in the array and won't be unreffed, so add it at the end + t[8] = in; + } + + if (has_alpha_channel(sub) == 0) { + vips_bandjoin_const1(sub, &t[1], 255.0, NULL); + // sub is no longer in the array and won't be unreffed, so add it at the end + t[9] = sub; + } + + // Place watermark image in the right place and size it to the size of the + // image that should be watermarked + if ( + vips_embed(t[1], &t[2], o->Left, o->Top, t[0]->Xsize, t[0]->Ysize, NULL)) { + g_object_unref(base); + return 1; + } + + // Create a mask image based on the alpha band from the watermark image + // and place it in the right position + if ( + vips_extract_band(t[1], &t[3], t[1]->Bands - 1, "n", 1, NULL) || + vips_linear1(t[3], &t[4], o->Opacity, 0.0, NULL) || + vips_cast(t[4], &t[5], VIPS_FORMAT_UCHAR, NULL) || + vips_copy(t[5], &t[6], "interpretation", t[0]->Type, NULL) || + vips_embed(t[6], &t[7], o->Left, o->Top, t[0]->Xsize, t[0]->Ysize, NULL)) { + g_object_unref(base); + return 1; + } + + // Blend the mask and watermark image and write to output. + if (vips_ifthenelse(t[7], t[2], t[0], out, "blend", TRUE, NULL)) { + g_object_unref(base); + return 1; + } + + g_object_unref(base); + return 0; } diff --git a/vips_test.go b/vips_test.go index 4faf46c..47b3f41 100644 --- a/vips_test.go +++ b/vips_test.go @@ -123,10 +123,10 @@ func TestVipsWatermark(t *testing.T) { func TestVipsWatermarkWithImage(t *testing.T) { image, _, _ := vipsRead(readImage("test.jpg")) - watermark, _, _ := vipsRead(readImage("transparent.png")) + watermark := readImage("transparent.png") - options := WatermarkImage{Left: 100, Top: 100, Opacity: 1.0} - newImg, err := vipsDrawWatermark(image, watermark, options) + options := WatermarkImage{Left: 100, Top: 100, Opacity: 1.0, Buf: watermark} + newImg, err := vipsDrawWatermark(image, options) if err != nil { t.Errorf("Cannot add watermark: %s", err) } From c19a86e12aa4f8258cbdb3cf25856f328171a4cb Mon Sep 17 00:00:00 2001 From: Albert-Jan de Vries Date: Wed, 5 Apr 2017 16:26:19 +0200 Subject: [PATCH 2/2] Change for older LIBVIPS versions. `vips_bandjoin_const1` is added in libvips 8.2. --- vips.h | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/vips.h b/vips.h index 1ac370b..67f081d 100644 --- a/vips.h +++ b/vips.h @@ -458,7 +458,19 @@ vips_sharpen_bridge(VipsImage *in, VipsImage **out, int radius, double x1, doubl int vips_add_band(VipsImage *in, VipsImage **out, double c) { - return vips_bandjoin_const1(in, out, c, NULL); +#if (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 2)) + return vips_bandjoin_const1(in, out, c, NULL); +#else + VipsImage *base = vips_image_new(); + if ( + vips_black(&base, in->Xsize, in->Ysize, NULL) || + vips_linear1(base, &base, 1, c, NULL)) { + g_object_unref(base); + return 1; + } + g_object_unref(base); + return vips_bandjoin2(in, base, out, c, NULL); +#endif } int @@ -471,13 +483,13 @@ vips_watermark_image(VipsImage *in, VipsImage *sub, VipsImage **out, WatermarkIm t[1] = sub; if (has_alpha_channel(in) == 0) { - vips_bandjoin_const1(in, &t[0], 255.0, NULL); + vips_add_band(in, &t[0], 255.0); // in is no longer in the array and won't be unreffed, so add it at the end t[8] = in; } if (has_alpha_channel(sub) == 0) { - vips_bandjoin_const1(sub, &t[1], 255.0, NULL); + vips_add_band(sub, &t[1], 255.0); // sub is no longer in the array and won't be unreffed, so add it at the end t[9] = sub; }