From 95554062296ad8ccbc246cddf27c4ae4129c2475 Mon Sep 17 00:00:00 2001 From: TheEssem Date: Thu, 23 Jul 2020 15:49:34 -0500 Subject: [PATCH] Final Magick++ ports, removed gm module --- commands/caption.js | 13 ++------ commands/caption2.js | 11 ++----- commands/homebrew.js | 1 - commands/meme.js | 7 ---- commands/pixelate.js | 2 +- commands/scott.js | 2 -- commands/sonic.js | 2 -- commands/spin.js | 2 -- commands/stretch.js | 2 +- commands/tile.js | 2 -- commands/trump.js | 1 - commands/waaw.js | 13 ++------ commands/wall.js | 8 ++--- commands/wdt.js | 8 ++--- commands/wide.js | 7 ++-- commands/woow.js | 11 ++----- events/ready.js | 39 ----------------------- natives/caption.cc | 76 ++++++++++++++++++++++++++++++++++++++++++++ natives/caption.h | 8 +++++ natives/caption2.cc | 75 +++++++++++++++++++++++++++++++++++++++++++ natives/caption2.h | 8 +++++ natives/image.cc | 12 ++++--- natives/resize.cc | 17 ++++++---- natives/wall.cc | 62 ++++++++++++++++++++++++++++++++++++ natives/wall.h | 8 +++++ natives/wdt.cc | 60 ++++++++++++++++++++++++++++++++++ natives/wdt.h | 8 +++++ package-lock.json | 62 ++---------------------------------- package.json | 1 - utils/gmbuffer.js | 39 ----------------------- 30 files changed, 349 insertions(+), 218 deletions(-) create mode 100644 natives/caption.cc create mode 100644 natives/caption.h create mode 100644 natives/caption2.cc create mode 100644 natives/caption2.h create mode 100644 natives/wall.cc create mode 100644 natives/wall.h create mode 100644 natives/wdt.cc create mode 100644 natives/wdt.h delete mode 100644 utils/gmbuffer.js diff --git a/commands/caption.js b/commands/caption.js index 45cdb1a..c1fff2f 100644 --- a/commands/caption.js +++ b/commands/caption.js @@ -1,7 +1,5 @@ -const gm = require("gm").subClass({ - imageMagick: true -}); -//const upload = require("../utils/upload.js"); +const magick = require("../build/Release/image.node"); +const { promisify } = require("util"); exports.run = async (message, args) => { const image = await require("../utils/imagedetect.js")(message); @@ -9,13 +7,8 @@ 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 add a caption!`; const processMessage = await message.channel.createMessage(" Processing... This might take a while"); - const size = await gm(image.path).sizePromise(); - const output = await gm().out("-size", `${size.width - ((size.width / 25) * 2)}x`).background("white").fill("black").font("./assets/caption.otf", size.width / 10).gravity("Center").out(`caption:${newArgs.join(" ")}`).extent(size.width, `%[fx:h+${size.width / 25}]`).bufferPromise("png"); - const size2 = await gm(output).sizePromise(); - //const output2 = await gm(output).gravity("Center").trim().out("+repage").extent(size.width, size2.height + (size.width / 10)).streamPromise(); - const outputFinal = await gm(output).in("(").gravity("Center").trim().out("+repage").extent(size.width, size2.height + (size.width / 10)).out(")").background("white").out("-alpha", "set").out("(").out(image.path).out("-coalesce").out(")").colorspace("sRGB").out("-set", "page", "%[fx:u.w]x%[fx:u.h+v.h]+%[fx:t?(u.w-v.w)/2:0]+%[fx:t?u.h:0]").out("-coalesce").out("null:").out("-insert", 1).out("-layers", "composite").bufferPromise(image.type, image.delay); + const outputFinal = await promisify(magick.caption)(newArgs.join(" "), image.path, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); await processMessage.delete(); - //return upload(message, outputFinal, `caption.${image.type}`); return { file: outputFinal, name: `caption.${image.type}` diff --git a/commands/caption2.js b/commands/caption2.js index 833f2f4..54e6511 100644 --- a/commands/caption2.js +++ b/commands/caption2.js @@ -1,6 +1,5 @@ -const gm = require("gm").subClass({ - imageMagick: true -}); +const magick = require("../build/Release/image.node"); +const { promisify } = require("util"); const words = ["me irl", "dank", "follow my second account @esmBot_", "2016", "meme", "wholesome", "reddit", "instagram", "twitter", "facebook", "fortnite", "minecraft", "relatable", "gold", "funny", "template", "hilarious", "memes", "deep fried", "2020", "leafy", "pewdiepie"]; exports.run = async (message, args) => { @@ -8,11 +7,7 @@ exports.run = async (message, args) => { if (image === undefined) return `${message.author.mention}, you need to provide an image/GIF to add a caption!`; const newArgs = args.filter(item => !item.includes(image.url) ); const processMessage = await message.channel.createMessage(" Processing... This might take a while"); - const text = `/tmp/${Math.random().toString(36).substring(2, 15)}.png`; - const size = await gm(image.path).sizePromise(); - await gm().out("-size", `${size.width - ((size.width / 25) * 2)}x`).background("white").fill("black").font("Helvetica Neue").out("-pointsize", size.width / 17).out(`pango:${newArgs.length !== 0 ? newArgs.join(" ") : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" ")}`).gravity("Center").extent(size.width, `%[fx:h+${size.width / 25}]`).writePromise(text); - const size2 = await gm(text).sizePromise(); - const outputFinal = await gm(image.path).coalesce().gravity("North").extent(size.width, size2.height + size.height).out("null:", "(", text, "-set", "page", `+0+${size.height}`, ")", "-layers", "composite", "-layers", "optimize").bufferPromise(image.type, image.delay); + const outputFinal = await promisify(magick.captionTwo)(newArgs.length !== 0 ? newArgs.join(" ") : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" "), image.path, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); await processMessage.delete(); return { file: outputFinal, diff --git a/commands/homebrew.js b/commands/homebrew.js index a09a7cc..27a13d0 100644 --- a/commands/homebrew.js +++ b/commands/homebrew.js @@ -4,7 +4,6 @@ const { promisify } = require("util"); exports.run = async (message, args) => { if (args.length === 0) return `${message.author.mention}, you need to provide some text to make a Homebrew Channel edit!`; message.channel.sendTyping(); - //const buffer = await gm(template).gravity("Center").font("./assets/hbc.ttf").out("-kerning", "-5").fill("white").pointSize(96).drawText(0, 0, cleanedMessage).bufferPromise("png"); const buffer = await promisify(magick.homebrew)(args.join(" ").toLowerCase().replace(/\n/g, " ")); return { file: buffer, diff --git a/commands/meme.js b/commands/meme.js index 60f5fd9..27bf1f0 100644 --- a/commands/meme.js +++ b/commands/meme.js @@ -8,13 +8,6 @@ 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 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 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 pixelate!`; - const buffer = await promisify(magick.resize)(image.path, false, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); + const buffer = await promisify(magick.resize)(image.path, false, false, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `pixelate.${image.type}` diff --git a/commands/scott.js b/commands/scott.js index 6be9ff0..17f34d0 100644 --- a/commands/scott.js +++ b/commands/scott.js @@ -5,8 +5,6 @@ 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 Scott the Woz TV meme!`; - //const template = "./assets/images/scott.png"; - //const buffer = await gm(template).out("null:").out("(").out(image.path).coalesce().out("-virtual-pixel", "transparent").resize("415x234!").out("+distort", "Perspective", "0,0 129,187 415,0 517,182 415,234 517,465 0,234 132,418").out(")").compose("over").gravity("Center").geometry("-238-98").out("-layers", "composite").bufferPromise(image.type, image.delay); const buffer = await promisify(magick.scott)(image.path, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, diff --git a/commands/sonic.js b/commands/sonic.js index 2f1eecb..bbaeb53 100644 --- a/commands/sonic.js +++ b/commands/sonic.js @@ -6,8 +6,6 @@ exports.run = async (message, args) => { if (args.length === 0) return `${message.author.mention}, you need to provide some text to make a Sonic meme!`; message.channel.sendTyping(); const cleanedMessage = args.join(" ").replace(/&/g, "\\&").replace(/>/g, "\\>").replace(/${wrap(cleanedMessage, {width: 15, indent: ""})}`).writePromise(file); - const buffer = await gm(template).composite(file).gravity("Center").geometry("474x332+160+10").bufferPromise("png", null, "sonic");*/ const buffer = await promisify(magick.sonic)(wrap(cleanedMessage, {width: 15, indent: ""})); return { file: buffer, diff --git a/commands/spin.js b/commands/spin.js index df54216..0f4b5f1 100644 --- a/commands/spin.js +++ b/commands/spin.js @@ -5,8 +5,6 @@ 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 spin!`; const processMessage = await message.channel.createMessage(" Processing... This might take a while"); - //const seq = await gm(image.path).coalesce().out("-duplicate", "29").scale("256x256>").scale("256x256<").out("-alpha", "set").virtualPixel("transparent").out("-distort", "SRT", "%[fx:360*t/n]").set("delay", "5").out("-loop", "0").streamPromise("miff"); - //const resultBuffer = await gm(seq).in("-dispose", "background").bufferPromise("gif"); const buffer = await promisify(magick.spin)(image.path, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); await processMessage.delete(); return { diff --git a/commands/stretch.js b/commands/stretch.js index df7749b..53acb50 100644 --- a/commands/stretch.js +++ b/commands/stretch.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 stretch!`; - const buffer = await promisify(magick.resize)(image.path, true, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); + const buffer = await promisify(magick.resize)(image.path, true, false, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `stretch.${image.type}` diff --git a/commands/tile.js b/commands/tile.js index aacaedb..b5fdb71 100644 --- a/commands/tile.js +++ b/commands/tile.js @@ -5,8 +5,6 @@ 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 tile!`; - //const output = await gm(image.path).coalesce().command("montage").out("-duplicate").out(24).tile("5x5").geometry("+0+0").streamPromise("miff"); - //const buffer = await gm(output).coalesce().resize("800x800>").bufferPromise(image.type, image.delay); const buffer = await promisify(magick.tile)(image.path, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, diff --git a/commands/trump.js b/commands/trump.js index 51e8aa7..ec2eaad 100644 --- a/commands/trump.js +++ b/commands/trump.js @@ -5,7 +5,6 @@ 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 Trump meme!`; - //const buffer = await gm(template).background("white").out("null:").out("(").out(image.path).coalesce().out("-virtual-pixel", "transparent").resize("365x179!").out("+distort", "Perspective", "0,0 207,268 365,0 548,271 365,179 558,450 0,179 193,450").out(")").compose("over").gravity("Center").geometry("-217-135").out("-layers", "composite").bufferPromise(image.type, image.delay); const buffer = await promisify(magick.trump)(image.path, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, diff --git a/commands/waaw.js b/commands/waaw.js index 7da1597..278d061 100644 --- a/commands/waaw.js +++ b/commands/waaw.js @@ -1,18 +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 data2 = `/tmp/${Math.random().toString(36).substring(2, 15)}.miff`; - const size = await gm(image.path).sizePromise(); - await gm(image.path).coalesce().gravity("East").crop("50%", 0).out("+repage").writePromise(data2); - await gm(data2).flop().writePromise(data); - // const buffer = await gm(data2).extent("%[fx:u.w*2]", "%[fx:u.h]").out("null:").out(data).gravity("West").out("-layers", "Composite").bufferPromise(image.type, image.delay); - const buffer = await gm(data2).extent(size.width, size.height).out("null:").out(data).geometry(`+${size.width / 2}+0`).out("-layers", "Composite").bufferPromise(image.type, image.delay); + const buffer = await promisify(magick.mirror)(image.path, false, false, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `waaw.${image.type}` diff --git a/commands/wall.js b/commands/wall.js index f06a651..9a4b105 100644 --- a/commands/wall.js +++ b/commands/wall.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 make a wall from!`; - const output = await gm(image.path).coalesce().resize(128).streamPromise("miff"); - const buffer = await gm(output).coalesce().virtualPixel("tile").matteColor("none").out("-background", "none").resize("512x512!").out("-distort").out("Perspective").out("0,0,57,42 0,128,63,130 128,0,140,60 128,128,140,140").bufferPromise(image.type, image.delay); + const buffer = await promisify(magick.wall)(image.path, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `wall.${image.type}` diff --git a/commands/wdt.js b/commands/wdt.js index 285da64..2cba8b0 100644 --- a/commands/wdt.js +++ b/commands/wdt.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 make a "who did this" meme!`; - const template = "./assets/images/whodidthis.png"; - const buffer = await gm(template).out("null:").out("(").out(image.path).coalesce().out(")").gravity("Center").resize("374x374>").out("-layers", "composite").bufferPromise(image.type, image.delay); + const buffer = await promisify(magick.wdt)(image.path, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `wdt.${image.type}` diff --git a/commands/wide.js b/commands/wide.js index 43ad2e5..03e8f39 100644 --- a/commands/wide.js +++ b/commands/wide.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 stretch!`; - const buffer = await gm(image.path).coalesce().resize("%[fx:(u.w*19)/2]x%[fx:u.h/2]!").bufferPromise(image.type, image.delay); + const buffer = await promisify(magick.resize)(image.path, false, true, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `wide.${image.type}` diff --git a/commands/woow.js b/commands/woow.js index 431952f..4d289ae 100644 --- a/commands/woow.js +++ b/commands/woow.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 data2 = `/tmp/${Math.random().toString(36).substring(2, 15)}.miff`; - await gm(image.path).coalesce().gravity("North").crop(0, "50%").out("+repage").writePromise(data2); - await gm(data2).flip().writePromise(data); - const buffer = await gm(data2).extent("%[fx:u.w]", "%[fx:u.h*2]").out("null:").out(data).gravity("South").out("-layers", "Composite").bufferPromise(image.type, image.delay); + const buffer = await promisify(magick.mirror)(image.path, true, true, image.type.toUpperCase(), image.delay ? (100 / image.delay.split("/")[0]) * image.delay.split("/")[1] : 0); return { file: buffer, name: `woow.${image.type}` diff --git a/events/ready.js b/events/ready.js index 07ddc2b..c844e1b 100644 --- a/events/ready.js +++ b/events/ready.js @@ -1,6 +1,4 @@ -const gm = require("gm"); const cron = require("cron"); -const { promisify } = require("util"); const client = require("../utils/client.js"); const database = require("../utils/database.js"); const collections = require("../utils/collections.js"); @@ -17,43 +15,6 @@ let run = false; // run when ready module.exports = async () => { - // add gm extensions - gm.prototype.writePromise = promisify(gm.prototype.write); - gm.prototype.streamPromise = promisify(gm.prototype.stream); - gm.prototype.sizePromise = promisify(gm.prototype.size); - gm.prototype.identifyPromise = promisify(gm.prototype.identify); - gm.prototype.bufferPromise = function(format, delay, type) { - return new Promise((resolve, reject) => { - this.in( - delay ? "-delay" : "", - delay ? delay.split("/").reverse().join("x") : "" - ) - .out( - type !== "sonic" ? "-layers" : "", - type !== "sonic" ? "OptimizeTransparency" : "" - ) - .out(type !== "sonic" ? "-fuzz" : "", type !== "sonic" ? "2%" : "") - .out("+profile", "xmp") - .out("-limit", "memory", "64MB") - .out("-limit", "map", "128MB") - .stream(format, (err, stdout, stderr) => { - if (err) return reject(err); - const chunks = []; - stdout.on("data", (chunk) => { - chunks.push(chunk); - }); - // these are 'once' because they can and do fire multiple times for multiple errors, - // but this is a promise so you'll have to deal with them one at a time - stdout.once("end", () => { - resolve(Buffer.concat(chunks)); - }); - stderr.once("data", (data) => { - reject(data.toString()); - }); - }); - }); - }; - // connect to lavalink if (!soundPlayer.status && !soundPlayer.connected) await soundPlayer.connect(); diff --git a/natives/caption.cc b/natives/caption.cc new file mode 100644 index 0000000..3eb457f --- /dev/null +++ b/natives/caption.cc @@ -0,0 +1,76 @@ +#include +#include +#include + +using namespace std; +using namespace Magick; + +class CaptionWorker : public Napi::AsyncWorker { + public: + CaptionWorker(Napi::Function& callback, string caption, string in_path, string type, int delay) + : Napi::AsyncWorker(callback), caption(caption), in_path(in_path), type(type), delay(delay) {} + ~CaptionWorker() {} + + void Execute() { + list frames; + list coalesced; + list captioned; + list result; + Blob caption_blob; + readImages(&frames, in_path); + + size_t width = frames.front().baseColumns(); + size_t height = frames.front().baseRows(); + string query(to_string(width - ((width / 25) * 2)) + "x"); + Image caption_image(Geometry(query), Color("white")); + caption_image.fillColor("black"); + caption_image.alpha(true); + caption_image.font("./assets/caption.otf"); + caption_image.fontPointsize(width / 10); + caption_image.textGravity(Magick::CenterGravity); + caption_image.read("caption:" + caption); + caption_image.extent(Geometry(width, caption_image.rows() + (width / 10)), Magick::CenterGravity); + + coalesceImages(&coalesced, frames.begin(), frames.end()); + + for (Image &image : coalesced) { + Image appended; + list images; + image.backgroundColor("white"); + images.push_back(caption_image); + images.push_back(image); + appendImages(&appended, images.begin(), images.end(), true); + appended.magick(type); + captioned.push_back(appended); + } + + optimizeImageLayers(&result, captioned.begin(), captioned.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 caption, in_path, type; + int delay, wordlength, i, n; + size_t bytes, type_size; + Blob blob; +}; + +Napi::Value Caption(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + + string caption = info[0].As().Utf8Value(); + string in_path = info[1].As().Utf8Value(); + string type = info[2].As().Utf8Value(); + int delay = info[3].As().Int32Value(); + Napi::Function cb = info[4].As(); + + CaptionWorker* captionWorker = new CaptionWorker(cb, caption, in_path, type, delay); + captionWorker->Queue(); + return env.Undefined(); +} \ No newline at end of file diff --git a/natives/caption.h b/natives/caption.h new file mode 100644 index 0000000..3dd5445 --- /dev/null +++ b/natives/caption.h @@ -0,0 +1,8 @@ +#ifndef ESMBOT_NATIVES_CAPTION_H_ +#define ESMBOT_NATIVES_CAPTION_H_ + +#include + +Napi::Value Caption(const Napi::CallbackInfo& info); + +#endif \ No newline at end of file diff --git a/natives/caption2.cc b/natives/caption2.cc new file mode 100644 index 0000000..e1c23a2 --- /dev/null +++ b/natives/caption2.cc @@ -0,0 +1,75 @@ +#include +#include +#include + +using namespace std; +using namespace Magick; + +class CaptionTwoWorker : public Napi::AsyncWorker { + public: + CaptionTwoWorker(Napi::Function& callback, string caption, string in_path, string type, int delay) + : Napi::AsyncWorker(callback), caption(caption), in_path(in_path), type(type), delay(delay) {} + ~CaptionTwoWorker() {} + + void Execute() { + list frames; + list coalesced; + list captioned; + list result; + Blob caption_blob; + readImages(&frames, in_path); + + size_t width = frames.front().baseColumns(); + size_t height = frames.front().baseRows(); + string query(to_string(width - ((width / 25) * 2)) + "x"); + Image caption_image(Geometry(query), Color("white")); + caption_image.fillColor("black"); + caption_image.font("Helvetica Neue"); + caption_image.fontPointsize(width / 17); + caption_image.read("pango:" + caption); + caption_image.extent(Geometry(width, caption_image.rows() + (width / 25)), Magick::CenterGravity); + + coalesceImages(&coalesced, frames.begin(), frames.end()); + + int iterator = 0; + for (Image &image : coalesced) { + Image appended; + list images; + image.backgroundColor("white"); + images.push_back(image); + images.push_back(caption_image); + appendImages(&appended, images.begin(), images.end(), true); + appended.magick(type); + captioned.push_back(appended); + } + + optimizeImageLayers(&result, captioned.begin(), captioned.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 caption, in_path, type; + int delay, wordlength, i, n; + size_t bytes, type_size; + Blob blob; +}; + +Napi::Value CaptionTwo(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + + string caption = info[0].As().Utf8Value(); + string in_path = info[1].As().Utf8Value(); + string type = info[2].As().Utf8Value(); + int delay = info[3].As().Int32Value(); + Napi::Function cb = info[4].As(); + + CaptionTwoWorker* captionTwoWorker = new CaptionTwoWorker(cb, caption, in_path, type, delay); + captionTwoWorker->Queue(); + return env.Undefined(); +} \ No newline at end of file diff --git a/natives/caption2.h b/natives/caption2.h new file mode 100644 index 0000000..78ada6e --- /dev/null +++ b/natives/caption2.h @@ -0,0 +1,8 @@ +#ifndef ESMBOT_NATIVES_CAPTIONTWO_H_ +#define ESMBOT_NATIVES_CAPTIONTWO_H_ + +#include + +Napi::Value CaptionTwo(const Napi::CallbackInfo& info); + +#endif \ No newline at end of file diff --git a/natives/image.cc b/natives/image.cc index 696f06f..e2ece82 100644 --- a/natives/image.cc +++ b/natives/image.cc @@ -1,8 +1,8 @@ #include #include "blur.h" #include "blurple.h" -//#include "caption.h" -//#include "caption2.h" +#include "caption.h" +#include "caption2.h" #include "circle.h" #include "explode.h" #include "flag.h" @@ -28,14 +28,16 @@ #include "spin.h" #include "tile.h" #include "trump.h" +#include "wall.h" +#include "wdt.h" #include "watermark.h" Napi::Object Init(Napi::Env env, Napi::Object exports) { exports.Set(Napi::String::New(env, "blur"), Napi::Function::New(env, Blur)); exports.Set(Napi::String::New(env, "blurple"), Napi::Function::New(env, Blurple)); - //exports.Set(Napi::String::New(env, "caption"), Napi::Function::New(env, Caption)); - //exports.Set(Napi::String::New(env, "captionTwo"), Napi::Function::New(env, CaptionTwo)); + exports.Set(Napi::String::New(env, "caption"), Napi::Function::New(env, Caption)); + exports.Set(Napi::String::New(env, "captionTwo"), Napi::Function::New(env, CaptionTwo)); exports.Set(Napi::String::New(env, "circle"), Napi::Function::New(env, Circle)); exports.Set(Napi::String::New(env, "explode"), Napi::Function::New(env, Explode)); exports.Set(Napi::String::New(env, "flag"), Napi::Function::New(env, Flag)); @@ -61,6 +63,8 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) exports.Set(Napi::String::New(env, "swirl"), Napi::Function::New(env, Swirl)); exports.Set(Napi::String::New(env, "tile"), Napi::Function::New(env, Tile)); exports.Set(Napi::String::New(env, "trump"), Napi::Function::New(env, Trump)); + exports.Set(Napi::String::New(env, "wall"), Napi::Function::New(env, Wall)); + exports.Set(Napi::String::New(env, "wdt"), Napi::Function::New(env, Wdt)); exports.Set(Napi::String::New(env, "watermark"), Napi::Function::New(env, Watermark)); return exports; } diff --git a/natives/resize.cc b/natives/resize.cc index 1b82409..19aeb0b 100644 --- a/natives/resize.cc +++ b/natives/resize.cc @@ -7,8 +7,8 @@ using namespace Magick; class ResizeWorker : public Napi::AsyncWorker { public: - ResizeWorker(Napi::Function& callback, string in_path, bool stretch, string type, int delay) - : Napi::AsyncWorker(callback), in_path(in_path), stretch(stretch), type(type), delay(delay) {} + ResizeWorker(Napi::Function& callback, string in_path, bool stretch, bool wide, string type, int delay) + : Napi::AsyncWorker(callback), in_path(in_path), stretch(stretch), wide(wide), type(type), delay(delay) {} ~ResizeWorker() {} void Execute() { @@ -22,6 +22,8 @@ class ResizeWorker : public Napi::AsyncWorker { for (Image &image : coalesced) { if (stretch) { image.resize(Geometry("512x512!")); + } else if (wide) { + image.resize(Geometry(to_string((image.baseColumns() * 19) / 2) + "x" + to_string(image.baseRows() / 2) + "!")); } else { image.scale(Geometry("10%")); image.scale(Geometry("1000%")); @@ -44,7 +46,7 @@ class ResizeWorker : public Napi::AsyncWorker { int delay, wordlength, i, n, amount; size_t bytes, type_size; Blob blob; - bool stretch; + bool stretch, wide; }; Napi::Value Resize(const Napi::CallbackInfo &info) @@ -53,11 +55,12 @@ Napi::Value Resize(const Napi::CallbackInfo &info) string in_path = info[0].As().Utf8Value(); bool stretch = info[1].As().Value(); - string type = info[2].As().Utf8Value(); - int delay = info[3].As().Int32Value(); - Napi::Function cb = info[4].As(); + bool wide = info[2].As().Value(); + string type = info[3].As().Utf8Value(); + int delay = info[4].As().Int32Value(); + Napi::Function cb = info[5].As(); - ResizeWorker* explodeWorker = new ResizeWorker(cb, in_path, stretch, type, delay); + ResizeWorker* explodeWorker = new ResizeWorker(cb, in_path, stretch, wide, type, delay); explodeWorker->Queue(); return env.Undefined(); } \ No newline at end of file diff --git a/natives/wall.cc b/natives/wall.cc new file mode 100644 index 0000000..62ff77a --- /dev/null +++ b/natives/wall.cc @@ -0,0 +1,62 @@ +#include +#include +#include + +using namespace std; +using namespace Magick; + +class WallWorker : public Napi::AsyncWorker { + public: + WallWorker(Napi::Function& callback, string in_path, string type, int delay) + : Napi::AsyncWorker(callback), in_path(in_path), type(type), delay(delay) {} + ~WallWorker() {} + + void Execute() { + list frames; + list coalesced; + list mid; + list result; + readImages(&frames, in_path); + coalesceImages(&coalesced, frames.begin(), frames.end()); + + for (Image &image : coalesced) { + image.resize(Geometry("128x128")); + image.virtualPixelMethod(Magick::TileVirtualPixelMethod); + image.matteColor("none"); + image.backgroundColor("none"); + image.scale(Geometry("512x512")); + double arguments[16] = {0, 0, 57, 42, 0, 128, 63, 130, 128, 0, 140, 60, 128, 128, 140, 140}; + image.distort(Magick::PerspectiveDistortion, 16, arguments); + image.scale(Geometry("800x800>")); + mid.push_back(image); + } + + 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; + Blob blob; +}; + +Napi::Value Wall(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(); + + WallWorker* flopWorker = new WallWorker(cb, in_path, type, delay); + flopWorker->Queue(); + return env.Undefined(); +} \ No newline at end of file diff --git a/natives/wall.h b/natives/wall.h new file mode 100644 index 0000000..d856142 --- /dev/null +++ b/natives/wall.h @@ -0,0 +1,8 @@ +#ifndef ESMBOT_NATIVES_WALL_H_ +#define ESMBOT_NATIVES_WALL_H_ + +#include + +Napi::Value Wall(const Napi::CallbackInfo& info); + +#endif \ No newline at end of file diff --git a/natives/wdt.cc b/natives/wdt.cc new file mode 100644 index 0000000..83b7444 --- /dev/null +++ b/natives/wdt.cc @@ -0,0 +1,60 @@ +#include +#include +#include + +using namespace std; +using namespace Magick; + +class WdtWorker : public Napi::AsyncWorker { + public: + WdtWorker(Napi::Function& callback, string in_path, string type, int delay) + : Napi::AsyncWorker(callback), in_path(in_path), type(type), delay(delay) {} + ~WdtWorker() {} + + void Execute() { + list frames; + list coalesced; + list mid; + list result; + Image watermark; + readImages(&frames, in_path); + watermark.read("./assets/images/whodidthis.png"); + coalesceImages(&coalesced, frames.begin(), frames.end()); + + for (Image &image : coalesced) { + Image watermark_new = watermark; + image.scale(Geometry("374x374>")); + watermark_new.composite(image, Magick::CenterGravity, Magick::OverCompositeOp); + watermark_new.magick(type); + mid.push_back(watermark_new); + } + + 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; + Blob blob; +}; + +Napi::Value Wdt(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(); + + WdtWorker* blurWorker = new WdtWorker(cb, in_path, type, delay); + blurWorker->Queue(); + return env.Undefined(); +} \ No newline at end of file diff --git a/natives/wdt.h b/natives/wdt.h new file mode 100644 index 0000000..4675f2f --- /dev/null +++ b/natives/wdt.h @@ -0,0 +1,8 @@ +#ifndef ESMBOT_NATIVES_WDT_H_ +#define ESMBOT_NATIVES_WDT_H_ + +#include + +Napi::Value Wdt(const Napi::CallbackInfo& info); + +#endif \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 52300f8..adfad33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -152,16 +152,6 @@ "sprintf-js": "~1.0.2" } }, - "array-parallel": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz", - "integrity": "sha1-j3hTCJJu1apHjEfmTRszS2wMlH0=" - }, - "array-series": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz", - "integrity": "sha1-3103v8XC7wdV4qpPkv6ufUtaly8=" - }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -559,15 +549,6 @@ "moment-timezone": "^0.5.x" } }, - "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, "css-select": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", @@ -1185,26 +1166,6 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, - "gm": { - "version": "github:TheEssem/gm#6c8e444106b20d868e3bcdd6791cbc7c90ae5cab", - "from": "github:TheEssem/gm", - "requires": { - "array-parallel": "~0.1.3", - "array-series": "~0.1.5", - "cross-spawn": "^4.0.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1392,7 +1353,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "js-tokens": { "version": "4.0.0", @@ -1489,15 +1451,6 @@ "triple-beam": "^1.3.0" } }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, "mime-db": { "version": "1.42.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", @@ -2004,11 +1957,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=" }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -2602,6 +2550,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -2737,11 +2686,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - }, "yargs": { "version": "13.3.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", diff --git a/package.json b/package.json index 21ad0f2..1c9ac94 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "emoji-regex": "^8.0.0", "eris": "^0.13.3", "file-type": "^13.1.2", - "gm": "github:TheEssem/gm", "jsqr": "^1.3.1", "lavacord": "^1.1.9", "moment": "^2.27.0", diff --git a/utils/gmbuffer.js b/utils/gmbuffer.js deleted file mode 100644 index 597f052..0000000 --- a/utils/gmbuffer.js +++ /dev/null @@ -1,39 +0,0 @@ -// workaround for a gm bug where it doesn't output buffers properly -// https://github.com/aheckmann/gm/issues/572#issuecomment-293768810 -module.exports = (data, format, type) => { - return new Promise((resolve, reject) => { - if (format) { - data.out(type !== "sonic" ? "-layers" : "", type !=="sonic" ? "optimize" : "").stream(format, (err, stdout, stderr) => { - if (err) return reject(err); - const chunks = []; - stdout.on("data", (chunk) => { - chunks.push(chunk); - }); - // these are 'once' because they can and do fire multiple times for multiple errors, - // but this is a promise so you'll have to deal with them one at a time - stdout.once("end", () => { - resolve(Buffer.concat(chunks)); - }); - stderr.once("data", (data) => { - reject(data.toString()); - }); - }); - } else { - data.out("-layers", "optimize").stream((err, stdout, stderr) => { - if (err) return reject(err); - const chunks = []; - stdout.on("data", (chunk) => { - chunks.push(chunk); - }); - // these are 'once' because they can and do fire multiple times for multiple errors, - // but this is a promise so you'll have to deal with them one at a time - stdout.once("end", () => { - resolve(Buffer.concat(chunks)); - }); - stderr.once("data", (data) => { - reject(data.toString()); - }); - }); - } - }); -};