diff --git a/assets/images/speech.png b/assets/images/speech.png new file mode 100644 index 0000000..99cbf63 Binary files /dev/null and b/assets/images/speech.png differ 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); } }