From f47a18495d8489af9fa2f802a95a1be02f0d5e7d Mon Sep 17 00:00:00 2001 From: Essem Date: Tue, 7 Jun 2022 16:27:47 -0500 Subject: [PATCH] Add speechbubble alpha option --- assets/images/speech.png | Bin 0 -> 4056 bytes commands/image-editing/speechbubble.js | 25 +++++-- natives/watermark.cc | 91 +++++++++++++++++-------- 3 files changed, 81 insertions(+), 35 deletions(-) create mode 100644 assets/images/speech.png diff --git a/assets/images/speech.png b/assets/images/speech.png new file mode 100644 index 0000000000000000000000000000000000000000..99cbf6314264c25d9f472029841c00bdbbd8816b GIT binary patch literal 4056 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 ZA^n(6dum##0RL47PIj)0n;7fV{{RBw!5#nr literal 0 HcmV?d00001 diff --git a/commands/image-editing/speechbubble.js b/commands/image-editing/speechbubble.js index c76739c..2127139 100644 --- a/commands/image-editing/speechbubble.js +++ b/commands/image-editing/speechbubble.js @@ -1,12 +1,25 @@ import ImageCommand from "../../classes/imageCommand.js"; class SpeechBubbleCommand extends ImageCommand { - params = { - water: "assets/images/speechbubble.png", - gravity: "north", - resize: true, - yscale: 0.2, - }; + params() { + return { + water: this.specialArgs.alpha ? "assets/images/speech.png" : "assets/images/speechbubble.png", + gravity: "north", + resize: true, + yscale: 0.2, + alpha: this.specialArgs.alpha + }; + } + + static init() { + super.init(); + this.flags.push({ + name: "alpha", + description: "Make the top of the speech bubble transparent", + type: 5 + }); + return this; + } static description = "Adds a speech bubble to an image"; static aliases = ["speech", "sb"]; diff --git a/natives/watermark.cc b/natives/watermark.cc index 7da60ef..ce62109 100644 --- a/natives/watermark.cc +++ b/natives/watermark.cc @@ -22,6 +22,8 @@ Napi::Value Watermark(const Napi::CallbackInfo &info) { bool append = obj.Has("append") ? obj.Get("append").As().Value() : false; + bool alpha = + obj.Has("alpha") ? obj.Get("alpha").As().Value() : false; bool mc = obj.Has("mc") ? obj.Get("mc").As().Value() : false; string basePath = obj.Get("basePath").As().Utf8Value(); string type = obj.Get("type").As().Utf8Value(); @@ -55,8 +57,40 @@ Napi::Value Watermark(const Napi::CallbackInfo &info) { watermark.resize((double)page_height / (double)watermark.height()); } + int x = 0, y = 0; + switch (gravity) { + case 1: + break; + case 2: + x = (width / 2) - (watermark.width() / 2); + break; + case 3: + x = width - watermark.width(); + break; + case 5: + x = (width / 2) - (watermark.width() / 2); + y = (page_height / 2) - (watermark.height() / 2); + break; + case 6: + x = width - watermark.width(); + y = (page_height / 2) - (watermark.height() / 2); + break; + case 8: + x = (width / 2) - (watermark.width() / 2); + y = page_height - watermark.height(); + break; + case 9: + x = width - watermark.width(); + y = page_height - watermark.height(); + break; + } + vector img; int addedHeight = 0; + VImage contentAlpha; + VImage frameAlpha; + VImage bg; + VImage frame; for (int i = 0; i < n_pages; i++) { VImage img_frame = type == "gif" ? in.crop(0, i * page_height, width, page_height) : in; @@ -77,36 +111,35 @@ Napi::Value Watermark(const Napi::CallbackInfo &info) { addedHeight = 15; img.push_back(composited); } else { - int x = 0, y = 0; - switch (gravity) { - case 1: - break; - case 2: - x = (width / 2) - (watermark.width() / 2); - break; - case 3: - x = width - watermark.width(); - break; - case 5: - x = (width / 2) - (watermark.width() / 2); - y = (page_height / 2) - (watermark.height() / 2); - break; - case 6: - x = width - watermark.width(); - y = (page_height / 2) - (watermark.height() / 2); - break; - case 8: - x = (width / 2) - (watermark.width() / 2); - y = page_height - watermark.height(); - break; - case 9: - x = width - watermark.width(); - y = page_height - watermark.height(); - break; - } - VImage composited = - img_frame.composite2(watermark, VIPS_BLEND_MODE_OVER, + VImage composited; + if (alpha) { + if (i == 0) { + contentAlpha = watermark.extract_band(0).embed( + x, y, width, page_height, + VImage::option()->set("extend", "white")); + frameAlpha = watermark.extract_band(1).embed( + x, y, width, page_height, + VImage::option()->set("extend", "black")); + bg = + frameAlpha.new_from_image({0, 0, 0}).copy(VImage::option()->set( + "interpretation", VIPS_INTERPRETATION_sRGB)); + frame = bg.bandjoin(frameAlpha); + if (type == "jpg" || type == "jpeg") { + type = "png"; + } + } + VImage content = + img_frame.extract_band(0, VImage::option()->set("n", 3)) + .bandjoin(contentAlpha & img_frame.extract_band(3)); + + composited = + content.composite2(frame, VIPS_BLEND_MODE_OVER, VImage::option()->set("x", x)->set("y", y)); + } else { + composited = + img_frame.composite2(watermark, VIPS_BLEND_MODE_OVER, + VImage::option()->set("x", x)->set("y", y)); + } img.push_back(composited); } }