From cd2e0bb57db5779e639c0b029fb14e52bdbb1655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Aparicio?= Date: Wed, 5 Apr 2017 09:58:54 +0100 Subject: [PATCH] Revert "Fix for memory issue when watermarking images" --- resize.go | 8 ++- vips.go | 11 ++-- vips.h | 155 +++++++++++++++++++++++++++++++++++---------------- vips_test.go | 6 +- 4 files changed, 125 insertions(+), 55 deletions(-) diff --git a/resize.go b/resize.go index 1f4dedc..6a1016c 100644 --- a/resize.go +++ b/resize.go @@ -354,7 +354,13 @@ func watermarkImageWithAnotherImage(image *C.VipsImage, w WatermarkImage) (*C.Vi w.Opacity = 1.0 } - image, err := vipsDrawWatermark(image, w) + watermark, _, err := loadImage(w.Buf) + + if err != nil { + return nil, err + } + + image, err = vipsDrawWatermark(image, watermark, w) if err != nil { return nil, err diff --git a/vips.go b/vips.go index e0b0e43..d826bbf 100644 --- a/vips.go +++ b/vips.go @@ -596,12 +596,15 @@ func max(x int) int { return int(math.Max(float64(x), 0)) } -func vipsDrawWatermark(image *C.VipsImage, o WatermarkImage) (*C.VipsImage, error) { +func vipsDrawWatermark(image *C.VipsImage, watermark *C.VipsImage, o WatermarkImage) (*C.VipsImage, error) { var out *C.VipsImage - watermark, _, e := vipsRead(o.Buf) - if e != nil { - return nil, e + 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)) } opts := vipsWatermarkImageOptions{C.int(o.Left), C.int(o.Top), C.float(o.Opacity)} diff --git a/vips.h b/vips.h index 1ac370b..febb516 100644 --- a/vips.h +++ b/vips.h @@ -463,51 +463,112 @@ vips_add_band(VipsImage *in, VipsImage **out, double c) { int vips_watermark_image(VipsImage *in, VipsImage *sub, VipsImage **out, WatermarkImageOptions *o) { - 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; + 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; } diff --git a/vips_test.go b/vips_test.go index 47b3f41..4faf46c 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 := readImage("transparent.png") + watermark, _, _ := vipsRead(readImage("transparent.png")) - options := WatermarkImage{Left: 100, Top: 100, Opacity: 1.0, Buf: watermark} - newImg, err := vipsDrawWatermark(image, options) + options := WatermarkImage{Left: 100, Top: 100, Opacity: 1.0} + newImg, err := vipsDrawWatermark(image, watermark, options) if err != nil { t.Errorf("Cannot add watermark: %s", err) }