diff --git a/assets/images/sphere_overlay.png b/assets/images/sphere_overlay.png new file mode 100644 index 0000000..7ca735d Binary files /dev/null and b/assets/images/sphere_overlay.png differ diff --git a/commands/9gag.js b/commands/9gag.js index a0ab359..e36d76a 100644 --- a/commands/9gag.js +++ b/commands/9gag.js @@ -5,7 +5,7 @@ exports.run = async (message) => { message.channel.sendTyping(); const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to add a 9GAG watermark!`; - const buffer = await promisify(magick.watermark)(image.path, "./assets/images/9gag.png", 6, false, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); + const buffer = await promisify(magick.watermark)(image.path, "./assets/images/9gag.png", 6, false, false, false, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `9gag.${image.type}` diff --git a/commands/avatar.js b/commands/avatar.js index 2ea0889..15ebd12 100644 --- a/commands/avatar.js +++ b/commands/avatar.js @@ -12,7 +12,7 @@ exports.run = async (message, args) => { }); return member ? member.user.dynamicAvatarURL(null, 1024) : message.author.user.dynamicAvatarURL(null, 1024); } else { - return message.author.user.dynamicAvatarURL(null, 1024); + return message.author.dynamicAvatarURL(null, 1024); } }; diff --git a/commands/bandicam.js b/commands/bandicam.js index ceb3586..df0a26c 100644 --- a/commands/bandicam.js +++ b/commands/bandicam.js @@ -5,7 +5,7 @@ exports.run = async (message) => { message.channel.sendTyping(); const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to add a Bandicam watermark!`; - const buffer = await promisify(magick.watermark)(image.path, "./assets/images/bandicam.png", 2, true, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); + const buffer = await promisify(magick.watermark)(image.path, "./assets/images/bandicam.png", 2, true, false, false, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `bandicam.${image.type}` diff --git a/commands/deviantart.js b/commands/deviantart.js index 88c84d5..d5df3c4 100644 --- a/commands/deviantart.js +++ b/commands/deviantart.js @@ -5,7 +5,7 @@ exports.run = async (message) => { message.channel.sendTyping(); const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to add a DeviantArt watermark!`; - const buffer = await promisify(magick.watermark)(image.path, "./assets/images/deviantart.png", 5, true, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); + const buffer = await promisify(magick.watermark)(image.path, "./assets/images/deviantart.png", 5, true, false, false, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `deviantart.${image.type}` diff --git a/commands/funky.js b/commands/funky.js index 4d0ca93..1613706 100644 --- a/commands/funky.js +++ b/commands/funky.js @@ -5,7 +5,7 @@ exports.run = async (message) => { message.channel.sendTyping(); const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to add New Funky Mode!`; - const buffer = await promisify(magick.watermark)(image.path, "./assets/images/funky.png", 3, true, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); + const buffer = await promisify(magick.watermark)(image.path, "./assets/images/funky.png", 3, true, false, false, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `funky.${image.type}` diff --git a/commands/haah.js b/commands/haah.js index ea33d4c..4872b39 100644 --- a/commands/haah.js +++ b/commands/haah.js @@ -1,14 +1,11 @@ -const gm = require("gm").subClass({ - imageMagick: true -}); +const magick = require("../build/Release/image.node"); +const { promisify } = require("util"); exports.run = async (message) => { message.channel.sendTyping(); const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to mirror!`; - const data = `/tmp/${Math.random().toString(36).substring(2, 15)}.miff`; - await gm(image.path).coalesce().gravity("West").crop("50%", 0).out("+repage").writePromise(data); - const buffer = await gm(data).extent("%[fx:u.w*2]", "%[fx:u.h]").out("null:").out("(").out(data).flop().out(")").gravity("East").out("-layers", "Composite").bufferPromise(image.type, image.delay); + const buffer = await promisify(magick.mirror)(image.path, false, true, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `haah.${image.type}` diff --git a/commands/hooh.js b/commands/hooh.js index 7b4b7dd..268b88d 100644 --- a/commands/hooh.js +++ b/commands/hooh.js @@ -1,16 +1,11 @@ -const gm = require("gm").subClass({ - imageMagick: true -}); +const magick = require("../build/Release/image.node"); +const { promisify } = require("util"); exports.run = async (message) => { message.channel.sendTyping(); const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to mirror!`; - const data = `/tmp/${Math.random().toString(36).substring(2, 15)}.miff`; - const size = await gm(image.path).sizePromise(); - await gm(image.path).coalesce().gravity("South").crop(0, "50%").out("+repage").writePromise(data); - // const buffer = await gm(data2).extent("%[fx:u.w]", "%[fx:u.h*2]").out("null:").out(data).gravity("North").out("-layers", "Composite").bufferPromise(image.type, image.delay); - const buffer = await gm(data).extent(size.width, size.height).out("null:").out("(").out(data).flip().out(")").geometry(`+0+${size.height / 2}`).out("-layers", "Composite").bufferPromise(image.type, image.delay); + const buffer = await promisify(magick.mirror)(image.path, true, false, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `hooh.${image.type}` diff --git a/commands/hypercam.js b/commands/hypercam.js index b6ff6f4..49eefbc 100644 --- a/commands/hypercam.js +++ b/commands/hypercam.js @@ -1,13 +1,11 @@ -const gm = require("gm").subClass({ - imageMagick: true -}); +const magick = require("../build/Release/image.node"); +const { promisify } = require("util"); exports.run = async (message) => { message.channel.sendTyping(); const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to add a Hypercam watermark!`; - const watermark = "./assets/images/hypercam.png"; - const buffer = await gm(image.path).coalesce().out("null:").out(watermark).gravity("NorthWest").scale(null, "%[fx:u.h]").out("-layers", "composite").bufferPromise(image.type, image.delay); + const buffer = await promisify(magick.watermark)(image.path, "./assets/images/hypercam.png", 1, true, false, false, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `hypercam.${image.type}` diff --git a/commands/ifunny.js b/commands/ifunny.js index 1109853..d9f24dd 100644 --- a/commands/ifunny.js +++ b/commands/ifunny.js @@ -1,13 +1,11 @@ -const gm = require("gm").subClass({ - imageMagick: true -}); +const magick = require("../build/Release/image.node"); +const { promisify } = require("util"); exports.run = async (message) => { message.channel.sendTyping(); const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to add a iFunny watermark!`; - const watermark = "./assets/images/ifunny.png"; - const buffer = await gm(image.path).coalesce().extent("%[fx:u.w]", "%[fx:u.h+(42*min(u.w/1024,u.h/42))]").out("null:").out(watermark).gravity("South").scale("%[fx:u.w]", null).out("-layers", "composite").bufferPromise(image.type, image.delay); + const buffer = await promisify(magick.watermark)(image.path, "./assets/images/ifunny.png", 8, true, true, false, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `ifunny.${image.type}` diff --git a/commands/implode.js b/commands/implode.js index ffc63df..66f34c4 100644 --- a/commands/implode.js +++ b/commands/implode.js @@ -1,12 +1,11 @@ -const gm = require("gm").subClass({ - imageMagick: true -}); +const magick = require("../build/Release/image.node"); +const { promisify } = require("util"); exports.run = async (message) => { message.channel.sendTyping(); const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to implode!`; - const buffer = await gm(image.path).implode([1]).bufferPromise(image.type, image.delay); + const buffer = await promisify(magick.explode)(image.path, 1, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `implode.${image.type}` diff --git a/commands/jpeg.js b/commands/jpeg.js index 416fc67..17cd637 100644 --- a/commands/jpeg.js +++ b/commands/jpeg.js @@ -1,12 +1,11 @@ -const gm = require("gm").subClass({ - imageMagick: true -}); +const magick = require("../build/Release/image.node"); +const { promisify } = require("util"); exports.run = async (message) => { message.channel.sendTyping(); const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to add more JPEG!`; - const buffer = await gm(`${image.path}[0]`).quality(1).bufferPromise("jpg"); + const buffer = await promisify(magick.jpeg)(image.path); return { file: buffer, name: "jpeg.jpg" diff --git a/commands/leak.js b/commands/leak.js index f1305ef..828b90f 100644 --- a/commands/leak.js +++ b/commands/leak.js @@ -1,15 +1,12 @@ -const gm = require("gm").subClass({ - imageMagick: true -}); +const magick = require("../build/Release/image.node"); +const { promisify } = require("util"); exports.run = async (message) => { message.channel.sendTyping(); const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to make a Super Smash Bros. leak meme!`; - if (image.type === "gif") return `${message.author.mention}, this command doesn't work with GIFs!`; - const template = "./assets/images/leak.png"; - const buffer = await gm(template).out("-background").out("white").out("-gravity").out("Center").out("(").out("-clone").out("0").out("(").out(image.path).out("-virtual-pixel").out("white").out("-resize").out("640x360!").rotate("white", 15).out(")").out("-geometry").out("+450-200").out("-composite").out(")").out("+swap").out("-composite").out("-alpha").out("remove").out("-alpha").out("off").bufferPromise(image.type, image.delay); - // const command = gm(template).out("-background", "white").gravity("Center").out("null:").out("(").out(path).out("-resize", "640x360!").out("-virtual-pixel", "white").rotate("white", 15).coalesce().geometry("+450-200").out(")").compose("over").out("-alpha", "remove").out("-alpha", "off").out("-layers", "composite"); + const buffer = await promisify(magick.leak)(image.path, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); + //const buffer = await gm(template).out("-background").out("white").out("-gravity").out("Center").out("(").out("-clone").out("0").out("(").out(image.path).out("-virtual-pixel").out("white").out("-resize").out("640x360!").rotate("white", 15).out(")").out("-geometry").out("+450-200").out("-composite").out(")").out("+swap").out("-composite").out("-alpha").out("remove").out("-alpha").out("off").bufferPromise(image.type, image.delay); return { file: buffer, name: `leak.${image.type}` diff --git a/commands/magik.js b/commands/magik.js index 34585f3..f3a5b27 100644 --- a/commands/magik.js +++ b/commands/magik.js @@ -1,16 +1,14 @@ -const gm = require("gm").subClass({ - imageMagick: true -}); +const magick = require("../build/Release/image.node"); +const { promisify } = require("util"); exports.run = async (message) => { const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to add some magik!`; - if (image.type === "gif") return `${message.author.mention}, this command doesn't work with GIFs!`; const processMessage = await message.channel.createMessage(" Processing... This might take a while"); - const resultBuffer = await gm(image.path).in("(").in("(").coalesce().scale(600, 600).out(")").out("-liquid-rescale", "300x300").out(")").out("-liquid-rescale", "600x600").bufferPromise(image.type, image.delay); + const buffer = await promisify(magick.magik)(image.path, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); await processMessage.delete(); return { - file: resultBuffer, + file: buffer, name: `magik.${image.type}` }; }; diff --git a/commands/meme.js b/commands/meme.js index 0d7470c..60f5fd9 100644 --- a/commands/meme.js +++ b/commands/meme.js @@ -1,6 +1,5 @@ -const gm = require("gm").subClass({ - imageMagick: true -}); +const magick = require("../build/Release/image.node"); +const { promisify } = require("util"); exports.run = async (message, args) => { message.channel.sendTyping(); @@ -9,13 +8,14 @@ exports.run = async (message, args) => { const newArgs = args.filter(item => !item.includes(image.url) ); if (args.length === 0) return `${message.author.mention}, you need to provide some text to generate a meme!`; const [topText, bottomText] = newArgs.join(" ").split(/(? elem.trim()); - const file = `/tmp/${Math.random().toString(36).substring(2, 15)}.miff`; + /*const file = `/tmp/${Math.random().toString(36).substring(2, 15)}.miff`; const file2 = `/tmp/${Math.random().toString(36).substring(2, 15)}.png`; const file3 = `/tmp/${Math.random().toString(36).substring(2, 15)}.png`; await gm(image.path).coalesce().scale(600, 600).noProfile().writePromise(file); await gm(file).out("-size", "%[fx:u.w]").out("-delete", "0--1").background("none").gravity("Center").out("(", "(").font("Impact").out("-pointsize", 40).out(`pango:${topText.toUpperCase().replace(/&/g, "\\&").replace(/>/g, "\\>").replace(/`).out(")", "(", "+clone").out("-alpha", "extract").out("-morphology", "EdgeOut", "Octagon", "-background", "black", "-alpha", "shape", ")").compose("DstOver").out(")", "-composite").writePromise(file2); if (bottomText) await gm(file).out("-size", "%[fx:u.w]").out("-delete", "0--1").background("none").gravity("Center").out("(", "(").font("Impact").out("-pointsize", 40).out(`pango:${bottomText.toUpperCase().replace(/&/g, "\\&").replace(/>/g, "\\>").replace(/`).out(")", "(", "+clone").out("-alpha", "extract").out("-morphology", "EdgeOut", "Octagon", "-background", "black", "-alpha", "shape", ")").compose("DstOver").out(")", "-composite").writePromise(file3); - const buffer = await gm(file).out("-coalesce").out("null:").gravity("North").out(file2).out("-layers", "composite").out("null:").gravity("South").out(bottomText ? file3 : "null:").out("-layers", "composite").bufferPromise(image.type, image.delay); + const buffer = await gm(file).out("-coalesce").out("null:").gravity("North").out(file2).out("-layers", "composite").out("null:").gravity("South").out(bottomText ? file3 : "null:").out("-layers", "composite").bufferPromise(image.type, image.delay);*/ + const buffer = await promisify(magick.meme)(image.path, topText.toUpperCase().replace(/&/g, "\\&").replace(/>/g, "\\>").replace(//g, "\\>").replace(/ { message.channel.sendTyping(); const image = await require("../utils/imagedetect.js")(message); if (image === undefined) return `${message.author.mention}, you need to provide an image to add a MemeCenter watermark!`; - const watermark = "./assets/images/memecenter.png"; - const output = await gm(image.path).coalesce().background("white").extent("%[fx:u.w]", "%[fx:u.h+15]").out("null:").out(watermark).gravity("SouthEast").compose("over").out("-layers", "composite").bufferPromise(image.type, image.delay); + const buffer = await promisify(magick.watermark)(image.path, "./assets/images/memecenter.png", 9, false, false, true, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { - file: output, + file: buffer, name: `memecenter.${image.type}` }; }; diff --git a/natives/explode.cc b/natives/explode.cc index 3035b3b..7451c1f 100644 --- a/natives/explode.cc +++ b/natives/explode.cc @@ -7,8 +7,8 @@ using namespace Magick; class ExplodeWorker : public Napi::AsyncWorker { public: - ExplodeWorker(Napi::Function& callback, string in_path, string type, int delay) - : Napi::AsyncWorker(callback), in_path(in_path), type(type), delay(delay) {} + ExplodeWorker(Napi::Function& callback, string in_path, int amount, string type, int delay) + : Napi::AsyncWorker(callback), in_path(in_path), amount(amount), type(type), delay(delay) {} ~ExplodeWorker() {} void Execute() { @@ -20,7 +20,7 @@ class ExplodeWorker : public Napi::AsyncWorker { coalesceImages(&coalesced, frames.begin(), frames.end()); for (Image &image : coalesced) { - image.implode(-1); + image.implode(amount); image.magick(type); blurred.push_back(image); } @@ -36,7 +36,7 @@ class ExplodeWorker : public Napi::AsyncWorker { private: string in_path, type; - int delay, wordlength, i, n; + int delay, wordlength, i, n, amount; size_t bytes, type_size; Blob blob; }; @@ -46,11 +46,12 @@ Napi::Value Explode(const Napi::CallbackInfo &info) Napi::Env env = info.Env(); string in_path = info[0].As().Utf8Value(); - string type = info[1].As().Utf8Value(); - int delay = info[2].As().Int32Value(); - Napi::Function cb = info[3].As(); + int amount = info[1].As().Int32Value(); + string type = info[2].As().Utf8Value(); + int delay = info[3].As().Int32Value(); + Napi::Function cb = info[4].As(); - ExplodeWorker* explodeWorker = new ExplodeWorker(cb, in_path, type, delay); + ExplodeWorker* explodeWorker = new ExplodeWorker(cb, in_path, amount, type, delay); explodeWorker->Queue(); return env.Undefined(); } \ No newline at end of file diff --git a/natives/globe.cc b/natives/globe.cc index 6f6a72a..9a194dc 100644 --- a/natives/globe.cc +++ b/natives/globe.cc @@ -18,8 +18,10 @@ class GlobeWorker : public Napi::AsyncWorker { list mid; list result; Image distort; + Image overlay; readImages(&frames, in_path); distort.read("./assets/images/spheremap.png"); + overlay.read("./assets/images/sphere_overlay.png"); coalesceImages(&coalesced, frames.begin(), frames.end()); if (type != "GIF") { @@ -30,12 +32,12 @@ class GlobeWorker : public Napi::AsyncWorker { } int i = 0; - for (Image &image : coalesced) { image.scale(Geometry("500x500!")); image.alphaChannel(Magick::SetAlphaChannel); size_t width = image.columns(); image.roll(Geometry("+" + to_string(width * i / coalesced.size()))); + image.composite(overlay, Magick::CenterGravity, Magick::HardLightCompositeOp); image.composite(distort, Magick::CenterGravity, Magick::DistortCompositeOp); image.magick("GIF"); mid.push_back(image); diff --git a/natives/image.cc b/natives/image.cc index ebed45d..30b9285 100644 --- a/natives/image.cc +++ b/natives/image.cc @@ -12,6 +12,11 @@ #include "gamexplain.h" #include "globe.h" #include "invert.h" +#include "jpeg.h" +#include "leak.h" +#include "magik.h" +#include "meme.h" +#include "mirror.h" #include "watermark.h" Napi::Object Init(Napi::Env env, Napi::Object exports) @@ -29,6 +34,11 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) exports.Set(Napi::String::New(env, "gamexplain"), Napi::Function::New(env, Gamexplain)); exports.Set(Napi::String::New(env, "globe"), Napi::Function::New(env, Globe)); exports.Set(Napi::String::New(env, "invert"), Napi::Function::New(env, Invert)); + exports.Set(Napi::String::New(env, "jpeg"), Napi::Function::New(env, Jpeg)); + exports.Set(Napi::String::New(env, "leak"), Napi::Function::New(env, Leak)); + exports.Set(Napi::String::New(env, "magik"), Napi::Function::New(env, Magik)); + exports.Set(Napi::String::New(env, "meme"), Napi::Function::New(env, Meme)); + exports.Set(Napi::String::New(env, "mirror"), Napi::Function::New(env, Mirror)); exports.Set(Napi::String::New(env, "watermark"), Napi::Function::New(env, Watermark)); return exports; } diff --git a/natives/jpeg.cc b/natives/jpeg.cc new file mode 100644 index 0000000..f81e9fc --- /dev/null +++ b/natives/jpeg.cc @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +using namespace std; +using namespace Magick; + +class JpegWorker : public Napi::AsyncWorker { + public: + JpegWorker(Napi::Function& callback, string in_path) + : Napi::AsyncWorker(callback), in_path(in_path) {} + ~JpegWorker() {} + + void Execute() { + Image image; + image.read(in_path); + image.quality(1); + image.magick("JPEG"); + image.write(&blob); + } + + void OnOK() { + Callback().Call({Env().Undefined(), Napi::Buffer::Copy(Env(), (char *)blob.data(), blob.length())}); + } + + private: + string in_path, type; + int delay, wordlength, i, n; + size_t bytes, type_size; + Blob blob; +}; + +Napi::Value Jpeg(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + + string in_path = info[0].As().Utf8Value(); + Napi::Function cb = info[1].As(); + + JpegWorker* explodeWorker = new JpegWorker(cb, in_path); + explodeWorker->Queue(); + return env.Undefined(); +} \ No newline at end of file diff --git a/natives/jpeg.h b/natives/jpeg.h new file mode 100644 index 0000000..c621f26 --- /dev/null +++ b/natives/jpeg.h @@ -0,0 +1,8 @@ +#ifndef ESMBOT_NATIVES_JPEG_H_ +#define ESMBOT_NATIVES_JPEG_H_ + +#include + +Napi::Value Jpeg(const Napi::CallbackInfo& info); + +#endif \ No newline at end of file diff --git a/natives/leak.cc b/natives/leak.cc new file mode 100644 index 0000000..5a325f7 --- /dev/null +++ b/natives/leak.cc @@ -0,0 +1,60 @@ +#include +#include +#include + +using namespace std; +using namespace Magick; + +class LeakWorker : public Napi::AsyncWorker { + public: + LeakWorker(Napi::Function& callback, string in_path, string type, int delay) + : Napi::AsyncWorker(callback), in_path(in_path), type(type), delay(delay) {} + ~LeakWorker() {} + + void Execute() { + list frames; + list coalesced; + list mid; + Image watermark; + readImages(&frames, in_path); + watermark.read("./assets/images/leak.png"); + coalesceImages(&coalesced, frames.begin(), frames.end()); + + for (Image &image : coalesced) { + image.backgroundColor("white"); + image.scale(Geometry("640x360!")); + image.rotate(15); + image.extent(Geometry("1280x720-700+100")); + image.composite(watermark, Geometry("+0+0"), Magick::OverCompositeOp); + image.magick(type); + mid.push_back(image); + } + + if (delay != 0) for_each(mid.begin(), mid.end(), animationDelayImage(delay)); + writeImages(mid.begin(), mid.end(), &blob); + } + + void OnOK() { + Callback().Call({Env().Undefined(), Napi::Buffer::Copy(Env(), (char *)blob.data(), blob.length())}); + } + + private: + string in_path, type; + int delay, wordlength, i, n; + size_t bytes, type_size; + Blob blob; +}; + +Napi::Value Leak(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + + string in_path = info[0].As().Utf8Value(); + string type = info[1].As().Utf8Value(); + int delay = info[2].As().Int32Value(); + Napi::Function cb = info[3].As(); + + LeakWorker* blurWorker = new LeakWorker(cb, in_path, type, delay); + blurWorker->Queue(); + return env.Undefined(); +} \ No newline at end of file diff --git a/natives/leak.h b/natives/leak.h new file mode 100644 index 0000000..6ebad5f --- /dev/null +++ b/natives/leak.h @@ -0,0 +1,8 @@ +#ifndef ESMBOT_NATIVES_LEAK_H_ +#define ESMBOT_NATIVES_LEAK_H_ + +#include + +Napi::Value Leak(const Napi::CallbackInfo& info); + +#endif \ No newline at end of file diff --git a/natives/magik.cc b/natives/magik.cc new file mode 100644 index 0000000..f77f6c5 --- /dev/null +++ b/natives/magik.cc @@ -0,0 +1,58 @@ +#include +#include +#include + +using namespace std; +using namespace Magick; + +class MagikWorker : public Napi::AsyncWorker { + public: + MagikWorker(Napi::Function& callback, string in_path, string type, int delay) + : Napi::AsyncWorker(callback), in_path(in_path), type(type), delay(delay) {} + ~MagikWorker() {} + + void Execute() { + list frames; + list coalesced; + list blurred; + list result; + readImages(&frames, in_path); + coalesceImages(&coalesced, frames.begin(), frames.end()); + + for (Image &image : coalesced) { + image.scale(Geometry("350x350")); + image.liquidRescale(Geometry("175x175")); + image.liquidRescale(Geometry("350x350")); + image.magick(type); + blurred.push_back(image); + } + + optimizeImageLayers(&result, blurred.begin(), blurred.end()); + if (delay != 0) for_each(result.begin(), result.end(), animationDelayImage(delay)); + writeImages(result.begin(), result.end(), &blob); + } + + void OnOK() { + Callback().Call({Env().Undefined(), Napi::Buffer::Copy(Env(), (char *)blob.data(), blob.length())}); + } + + private: + string in_path, type; + int delay, wordlength, i, n, amount; + size_t bytes, type_size; + Blob blob; +}; + +Napi::Value Magik(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + + string in_path = info[0].As().Utf8Value(); + string type = info[1].As().Utf8Value(); + int delay = info[2].As().Int32Value(); + Napi::Function cb = info[3].As(); + + MagikWorker* explodeWorker = new MagikWorker(cb, in_path, type, delay); + explodeWorker->Queue(); + return env.Undefined(); +} \ No newline at end of file diff --git a/natives/magik.h b/natives/magik.h new file mode 100644 index 0000000..8f8c107 --- /dev/null +++ b/natives/magik.h @@ -0,0 +1,8 @@ +#ifndef ESMBOT_NATIVES_MAGIK_H_ +#define ESMBOT_NATIVES_MAGIK_H_ + +#include + +Napi::Value Magik(const Napi::CallbackInfo& info); + +#endif \ No newline at end of file diff --git a/natives/meme.cc b/natives/meme.cc new file mode 100644 index 0000000..00471d0 --- /dev/null +++ b/natives/meme.cc @@ -0,0 +1,89 @@ +#include +#include +#include +#include + +using namespace std; +using namespace Magick; + +class MemeWorker : public Napi::AsyncWorker { + public: + MemeWorker(Napi::Function& callback, string in_path, string text_top, string text_bottom, string type, int delay) + : Napi::AsyncWorker(callback), in_path(in_path), text_top(text_top), text_bottom(text_bottom), type(type), delay(delay) {} + ~MemeWorker() {} + + void Execute() { + list frames; + list coalesced; + list mid; + Image top_text; + Image bottom_text; + readImages(&frames, in_path); + coalesceImages(&coalesced, frames.begin(), frames.end()); + for_each(coalesced.begin(), coalesced.end(), scaleImage(Geometry(600, 600))); + + top_text.size(Geometry(to_string(coalesced.front().columns()))); + top_text.backgroundColor("none"); + top_text.font("Impact"); + top_text.fontPointsize(40); + top_text.textGravity(Magick::CenterGravity); + top_text.read("pango:" + text_top + ""); + Image top_text_fill = top_text; + top_text_fill.channel(Magick::AlphaChannel); + top_text_fill.morphology(Magick::EdgeOutMorphology, "Octagon"); + top_text_fill.backgroundColor("black"); + top_text_fill.alphaChannel(Magick::ShapeAlphaChannel); + top_text.composite(top_text_fill, Magick::CenterGravity, Magick::DstOverCompositeOp); + + if (text_bottom != "") { + bottom_text.size(Geometry(to_string(coalesced.front().columns()))); + bottom_text.backgroundColor("none"); + bottom_text.font("Impact"); + bottom_text.fontPointsize(40); + bottom_text.textGravity(Magick::CenterGravity); + bottom_text.read("pango:" + text_bottom + ""); + Image bottom_text_fill = bottom_text; + bottom_text_fill.channel(Magick::AlphaChannel); + bottom_text_fill.morphology(Magick::EdgeOutMorphology, "Octagon"); + bottom_text_fill.backgroundColor("black"); + bottom_text_fill.alphaChannel(Magick::ShapeAlphaChannel); + bottom_text.composite(bottom_text_fill, Magick::CenterGravity, Magick::DstOverCompositeOp); + } + + for (Image &image : coalesced) { + image.composite(top_text, Magick::NorthGravity, Magick::OverCompositeOp); + if (text_bottom != "") image.composite(bottom_text, Magick::SouthGravity, Magick::OverCompositeOp); + image.magick(type); + mid.push_back(image); + } + + if (delay != 0) for_each(mid.begin(), mid.end(), animationDelayImage(delay)); + writeImages(mid.begin(), mid.end(), &blob); + } + + void OnOK() { + Callback().Call({Env().Undefined(), Napi::Buffer::Copy(Env(), (char *)blob.data(), blob.length())}); + } + + private: + string in_path, type, text_top, text_bottom; + int delay, wordlength, i, n; + size_t bytes, type_size; + Blob blob; +}; + +Napi::Value Meme(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + + string in_path = info[0].As().Utf8Value(); + string text_top = info[1].As().Utf8Value(); + string text_bottom = info[2].As().Utf8Value(); + string type = info[3].As().Utf8Value(); + int delay = info[4].As().Int32Value(); + Napi::Function cb = info[5].As(); + + MemeWorker* blurWorker = new MemeWorker(cb, in_path, text_top, text_bottom, type, delay); + blurWorker->Queue(); + return env.Undefined(); +} \ No newline at end of file diff --git a/natives/meme.h b/natives/meme.h new file mode 100644 index 0000000..09099fa --- /dev/null +++ b/natives/meme.h @@ -0,0 +1,8 @@ +#ifndef ESMBOT_NATIVES_MEME_H_ +#define ESMBOT_NATIVES_MEME_H_ + +#include + +Napi::Value Meme(const Napi::CallbackInfo& info); + +#endif \ No newline at end of file diff --git a/natives/mirror.cc b/natives/mirror.cc new file mode 100644 index 0000000..6607df2 --- /dev/null +++ b/natives/mirror.cc @@ -0,0 +1,85 @@ +#include +#include +#include + +using namespace std; +using namespace Magick; + +class MirrorWorker : public Napi::AsyncWorker { + public: + MirrorWorker(Napi::Function& callback, string in_path, bool vertical, bool first, string type, int delay) + : Napi::AsyncWorker(callback), in_path(in_path), vertical(vertical), first(first), type(type), delay(delay) {} + ~MirrorWorker() {} + + void Execute() { + list frames; + list coalesced; + list mid; + list result; + MagickCore::GravityType gravity; + readImages(&frames, in_path); + coalesceImages(&coalesced, frames.begin(), frames.end()); + + if (vertical && first) { + gravity = Magick::NorthGravity; + } else if (!vertical && first) { + gravity = Magick::WestGravity; + } else if (vertical && !first) { + gravity = Magick::SouthGravity; + } else { + gravity = Magick::EastGravity; + } + + for (Image &image : coalesced) { + list mirrored; + Image final; + image.extent(Geometry(to_string(vertical ? image.baseColumns() : image.baseColumns() / 2) + "x" + to_string(vertical ? image.baseRows() / 2 : image.baseRows())), gravity); + mirrored.push_back(image); + Image mirror = image; + if (vertical) { + mirror.flip(); + } else { + mirror.flop(); + } + if (first) { + mirrored.push_back(mirror); + } else { + mirrored.push_front(mirror); + } + appendImages(&final, mirrored.begin(), mirrored.end(), vertical); + final.magick(type); + mid.push_back(final); + } + + optimizeImageLayers(&result, mid.begin(), mid.end()); + if (delay != 0) for_each(result.begin(), result.end(), animationDelayImage(delay)); + writeImages(result.begin(), result.end(), &blob); + } + + void OnOK() { + Callback().Call({Env().Undefined(), Napi::Buffer::Copy(Env(), (char *)blob.data(), blob.length())}); + } + + private: + string in_path, type; + int delay, wordlength, i, n; + size_t bytes, type_size; + bool vertical, first; + Blob blob; +}; + +Napi::Value Mirror(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + + string in_path = info[0].As().Utf8Value(); + bool vertical = info[1].As().Value(); + bool first = info[2].As().Value(); + string type = info[3].As().Utf8Value(); + int delay = info[4].As().Int32Value(); + Napi::Function cb = info[5].As(); + + MirrorWorker* mirrorWorker = new MirrorWorker(cb, in_path, vertical, first, type, delay); + mirrorWorker->Queue(); + return env.Undefined(); +} \ No newline at end of file diff --git a/natives/mirror.h b/natives/mirror.h new file mode 100644 index 0000000..d0631b5 --- /dev/null +++ b/natives/mirror.h @@ -0,0 +1,8 @@ +#ifndef ESMBOT_NATIVES_MIRROR_H_ +#define ESMBOT_NATIVES_MIRROR_H_ + +#include + +Napi::Value Mirror(const Napi::CallbackInfo& info); + +#endif \ No newline at end of file diff --git a/natives/watermark.cc b/natives/watermark.cc index 8e07502..9354279 100644 --- a/natives/watermark.cc +++ b/natives/watermark.cc @@ -7,8 +7,8 @@ using namespace Magick; class WatermarkWorker : public Napi::AsyncWorker { public: - WatermarkWorker(Napi::Function& callback, string in_path, string water_path, int gravity, bool resize, string type, int delay) - : Napi::AsyncWorker(callback), in_path(in_path), water_path(water_path), gravity(gravity), resize(resize), type(type), delay(delay) {} + WatermarkWorker(Napi::Function& callback, string in_path, string water_path, int gravity, bool resize, bool append, bool mc, string type, int delay) + : Napi::AsyncWorker(callback), in_path(in_path), water_path(water_path), gravity(gravity), resize(resize), append(append), mc(mc), type(type), delay(delay) {} ~WatermarkWorker() {} void Execute() { @@ -19,16 +19,33 @@ class WatermarkWorker : public Napi::AsyncWorker { Image watermark; readImages(&frames, in_path); watermark.read(water_path); - if (resize) { + if (resize && append) { + string query(to_string(frames.front().baseColumns()) + "x"); + watermark.scale(Geometry(query)); + } else if (resize) { string query("x" + to_string(frames.front().baseRows())); watermark.scale(Geometry(query)); } coalesceImages(&coalesced, frames.begin(), frames.end()); for (Image &image : coalesced) { - image.composite(watermark, Magick::GravityType(gravity), Magick::OverCompositeOp); + Image final; + if (append) { + list to_append; + to_append.push_back(image); + to_append.push_back(watermark); + appendImages(&final, to_append.begin(), to_append.end(), true); + } else if (mc) { + image.backgroundColor("white"); + image.extent(Geometry(image.columns(), image.rows() + 15)); + image.composite(watermark, Magick::GravityType(gravity), Magick::OverCompositeOp); + final = image; + } else { + image.composite(watermark, Magick::GravityType(gravity), Magick::OverCompositeOp); + final = image; + } image.magick(type); - mid.push_back(image); + mid.push_back(final); } optimizeImageLayers(&result, mid.begin(), mid.end()); @@ -45,7 +62,7 @@ class WatermarkWorker : public Napi::AsyncWorker { int delay, wordlength, i, n, gravity; size_t bytes, type_size; Blob blob; - bool resize; + bool resize, append, mc; }; Napi::Value Watermark(const Napi::CallbackInfo &info) @@ -56,11 +73,13 @@ Napi::Value Watermark(const Napi::CallbackInfo &info) string water = info[1].As().Utf8Value(); int gravity = info[2].As().Int32Value(); bool resize = info[3].As().Value(); - string type = info[4].As().Utf8Value(); - int delay = info[5].As().Int32Value(); - Napi::Function cb = info[6].As(); + bool append = info[4].As().Value(); + bool mc = info[5].As().Value(); + string type = info[6].As().Utf8Value(); + int delay = info[7].As().Int32Value(); + Napi::Function cb = info[8].As(); - WatermarkWorker* watermarkWorker = new WatermarkWorker(cb, in_path, water, gravity, resize, type, delay); + WatermarkWorker* watermarkWorker = new WatermarkWorker(cb, in_path, water, gravity, resize, append, mc, type, delay); watermarkWorker->Queue(); return env.Undefined(); } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 61cf05c..52300f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -727,8 +727,8 @@ } }, "erlpack": { - "version": "0.1.3", - "resolved": "github:abalabahaha/erlpack#5d0064f9e106841e1eead711a6451f99b0d289fd", + "version": "github:abalabahaha/erlpack#5d0064f9e106841e1eead711a6451f99b0d289fd", + "from": "github:abalabahaha/erlpack", "optional": true, "requires": { "bindings": "^1.5.0", @@ -1641,9 +1641,9 @@ } }, "node-addon-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.0.tgz", - "integrity": "sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.1.tgz", + "integrity": "sha512-YUpjl57P55u2yUaKX5Bgy4t5s6SCNYMg+62XNg+k41aYbBL1NgWrZfcgljR5MxDxHDjzl0qHDNtH6SkW4DXNCA==" }, "node-emoji": { "version": "1.10.0", @@ -2253,9 +2253,9 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "simple-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", - "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" }, "simple-get": { "version": "4.0.0", @@ -2792,9 +2792,9 @@ } }, "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" diff --git a/package.json b/package.json index e4712d7..21ad0f2 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "lavacord": "^1.1.9", "moment": "^2.27.0", "moment-duration-format": "^2.3.2", - "node-addon-api": "^3.0.0", + "node-addon-api": "^3.0.1", "node-emoji": "^1.10.0", "node-fetch": "^2.6.0", "node-tweet": "^0.1.4",