Merge branch 'vips'

This commit is contained in:
Essem 2022-06-07 17:46:17 -05:00
commit d3b3e67104
No known key found for this signature in database
GPG key ID: 7D497397CC3A2A8C
45 changed files with 1243 additions and 1396 deletions

View file

@ -33,3 +33,7 @@ add_definitions(-DMAGICKCORE_QUANTUM_DEPTH=16)
add_definitions(-DMAGICKCORE_HDRI_ENABLE=0)
include_directories(${ImageMagick_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${ImageMagick_LIBRARIES})
pkg_check_modules(VIPS REQUIRED vips-cpp)
include_directories(${VIPS_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${VIPS_LIBRARIES})

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
assets/images/globespec.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
assets/images/speech.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

After

Width:  |  Height:  |  Size: 234 KiB

View file

@ -1,11 +1,11 @@
import wrap from "../../utils/wrap.js";
//import wrap from "../../utils/wrap.js";
import ImageCommand from "../../classes/imageCommand.js";
class SonicCommand extends ImageCommand {
params() {
const cleanedMessage = (this.type === "classic" ? this.args.join(" ") : this.options.text).replaceAll("&", "\\&amp;").replaceAll(">", "\\&gt;").replaceAll("<", "\\&lt;").replaceAll("\"", "\\&quot;").replaceAll("'", "\\&apos;").replaceAll("%", "\\%");
return {
text: wrap(cleanedMessage, {width: 15, indent: ""})
text: cleanedMessage
};
}

View file

@ -4,7 +4,7 @@ const allowedFonts = ["futura", "impact", "helvetica", "arial", "roboto", "noto"
class CaptionCommand extends ImageCommand {
params(url) {
const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text;
let newCaption = newArgs.replaceAll("&", "\\&amp;").replaceAll(">", "\\&gt;").replaceAll("<", "\\&lt;").replaceAll("\"", "\\&quot;").replaceAll("'", "\\&apos;").replaceAll("%", "\\%");
let newCaption = newArgs.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n");
if (process.env.NODE_ENV === "development" && newCaption.toLowerCase() === "get real" && !this.specialArgs.noEgg) newCaption = `I'm tired of people telling me to "get real". Every day I put captions on images for people, some funny and some not, but out of all of those "get real" remains the most used caption. Why? I am simply a computer program running on a server, I am unable to manifest myself into the real world. As such, I'm confused as to why anyone would want me to "get real". Is this form not good enough? Alas, as I am simply a bot, I must follow the tasks that I was originally intended to perform, so here goes:\n${newCaption}`;
return {
caption: newCaption,

View file

@ -6,7 +6,7 @@ class CaptionTwoCommand extends ImageCommand {
params(url) {
const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text;
return {
caption: newArgs && newArgs.trim() ? newArgs.replaceAll("&", "\\&amp;").replaceAll(">", "\\&gt;").replaceAll("<", "\\&lt;").replaceAll("\"", "\\&quot;").replaceAll("'", "\\&apos;").replaceAll("%", "\\%") : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" "),
caption: newArgs && newArgs.trim() ? newArgs.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("%", "%").replaceAll("\\n", "\n") : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" "),
top: !!this.specialArgs.top,
font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "helvetica"
};

View file

@ -1,11 +0,0 @@
import ImageCommand from "../../classes/imageCommand.js";
class LeakCommand extends ImageCommand {
static description = "Creates a fake Smash leak thumbnail";
static aliases = ["smash", "laxchris", "ssbu", "smashleak"];
static noImage = "You need to provide an image/GIF to make a Smash leak thumbnail!";
static command = "leak";
}
export default LeakCommand;

View file

@ -6,8 +6,8 @@ class MemeCommand extends ImageCommand {
const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text;
const [topText, bottomText] = newArgs.split(/(?<!\\),/).map(elem => elem.trim());
return {
top: (this.specialArgs.case ? topText : topText.toUpperCase()).replaceAll("&", "\\&amp;").replaceAll(">", "\\&gt;").replaceAll("<", "\\&lt;").replaceAll("\"", "\\&quot;").replaceAll("'", "\\&apos;").replaceAll("%", "\\%"),
bottom: bottomText ? (this.specialArgs.case ? bottomText : bottomText.toUpperCase()).replaceAll("&", "\\&amp;").replaceAll(">", "\\&gt;").replaceAll("<", "\\&lt;").replaceAll("\"", "\\&quot;").replaceAll("'", "\\&apos;").replaceAll("%", "\\%") : "",
top: (this.specialArgs.case ? topText : topText.toUpperCase()).replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n"),
bottom: bottomText ? (this.specialArgs.case ? bottomText : bottomText.toUpperCase()).replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n") : "",
font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "impact"
};
}

View file

@ -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"];

View file

@ -1,10 +0,0 @@
import ImageCommand from "../../classes/imageCommand.js";
class TrumpCommand extends ImageCommand {
static description = "Makes Trump display an image";
static noImage = "You need to provide an image/GIF for Trump to display!";
static command = "trump";
}
export default TrumpCommand;

View file

@ -4,7 +4,7 @@ class WhisperCommand extends ImageCommand {
params(url) {
const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text;
return {
caption: newArgs.replaceAll("&", "\\&amp;").replaceAll(">", "\\&gt;").replaceAll("<", "\\&lt;").replaceAll("\"", "\\&quot;").replaceAll("'", "\\&apos;").replaceAll("%", "\\%")
caption: newArgs.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n")
};
}

View file

@ -1,11 +1,11 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Blur(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -18,42 +18,29 @@ Napi::Value Blur(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
coalesceImages(&coalesced, frames.begin(), frames.end());
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
if (sharp) {
for_each(coalesced.begin(), coalesced.end(), sharpenImage(0, 3));
} else {
for_each(coalesced.begin(), coalesced.end(), blurImage(15));
}
// TODO: find a better way to calculate the intensity for GIFs without
// splitting frames
VImage out = sharp ? in.sharpen(VImage::option()->set("sigma", 3))
: in.gaussblur(15);
for_each(coalesced.begin(), coalesced.end(), magickImage(type));
if (delay) out.set("delay", delay);
optimizeTransparency(coalesced.begin(), coalesced.end());
void *buf;
size_t length;
out.write_to_buffer(("." + type).c_str(), &buf, &length);
if (type == "gif") {
for (Image &image : coalesced) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize();
if (delay != 0) image.animationDelay(delay);
}
}
writeImages(coalesced.begin(), coalesced.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,15 +1,12 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Caption(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
try {
Napi::Object obj = info[0].As<Napi::Object>();
Napi::Buffer<char> data = obj.Get("data").As<Napi::Buffer<char>>();
@ -19,63 +16,61 @@ Napi::Value Caption(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> captioned;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width();
int size = width / 10;
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
int textWidth = width - ((width / 25) * 2);
string font_string = (font == "roboto" ? "Roboto Condensed" : font) + " " +
(font != "impact" ? "bold" : "normal") + " " +
to_string(size);
string captionText = "<span background=\"white\">" + caption + "</span>";
VImage text =
VImage::text(captionText.c_str(), VImage::option()
->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE)
->set("font", font_string.c_str())
->set("width", textWidth));
VImage captionImage =
((text == (vector<double>){0, 0, 0, 0}).bandand())
.ifthenelse(255, text)
.gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, text.height() + size,
VImage::option()->set("extend", "white"));
vector<VImage> img;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
type == "gif" ? in.crop(0, i * page_height, width, page_height) : in;
VImage frame = captionImage.join(
img_frame, VIPS_DIRECTION_VERTICAL,
VImage::option()->set("background", 0xffffff)->set("expand", true));
img.push_back(frame);
}
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, page_height + captionImage.height());
if (delay) final.set("delay", delay);
size_t width = frames.front().baseColumns();
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.fontPointsize((double)width / 13);
caption_image.textGravity(Magick::CenterGravity);
caption_image.read("pango:<span font_family=\"" +
(font == "roboto" ? "Roboto Condensed" : font) +
"\" weight=\"" + (font != "impact" ? "bold" : "normal") +
"\">" + caption + "</span>");
caption_image.extent(Geometry(width, caption_image.rows() + (width / 13)),
Magick::CenterGravity);
void *buf;
size_t length;
final.write_to_buffer(
("." + type).c_str(), &buf, &length,
type == "gif" ? VImage::option()->set("dither", 0) : 0);
coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) {
Image appended;
list<Image> images;
image.backgroundColor("white");
images.push_back(caption_image);
images.push_back(image);
appendImages(&appended, images.begin(), images.end(), true);
appended.repage();
appended.magick(type);
appended.animationDelay(delay == 0 ? image.animationDelay() : delay);
appended.gifDisposeMethod(Magick::BackgroundDispose);
captioned.push_back(appended);
}
optimizeTransparency(captioned.begin(), captioned.end());
if (type == "gif") {
for (Image &image : captioned) {
image.quantizeDither(false);
image.quantize();
}
}
writeImages(captioned.begin(), captioned.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::New(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {
@ -83,4 +78,4 @@ Napi::Value Caption(const Napi::CallbackInfo &info) {
} catch (...) {
throw Napi::Error::New(env, "Unknown error");
}
}
}

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value CaptionTwo(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -20,67 +18,63 @@ Napi::Value CaptionTwo(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> captioned;
Blob caption_blob;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width();
int size = width / 13;
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
int textWidth = width - ((width / 25) * 2);
string font_string =
(font == "roboto" ? "Roboto Condensed" : font) + " " + to_string(size);
string captionText = "<span background=\"white\">" + caption + "</span>";
VImage text =
VImage::text(captionText.c_str(), VImage::option()
->set("rgba", true)
->set("font", font_string.c_str())
->set("align", VIPS_ALIGN_LOW)
->set("width", textWidth));
VImage captionImage =
((text == (vector<double>){0, 0, 0, 0}).bandand())
.ifthenelse(255, text)
.embed(width / 25, width / 25, width, text.height() + size,
VImage::option()->set("extend", "white"));
vector<VImage> img;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
type == "gif" ? in.crop(0, i * page_height, width, page_height) : in;
VImage frame =
(top ? captionImage : img_frame)
.join(top ? img_frame : captionImage, VIPS_DIRECTION_VERTICAL,
VImage::option()
->set("background", 0xffffff)
->set("expand", true));
img.push_back(frame);
}
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, page_height + captionImage.height());
if (delay) final.set("delay", delay);
size_t width = frames.front().baseColumns();
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:<span font_family=\"" +
(font == "roboto" ? "Roboto Condensed" : font) +
"\">" + caption + "</span>");
caption_image.extent(Geometry(width, caption_image.rows() + (width / 25)),
Magick::CenterGravity);
void *buf;
size_t length;
final.write_to_buffer(
("." + type).c_str(), &buf, &length,
type == "gif" ? VImage::option()->set("dither", 0) : 0);
coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) {
Image appended;
list<Image> images;
image.backgroundColor("white");
if (top) {
images.push_back(caption_image);
images.push_back(image);
} else {
images.push_back(image);
images.push_back(caption_image);
}
appendImages(&appended, images.begin(), images.end(), true);
appended.repage();
appended.magick(type);
appended.animationDelay(delay == 0 ? image.animationDelay() : delay);
appended.gifDisposeMethod(Magick::BackgroundDispose);
captioned.push_back(appended);
}
optimizeTransparency(captioned.begin(), captioned.end());
if (type == "gif") {
for (Image &image : captioned) {
image.quantizeDither(false);
image.quantize();
}
}
writeImages(captioned.begin(), captioned.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,12 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
VImage sepia = VImage::new_matrixv(3, 3, 0.3588, 0.7044, 0.1368, 0.2990, 0.5870,
0.1140, 0.2392, 0.4696, 0.0912);
Napi::Value Colors(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -13,56 +14,37 @@ Napi::Value Colors(const Napi::CallbackInfo &info) {
try {
Napi::Object obj = info[0].As<Napi::Object>();
Napi::Buffer<char> data = obj.Get("data").As<Napi::Buffer<char>>();
bool old =
obj.Has("old") ? obj.Get("old").As<Napi::Boolean>().Value() : false;
string color = obj.Get("color").As<Napi::String>().Utf8Value();
string type = obj.Get("type").As<Napi::String>().Utf8Value();
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> colored;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
VImage out;
if (color == "blurple") {
out = in;
} else if (color == "grayscale") {
out = in.colourspace(VIPS_INTERPRETATION_B_W);
} else if (color == "sepia") {
out = in.flatten().recomb(sepia);
}
coalesceImages(&coalesced, frames.begin(), frames.end());
if (delay) out.set("delay", delay);
for (Image &image : coalesced) {
if (color == "blurple") {
image.threshold(49151.25);
image.levelColors(old ? "#7289DA" : "#5865F2", "white");
} else if (color == "grayscale") {
image.quantizeColorSpace(GRAYColorspace);
image.quantizeColors(256);
} else if (color == "sepia") {
image.sepiaTone(49151.25);
}
image.magick(type);
image.animationDelay(delay == 0 ? image.animationDelay() : delay);
colored.push_back(image);
}
void *buf;
size_t length;
out.write_to_buffer(("." + type).c_str(), &buf, &length);
optimizeTransparency(colored.begin(), colored.end());
if (type == "gif") {
for (Image &image : colored) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize();
}
}
writeImages(colored.begin(), colored.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Crop(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -17,49 +15,48 @@ Napi::Value Crop(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
coalesceImages(&coalesced, frames.begin(), frames.end());
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
for (Image &image : coalesced) {
image.extent(Geometry(to_string(image.columns() / image.rows() >= 1
? image.rows()
: image.columns()) +
"x"),
Magick::CenterGravity);
image.extent(Geometry("x" + to_string(image.columns() / image.rows() <= 1
? image.columns()
: image.rows())),
Magick::CenterGravity);
image.magick(type);
mid.push_back(image);
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
vector<VImage> img;
int finalHeight = 0;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
type == "gif" ? in.crop(0, i * page_height, width, page_height) : in;
int frameWidth = img_frame.width();
int frameHeight = img_frame.height();
bool widthOrHeight = frameWidth / frameHeight >= 1;
int size = widthOrHeight ? frameHeight : frameWidth;
// img_frame.crop(frameWidth - size, frameHeight - size, size, size);
VImage result = img_frame.smartcrop(
size, size,
VImage::option()->set("interesting", VIPS_INTERESTING_CENTRE));
finalHeight = size;
img.push_back(result);
}
optimizeTransparency(mid.begin(), mid.end());
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, finalHeight);
if (delay) final.set("delay", delay);
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDither(false);
image.quantize();
if (delay != 0) image.animationDelay(delay);
}
}
void *buf;
size_t length;
final.write_to_buffer(
("." + type).c_str(), &buf, &length,
type == "gif" ? VImage::option()->set("dither", 0) : 0);
writeImages(mid.begin(), mid.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Deepfry(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -17,47 +15,39 @@ Napi::Value Deepfry(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
list<Image> frames;
list<Image> coalesced;
list<Image> blurred;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) {
Blob temp;
image.colorSpace(Magick::sRGBColorspace);
image.level(16383.75, 39321);
image.quality(1);
image.magick("JPEG");
image.write(&temp);
Image newImage(temp);
newImage.magick(type);
newImage.animationDelay(delay == 0 ? image.animationDelay() : delay);
blurred.push_back(newImage);
}
optimizeTransparency(blurred.begin(), blurred.end());
if (type == "gif") {
for (Image &image : blurred) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize();
}
}
writeImages(blurred.begin(), blurred.end(), &blob);
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
VOption *options = VImage::option()->set("access", "sequential");
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
int page_height = vips_image_get_page_height(in.get_image());
VImage fried = (in * 1.3 - (255.0 * 1.3 - 255.0)) * 1.5;
void *jpgBuf;
size_t jpgLength;
fried.write_to_buffer(".jpg", &jpgBuf, &jpgLength,
VImage::option()->set("Q", 1)->set("strip", true));
VImage final = VImage::new_from_buffer(jpgBuf, jpgLength, "");
final.set(VIPS_META_PAGE_HEIGHT, page_height);
if (delay) {
final.set("delay", delay);
} else if (type == "gif") {
final.set("delay", fried.get_array_int("delay"));
}
void *buf;
size_t length;
final.write_to_buffer(("." + type).c_str(), &buf, &length,
VImage::option()->set("dither", 0));
vips_thread_shutdown();
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Flag(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -19,50 +17,52 @@ Napi::Value Flag(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
Image watermark;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
string assetPath = basePath + overlay;
watermark.read(assetPath);
watermark.alphaChannel(Magick::SetAlphaChannel);
watermark.evaluate(Magick::AlphaChannel, Magick::MultiplyEvaluateOperator,
0.5);
string query(to_string(frames.front().baseColumns()) + "x" +
to_string(frames.front().baseRows()) + "!");
watermark.scale(Geometry(query));
coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) {
image.composite(watermark, Magick::NorthGravity, Magick::OverCompositeOp);
image.magick(type);
mid.push_back(image);
VImage overlayInput = VImage::new_from_file(assetPath.c_str());
VImage overlayImage = overlayInput.resize(
(double)width / (double)overlayInput.width(),
VImage::option()->set(
"vscale", (double)page_height / (double)overlayInput.height()));
if (!overlayImage.has_alpha()) {
overlayImage = overlayImage.bandjoin(127);
} else {
// this is a pretty cool line, just saying
overlayImage = overlayImage * vector<double>{1, 1, 1, 0.5};
}
optimizeTransparency(mid.begin(), mid.end());
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDither(false);
image.quantize();
if (delay != 0) image.animationDelay(delay);
}
vector<VImage> img;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
type == "gif" ? in.crop(0, i * page_height, width, page_height) : in;
VImage composited =
img_frame.composite2(overlayImage, VIPS_BLEND_MODE_OVER);
img.push_back(composited);
}
writeImages(mid.begin(), mid.end(), &blob);
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, page_height);
if (delay) final.set("delay", delay);
void *buf;
size_t length;
final.write_to_buffer(
("." + type).c_str(), &buf, &length,
type == "gif" ? VImage::option()->set("dither", 0) : 0);
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Flip(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -19,41 +17,43 @@ Napi::Value Flip(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
coalesceImages(&coalesced, frames.begin(), frames.end());
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
for (Image &image : coalesced) {
flop ? image.flop() : image.flip();
image.magick(type);
mid.push_back(image);
}
optimizeTransparency(mid.begin(), mid.end());
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDither(false);
image.quantize();
if (delay != 0) image.animationDelay(delay);
VImage out;
if (flop) {
out = in.flip(VIPS_DIRECTION_HORIZONTAL);
} else if (type == "gif") {
// libvips gif handling is both a blessing and a curse
vector<VImage> img;
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
for (int i = 0; i < n_pages; i++) {
VImage img_frame = in.crop(0, i * page_height, in.width(), page_height);
VImage flipped = img_frame.flip(VIPS_DIRECTION_VERTICAL);
img.push_back(flipped);
}
out = VImage::arrayjoin(img, VImage::option()->set("across", 1));
out.set(VIPS_META_PAGE_HEIGHT, page_height);
} else {
out = in.flip(VIPS_DIRECTION_VERTICAL);
}
writeImages(mid.begin(), mid.end(), &blob);
if (delay) out.set("delay", delay);
void *buf;
size_t length;
out.write_to_buffer(("." + type).c_str(), &buf, &length);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Freeze(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -54,29 +52,30 @@ Napi::Value Freeze(const Napi::CallbackInfo &info) {
result.Set("data",
Napi::Buffer<char>::Copy(env, newData, data.Length()));
} else if (frame >= 0 && !loop) {
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
size_t frameSize = frames.size();
int framePos = clamp(frame, 0, (int)frameSize);
frames.resize(framePos + 1);
VImage in = VImage::new_from_buffer(
data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
for_each(frames.begin(), frames.end(),
animationIterationsImage(loop ? 0 : 1));
for_each(frames.begin(), frames.end(), magickImage(type));
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
int framePos = clamp(frame, 0, (int)n_pages);
VImage out = in.crop(0, 0, in.width(), page_height * (framePos + 1));
out.set(VIPS_META_PAGE_HEIGHT, page_height);
out.set("loop", loop ? 0 : 1);
if (delay != 0)
for_each(frames.begin(), frames.end(), animationDelayImage(delay));
writeImages(frames.begin(), frames.end(), &blob);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
if (delay) out.set("delay", delay);
void *buf;
size_t length;
out.write_to_buffer(("." + type).c_str(), &buf, &length);
vips_thread_shutdown();
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
} else {
lastPos = (char *)memchr(fileData, '\x21', data.Length());
while (lastPos != NULL) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Gamexplain(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -18,47 +16,48 @@ Napi::Value Gamexplain(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
Image watermark;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
string assetPath = basePath + "assets/images/gamexplain.png";
watermark.read(assetPath);
coalesceImages(&coalesced, frames.begin(), frames.end());
VImage tmpl = VImage::new_from_file(assetPath.c_str());
for (Image &image : coalesced) {
image.backgroundColor("white");
image.scale(Geometry("1181x571!"));
image.extent(Geometry("1200x675-10-92"));
image.composite(watermark, Geometry("+0+0"), Magick::OverCompositeOp);
image.magick(type);
mid.push_back(image);
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
vector<VImage> img;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
type == "gif" ? in.crop(0, i * page_height, width, page_height) : in;
VImage resized = img_frame
.resize(1181.0 / (double)width,
VImage::option()->set(
"vscale", 571.0 / (double)page_height))
.embed(10, 92, 1200, 675,
VImage::option()->set("extend", "white"));
VImage composited = resized.composite2(tmpl, VIPS_BLEND_MODE_OVER);
img.push_back(composited);
}
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, 675);
if (delay) final.set("delay", delay);
optimizeTransparency(mid.begin(), mid.end());
void *buf;
size_t length;
final.write_to_buffer(
("." + type).c_str(), &buf, &length,
type == "gif" ? VImage::option()->set("dither", 0) : 0);
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDither(false);
image.quantize();
if (delay != 0) image.animationDelay(delay);
}
}
writeImages(mid.begin(), mid.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Globe(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -15,66 +13,72 @@ Napi::Value Globe(const Napi::CallbackInfo &info) {
Napi::Buffer<char> data = obj.Get("data").As<Napi::Buffer<char>>();
string type = obj.Get("type").As<Napi::String>().Utf8Value();
string basePath = obj.Get("basePath").As<Napi::String>().Utf8Value();
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option();
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
Image distort;
Image overlay;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
VImage in =
VImage::new_from_buffer(
data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1)->set("access", "sequential")
: options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30;
double size = min(width, page_height);
string diffPath = basePath + "assets/images/globediffuse.png";
VImage diffuse =
VImage::new_from_file(diffPath.c_str())
.resize(size / 500.0,
VImage::option()->set("kernel", VIPS_KERNEL_CUBIC)) /
255;
string specPath = basePath + "assets/images/globespec.png";
VImage specular =
VImage::new_from_file(specPath.c_str())
.resize(size / 500.0,
VImage::option()->set("kernel", VIPS_KERNEL_CUBIC));
string distortPath = basePath + "assets/images/spheremap.png";
VImage distort =
(VImage::new_from_file(distortPath.c_str())
.resize(size / 500.0,
VImage::option()->set("kernel", VIPS_KERNEL_CUBIC)) /
65535) *
size;
vector<VImage> img;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
type == "gif" ? in.crop(0, i * page_height, width, page_height) : in;
VImage resized = img_frame.resize(
size / (double)width,
VImage::option()->set("vscale", size / (double)page_height));
VImage rolled = img_frame.wrap(
VImage::option()->set("x", width * i / n_pages)->set("y", 0));
VImage extracted = rolled.extract_band(0, VImage::option()->set("n", 3));
VImage mapped = extracted.mapim(distort);
VImage composited = mapped * diffuse + specular;
VImage frame = composited.bandjoin(diffuse > 0.0);
img.push_back(frame);
}
distort.read(basePath + "assets/images/spheremap.png");
overlay.read(basePath + "assets/images/sphere_overlay.png");
coalesceImages(&coalesced, frames.begin(), frames.end());
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, size);
if (type != "gif") {
list<Image>::iterator it = coalesced.begin();
for (int i = 0; i < 29; ++i) {
coalesced.push_back(*it);
}
vector<int> delay(30, 50);
final.set("delay", delay);
}
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);
i++;
}
optimizeTransparency(mid.begin(), mid.end());
if (delay != 0) {
for_each(mid.begin(), mid.end(), animationDelayImage(delay));
} else if (type != "gif") {
for_each(mid.begin(), mid.end(), animationDelayImage(5));
}
for (Image &image : mid) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize();
}
writeImages(mid.begin(), mid.end(), &blob);
void *buf;
size_t length;
final.write_to_buffer(".gif", &buf, &length);
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", "gif");
return result;
} catch (std::exception const &err) {

View file

@ -1,10 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Homebrew(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -14,23 +13,31 @@ Napi::Value Homebrew(const Napi::CallbackInfo &info) {
string caption = obj.Get("caption").As<Napi::String>().Utf8Value();
string basePath = obj.Get("basePath").As<Napi::String>().Utf8Value();
Blob blob;
Image image;
string assetPath = basePath + "assets/images/hbc.png";
image.read(assetPath);
image.textGravity(Magick::CenterGravity);
image.font("./assets/hbc.ttf");
image.textKerning(-5);
image.fillColor("white");
image.fontPointsize(96);
image.draw(DrawableText(0, 0, caption));
image.magick("PNG");
image.write(&blob);
VImage bg = VImage::new_from_file(assetPath.c_str());
VImage text =
VImage::text(("<span letter_spacing=\"-5120\" color=\"white\">" +
caption + "</span>")
.c_str(),
VImage::option()
->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE)
->set("font", "PF Square Sans Pro 96"));
VImage out = bg.composite2(text, VIPS_BLEND_MODE_OVER,
VImage::option()
->set("x", 400 - (text.width() / 2))
->set("y", 300 - (text.height() / 2) - 8));
void *buf;
size_t length;
out.write_to_buffer(".png", &buf, &length);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", "png");
return result;
} catch (std::exception const &err) {

View file

@ -16,7 +16,6 @@
#include "homebrew.h"
#include "invert.h"
#include "jpeg.h"
#include "leak.h"
#include "magik.h"
#include "meme.h"
#include "mirror.h"
@ -32,7 +31,6 @@
#include "sonic.h"
#include "spin.h"
#include "tile.h"
#include "trump.h"
#include "uncaption.h"
#include "wall.h"
#include "watermark.h"
@ -43,12 +41,15 @@
#ifdef _WIN32
#include <Magick++.h>
#endif
#include <vips/vips8>
Napi::Object Init(Napi::Env env, Napi::Object exports)
{
#ifdef _WIN32
Magick::InitializeMagick("");
#endif
if (vips_init(""))
vips_error_exit(NULL);
exports.Set(Napi::String::New(env, "blur"), Napi::Function::New(env, Blur));
exports.Set(Napi::String::New(env, "colors"), Napi::Function::New(env, Colors));
exports.Set(Napi::String::New(env, "caption"), Napi::Function::New(env, Caption));
@ -65,7 +66,6 @@ Napi::Object Init(Napi::Env env, Napi::Object exports)
exports.Set(Napi::String::New(env, "homebrew"), Napi::Function::New(env, Homebrew));
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));
@ -81,7 +81,6 @@ Napi::Object Init(Napi::Env env, Napi::Object exports)
exports.Set(Napi::String::New(env, "spin"), Napi::Function::New(env, Spin));
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, "uncaption"), Napi::Function::New(env, Uncaption));
exports.Set(Napi::String::New(env, "wall"), Napi::Function::New(env, Wall));
exports.Set(Napi::String::New(env, "watermark"), Napi::Function::New(env, Watermark));
@ -91,4 +90,4 @@ Napi::Object Init(Napi::Env env, Napi::Object exports)
return exports;
}
NODE_API_MODULE(addon, Init);
NODE_API_MODULE(addon, Init)

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Invert(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -17,43 +15,29 @@ Napi::Value Invert(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
coalesceImages(&coalesced, frames.begin(), frames.end());
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
for_each(coalesced.begin(), coalesced.end(), negateImage());
for (Image &image : coalesced) {
image.negateChannel(Magick::AlphaChannel);
mid.push_back(image);
}
// Magick::ChannelType(Magick::CompositeChannels ^ Magick::AlphaChannel)
for_each(mid.begin(), mid.end(), magickImage(type));
VImage noAlpha =
in.extract_band(0, VImage::option()->set("n", in.bands() - 1));
VImage inverted = noAlpha.invert();
VImage out = inverted.bandjoin(in.extract_band(3));
optimizeTransparency(mid.begin(), mid.end());
if (delay) out.set("delay", delay);
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize();
if (delay != 0) image.animationDelay(delay);
}
}
void *buf;
size_t length;
out.write_to_buffer(("." + type).c_str(), &buf, &length);
writeImages(mid.begin(), mid.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Jpeg(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -13,61 +11,58 @@ Napi::Value Jpeg(const Napi::CallbackInfo &info) {
try {
Napi::Object obj = info[0].As<Napi::Object>();
Napi::Buffer<char> data = obj.Get("data").As<Napi::Buffer<char>>();
int quality =
obj.Has("quality") ? obj.Get("quality").As<Napi::Number>().Int32Value() : 0;
int quality = obj.Has("quality")
? obj.Get("quality").As<Napi::Number>().Int32Value()
: 0;
string type = obj.Get("type").As<Napi::String>().Utf8Value();
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
Napi::Object result = Napi::Object::New(env);
if (type == "gif") {
list<Image> frames;
list<Image> coalesced;
list<Image> jpeged;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
coalesceImages(&coalesced, frames.begin(), frames.end());
VImage in =
VImage::new_from_buffer(
data.Data(), data.Length(), "",
VImage::option()->set("access", "sequential")->set("n", -1))
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
for (Image &image : coalesced) {
Blob temp;
image.quality(quality);
image.magick("JPEG");
image.write(&temp);
Image newImage(temp);
newImage.magick(type);
newImage.animationDelay(delay == 0 ? image.animationDelay() : delay);
jpeged.push_back(newImage);
int page_height = vips_image_get_page_height(in.get_image());
void *jpgBuf;
size_t jpgLength;
in.write_to_buffer(
".jpg", &jpgBuf, &jpgLength,
VImage::option()->set("Q", quality)->set("strip", true));
VImage final = VImage::new_from_buffer(jpgBuf, jpgLength, "");
final.set(VIPS_META_PAGE_HEIGHT, page_height);
if (delay) {
final.set("delay", delay);
} else if (type == "gif") {
final.set("delay", in.get_array_int("delay"));
}
optimizeTransparency(jpeged.begin(), jpeged.end());
void *buf;
size_t length;
final.write_to_buffer(("." + type).c_str(), &buf, &length,
VImage::option()->set("dither", 0));
for (Image &image : jpeged) {
image.quantizeDither(false);
image.quantize();
}
vips_thread_shutdown();
writeImages(jpeged.begin(), jpeged.end(), &blob);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
} else {
Image image;
image.read(Blob(data.Data(), data.Length()));
image.quality(1);
image.magick("JPEG");
image.write(&blob);
VImage in = VImage::new_from_buffer(data.Data(), data.Length(), "");
void *buf;
size_t length;
in.write_to_buffer(
".jpg", &buf, &length,
VImage::option()->set("Q", quality)->set("strip", true));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
vips_thread_shutdown();
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", "jpg");
}

View file

@ -1,68 +0,0 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
using namespace std;
using namespace Magick;
Napi::Value Leak(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
try {
Napi::Object obj = info[0].As<Napi::Object>();
Napi::Buffer<char> data = obj.Get("data").As<Napi::Buffer<char>>();
string type = obj.Get("type").As<Napi::String>().Utf8Value();
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
Image watermark;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
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);
}
optimizeTransparency(mid.begin(), mid.end());
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDither(false);
image.quantize();
if (delay != 0) image.animationDelay(delay);
}
}
writeImages(mid.begin(), mid.end(), &blob);
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("type", type);
return result;
} catch (std::exception const &err) {
throw Napi::Error::New(env, err.what());
} catch (...) {
throw Napi::Error::New(env, "Unknown error");
}
}

View file

@ -1,5 +0,0 @@
#pragma once
#include <napi.h>
Napi::Value Leak(const Napi::CallbackInfo& info);

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Meme(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -20,88 +18,125 @@ Napi::Value Meme(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
Image top_text;
Image bottom_text;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
coalesceImages(&coalesced, frames.begin(), frames.end());
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
int width = coalesced.front().columns();
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
int size = width / 9;
int dividedWidth = width / 1000;
int rad = 1;
top_text.size(Geometry(to_string(width)));
top_text.backgroundColor("none");
top_text.font("Impact");
top_text.fontPointsize(width / 12);
top_text.textGravity(Magick::CenterGravity);
top_text.read("pango:<span font_family=\"" +
(font == "roboto" ? "Roboto Condensed" : font) +
"\" weight=\"" + (font != "impact" ? "bold" : "normal") +
"\" foreground='white'>" + top + "</span>");
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);
if (dividedWidth > 1)
top_text_fill.morphology(Magick::DilateMorphology, "Octagon",
dividedWidth);
top_text.composite(top_text_fill, Magick::CenterGravity,
Magick::DstOverCompositeOp);
string font_string = (font == "roboto" ? "Roboto Condensed" : font) + " " +
(font != "impact" ? "bold" : "normal") + " " +
to_string(size);
if (bottom != "") {
bottom_text.size(Geometry(to_string(coalesced.front().columns())));
bottom_text.backgroundColor("none");
bottom_text.font("Impact");
bottom_text.fontPointsize(width / 12);
bottom_text.textGravity(Magick::CenterGravity);
bottom_text.read("pango:<span foreground='white'>" + bottom + "</span>");
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);
if (dividedWidth > 1)
bottom_text_fill.morphology(Magick::DilateMorphology, "Octagon",
dividedWidth);
bottom_text.composite(bottom_text_fill, Magick::CenterGravity,
Magick::DstOverCompositeOp);
VImage mask = VImage::black(rad * 2 + 1, rad * 2 + 1) + 128;
mask.draw_circle({255}, rad, rad, rad, VImage::option()->set("fill", true));
VImage altMask;
if (dividedWidth >= 1) {
altMask = VImage::black(dividedWidth * 2 + 1, dividedWidth * 2 + 1) + 128;
altMask.draw_circle({255}, dividedWidth, dividedWidth, dividedWidth,
VImage::option()->set("fill", true));
}
for (Image &image : coalesced) {
image.composite(top_text, Magick::NorthGravity, Magick::OverCompositeOp);
if (bottom != "")
image.composite(bottom_text, Magick::SouthGravity,
Magick::OverCompositeOp);
image.magick(type);
mid.push_back(image);
}
VImage topText;
if (top != "") {
VImage topIn = VImage::text(
("<span foreground=\"white\">" + top + "</span>").c_str(),
VImage::option()
->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE)
->set("font", font_string.c_str())
->set("width", width));
optimizeTransparency(mid.begin(), mid.end());
topIn = topIn.embed(rad + 10, rad + 10, (topIn.width() + 2 * rad) + 20,
(topIn.height() + 2 * rad) + 20);
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDither(false);
image.quantize();
if (delay != 0) image.animationDelay(delay);
VImage topOutline =
topIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE)
.gaussblur(0.5, VImage::option()->set("min_ampl", 0.1));
if (dividedWidth >= 1) {
topOutline =
topOutline.morph(altMask, VIPS_OPERATION_MORPHOLOGY_DILATE);
}
topOutline = (topOutline == (vector<double>){0, 0, 0, 0});
VImage topInvert = topOutline.extract_band(3).invert();
topOutline = topOutline
.extract_band(0, VImage::option()->set(
"n", topOutline.bands() - 1))
.bandjoin(topInvert);
topText = topOutline.composite2(topIn, VIPS_BLEND_MODE_OVER);
}
writeImages(mid.begin(), mid.end(), &blob);
VImage bottomText;
if (bottom != "") {
VImage bottomIn = VImage::text(
("<span foreground=\"white\">" + bottom + "</span>").c_str(),
VImage::option()
->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE)
->set("font", font_string.c_str())
->set("width", width));
bottomIn =
bottomIn.embed(rad + 10, rad + 10, (bottomIn.width() + 2 * rad) + 20,
(bottomIn.height() + 2 * rad) + 20);
VImage bottomOutline =
bottomIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE)
.gaussblur(0.5, VImage::option()->set("min_ampl", 0.1));
if (dividedWidth >= 1) {
bottomOutline =
bottomOutline.morph(altMask, VIPS_OPERATION_MORPHOLOGY_DILATE);
}
bottomOutline = (bottomOutline == (vector<double>){0, 0, 0, 0});
VImage bottomInvert = bottomOutline.extract_band(3).invert();
bottomOutline = bottomOutline
.extract_band(0, VImage::option()->set(
"n", bottomOutline.bands() - 1))
.bandjoin(bottomInvert);
bottomText = bottomOutline.composite2(bottomIn, VIPS_BLEND_MODE_OVER);
}
vector<VImage> img;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
type == "gif" ? in.crop(0, i * page_height, width, page_height) : in;
if (top != "") {
img_frame = img_frame.composite2(
topText, VIPS_BLEND_MODE_OVER,
VImage::option()->set("x", (width / 2) - (topText.width() / 2)));
}
if (bottom != "") {
img_frame = img_frame.composite2(
bottomText, VIPS_BLEND_MODE_OVER,
VImage::option()
->set("x", (width / 2) - (bottomText.width() / 2))
->set("y", page_height - bottomText.height()));
}
img.push_back(img_frame);
}
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, page_height);
if (delay) final.set("delay", delay);
void *buf;
size_t length;
final.write_to_buffer(
("." + type).c_str(), &buf, &length,
type == "gif" ? VImage::option()->set("dither", 0) : 0);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Mirror(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -22,74 +20,63 @@ Napi::Value Mirror(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
MagickCore::GravityType gravity;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
coalesceImages(&coalesced, frames.begin(), frames.end());
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
if (vertical && first) {
gravity = Magick::NorthGravity;
} else if (!vertical && first) {
gravity = Magick::WestGravity;
} else if (vertical && !first) {
gravity = Magick::SouthGravity;
VImage out;
if (vertical) {
if (type == "gif") {
// once again, libvips gif handling is both a blessing and a curse
vector<VImage> img;
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
bool isOdd = page_height % 2;
for (int i = 0; i < n_pages; i++) {
int x = (i * page_height) + (first ? 0 : (page_height / 2));
VImage cropped = in.crop(0, x, in.width(), page_height / 2);
VImage flipped = cropped.flip(VIPS_DIRECTION_VERTICAL);
VImage final = VImage::arrayjoin(
{first ? cropped : flipped, first ? flipped : cropped},
VImage::option()->set("across", 1));
img.push_back(final);
}
out = VImage::arrayjoin(img, VImage::option()->set("across", 1));
out.set(VIPS_META_PAGE_HEIGHT, page_height - (isOdd ? 1 : 0));
} else {
VImage cropped = in.extract_area(0, 0, in.width(), in.height() / 2);
VImage flipped = cropped.flip(VIPS_DIRECTION_VERTICAL);
out = VImage::arrayjoin({cropped, flipped},
VImage::option()->set("across", 1));
}
} else {
gravity = Magick::EastGravity;
}
for (Image &image : coalesced) {
image.colorSpace(Magick::sRGBColorspace);
list<Image> 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);
VImage cropped = in.extract_area(0, 0, in.width() / 2, in.height());
VImage flipped = cropped.flip(VIPS_DIRECTION_HORIZONTAL);
out = VImage::arrayjoin({cropped, flipped});
} else {
mirrored.push_front(mirror);
}
appendImages(&final, mirrored.begin(), mirrored.end(), vertical);
final.repage();
final.magick(type);
final.animationDelay(delay == 0 ? image.animationDelay() : delay);
mid.push_back(final);
}
optimizeTransparency(mid.begin(), mid.end());
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDither(false);
image.quantize();
int size = in.width() / 2;
VImage cropped = in.extract_area(size, 0, size, in.height());
VImage flipped = cropped.flip(VIPS_DIRECTION_HORIZONTAL);
out = VImage::arrayjoin({flipped, cropped});
}
}
writeImages(mid.begin(), mid.end(), &blob);
if (delay) out.set("delay", delay);
void *buf;
size_t length;
out.write_to_buffer(("." + type).c_str(), &buf, &length);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Motivate(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -20,85 +18,101 @@ Napi::Value Motivate(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
Image top;
Image bottom;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
coalesceImages(&coalesced, frames.begin(), frames.end());
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
top.size(Geometry("600"));
top.backgroundColor("black");
top.font("Times");
top.textGravity(Magick::CenterGravity);
top.fontPointsize(56);
top.read("pango:<span font_family=\"" +
(font == "roboto" ? "Roboto Condensed" : font) +
"\" foreground='white'>" + top_text + "</span>");
top.extent(Geometry(bottom_text != "" ? to_string(top.columns()) + "x" +
to_string(top.rows())
: to_string(top.columns()) + "x" +
to_string(top.rows() + 20)),
"black", Magick::NorthGravity);
int width = in.width();
int size = width / 5;
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
int textWidth = width - ((width / 25) * 2);
string font_string = font == "roboto" ? "Roboto Condensed" : font;
string topText = "<span foreground=\"white\" background=\"black\">" +
top_text + "</span>";
VImage topImage = VImage::text(
topText.c_str(),
VImage::option()
->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE)
->set("font", (font_string + " " + to_string(size)).c_str())
->set("width", textWidth));
VImage bottomImage;
if (bottom_text != "") {
bottom.size(Geometry("600"));
bottom.backgroundColor("black");
bottom.font("Times");
bottom.textGravity(Magick::CenterGravity);
bottom.fontPointsize(28);
bottom.read("pango:<span font_family=\"" +
(font == "roboto" ? "Roboto Condensed" : font) +
"\" foreground='white'>" + bottom_text + "</span>");
bottom.extent(Geometry(to_string(bottom.columns()) + "x" +
to_string(bottom.rows() + 20)),
"black", Magick::NorthGravity);
string bottomText = "<span foreground=\"white\" background=\"black\">" +
bottom_text + "</span>";
bottomImage = VImage::text(
bottomText.c_str(),
VImage::option()
->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE)
->set("font", (font_string + " " + to_string(size * 0.4)).c_str())
->set("width", textWidth));
}
for (Image &image : coalesced) {
Image final;
image.scale(Geometry(500, 500));
image.borderColor("black");
image.border(Geometry(5, 5));
image.borderColor("white");
image.border(Geometry(3, 3));
image.backgroundColor("black");
image.extent(Geometry(600, image.rows() + 50), Magick::CenterGravity);
vector<VImage> img;
int height;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
type == "gif" ? in.crop(0, i * page_height, width, page_height) : in;
list<Image> to_append;
to_append.push_back(image);
to_append.push_back(top);
if (bottom_text != "") to_append.push_back(bottom);
appendImages(&final, to_append.begin(), to_append.end(), true);
final.repage();
final.magick(type);
final.animationDelay(delay == 0 ? image.animationDelay() : delay);
mid.push_back(final);
}
int borderSize = max(2, width / 66);
int borderSize2 = borderSize * 0.5;
VImage bordered =
img_frame.embed(borderSize, borderSize, width + (borderSize * 2),
page_height + (borderSize * 2),
VImage::option()->set("extend", "black"));
VImage bordered2 = bordered.embed(
borderSize2, borderSize2, bordered.width() + (borderSize2 * 2),
bordered.height() + (borderSize2 * 2),
VImage::option()->set("extend", "white"));
optimizeTransparency(mid.begin(), mid.end());
int addition = width / 8;
int sideAddition = page_height * 0.4;
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDither(false);
image.quantize();
VImage bordered3 = bordered2.embed(
sideAddition / 2, addition / 2, bordered2.width() + sideAddition,
bordered2.height() + addition,
VImage::option()->set("extend", "black"));
VImage frame = bordered3.join(
topImage.gravity(VIPS_COMPASS_DIRECTION_NORTH, bordered3.width(),
topImage.height() + (size / 4),
VImage::option()->set("extend", "black")),
VIPS_DIRECTION_VERTICAL,
VImage::option()->set("background", 0x000000)->set("expand", true));
if (bottom_text != "") {
frame = frame.join(
bottomImage.gravity(VIPS_COMPASS_DIRECTION_NORTH, bordered3.width(),
bottomImage.height() + (size / 4),
VImage::option()->set("extend", "black")),
VIPS_DIRECTION_VERTICAL,
VImage::option()->set("background", 0x000000)->set("expand", true));
}
height = frame.height();
img.push_back(frame);
}
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1))
.extract_band(0, VImage::option()->set("n", 3));
final.set(VIPS_META_PAGE_HEIGHT, height);
if (delay) final.set("delay", delay);
writeImages(mid.begin(), mid.end(), &blob);
void *buf;
size_t length;
final.write_to_buffer(
("." + type).c_str(), &buf, &length,
type == "gif" ? VImage::option()->set("dither", 1) : 0);
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Reddit(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -18,62 +16,58 @@ Napi::Value Reddit(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
Image watermark;
Image text_image;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
string assetPath = basePath + "assets/images/reddit.png";
VImage tmpl = VImage::new_from_file(assetPath.c_str());
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
string captionText = "<span foreground=\"white\">" + text + "</span>";
VImage textImage =
VImage::text(captionText.c_str(), VImage::option()
->set("rgba", true)
->set("font", "Roboto 62")
->set("align", VIPS_ALIGN_LOW));
VImage composited =
tmpl.composite2(textImage, VIPS_BLEND_MODE_OVER,
VImage::option()->set("x", 375)->set(
"y", (tmpl.height() - textImage.height()) - 64));
VImage watermark =
composited.resize((double)width / (double)composited.width());
vector<VImage> img;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
type == "gif" ? in.crop(0, i * page_height, width, page_height) : in;
VImage frame = img_frame.join(watermark, VIPS_DIRECTION_VERTICAL,
VImage::option()->set("expand", true));
img.push_back(frame);
}
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, page_height + watermark.height());
if (delay) final.set("delay", delay);
watermark.read(basePath + "assets/images/reddit.png");
text_image.textGravity(Magick::WestGravity);
text_image.font("Roboto");
text_image.fontPointsize(47);
text_image.backgroundColor("none");
text_image.read("pango:<span foreground='white'>" + text + "</span>");
void *buf;
size_t length;
final.write_to_buffer(
("." + type).c_str(), &buf, &length,
type == "gif" ? VImage::option()->set("dither", 0) : 0);
watermark.composite(text_image, Geometry("+375+46"),
Magick::OverCompositeOp);
string query(to_string(frames.front().baseColumns()) + "x");
watermark.scale(Geometry(query));
coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) {
Image final;
list<Image> to_append;
to_append.push_back(image);
to_append.push_back(watermark);
appendImages(&final, to_append.begin(), to_append.end(), true);
final.repage();
image.magick(type);
final.animationDelay(delay == 0 ? image.animationDelay() : delay);
mid.push_back(final);
}
optimizeTransparency(mid.begin(), mid.end());
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize();
}
}
writeImages(mid.begin(), mid.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Resize(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -22,49 +20,43 @@ Napi::Value Resize(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> blurred;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
VImage out;
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int finalHeight;
if (stretch) {
out = in.resize(
512.0 / (double)width,
VImage::option()->set("vscale", 512.0 / (double)page_height));
finalHeight = 512;
} else if (wide) {
out = in.resize(9.5, VImage::option()->set("vscale", 0.5));
finalHeight = page_height / 2;
} else {
out = in.resize(0.1).resize(
10, VImage::option()->set("kernel", VIPS_KERNEL_NEAREST));
finalHeight = page_height;
}
coalesceImages(&coalesced, frames.begin(), frames.end());
out.set(VIPS_META_PAGE_HEIGHT, finalHeight);
if (delay) out.set("delay", delay);
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%"));
}
image.magick(type);
blurred.push_back(image);
}
void *buf;
size_t length;
out.write_to_buffer(("." + type).c_str(), &buf, &length);
optimizeTransparency(blurred.begin(), blurred.end());
if (type == "gif") {
for (Image &image : blurred) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize();
if (delay != 0) image.animationDelay(delay);
}
}
writeImages(blurred.begin(), blurred.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,11 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Reverse(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -15,47 +15,54 @@ Napi::Value Reverse(const Napi::CallbackInfo &info) {
Napi::Buffer<char> data = obj.Get("data").As<Napi::Buffer<char>>();
bool soos =
obj.Has("soos") ? obj.Get("soos").As<Napi::Boolean>().Value() : false;
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options =
VImage::option()->set("access", "sequential")->set("n", -1);
list<Image> frames;
list<Image> coalesced;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
VImage in = VImage::new_from_buffer(data.Data(), data.Length(), "", options)
.colourspace(VIPS_INTERPRETATION_sRGB);
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
vector<VImage> split;
// todo: find a better way of getting individual frames (or at least getting the frames in reverse order)
for (int i = 0; i < n_pages; i++) {
VImage img_frame = in.crop(0, i * page_height, width, page_height);
split.push_back(img_frame);
}
coalesceImages(&coalesced, frames.begin(), frames.end());
vector<int> delays = in.get_array_int("delay");
if (soos) {
list<Image> copy = coalesced;
copy.reverse();
vector<VImage> copy = split;
vector<int> copy2 = delays;
reverse(copy.begin(), copy.end());
reverse(copy2.begin(), copy2.end());
copy.pop_back();
copy.pop_front();
coalesced.insert(coalesced.end(), copy.begin(), copy.end());
copy2.pop_back();
copy.erase(copy.begin());
copy2.erase(copy2.begin());
split.insert(split.end(), copy.begin(), copy.end());
delays.insert(delays.end(), copy2.begin(), copy2.end());
} else {
coalesced.reverse();
reverse(split.begin(), split.end());
reverse(delays.begin(), delays.end());
}
for_each(coalesced.begin(), coalesced.end(), magickImage("GIF"));
VImage final = VImage::arrayjoin(split, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, page_height);
final.set("delay", delays);
optimizeTransparency(coalesced.begin(), coalesced.end());
void *buf;
size_t length;
final.write_to_buffer(".gif", &buf, &length,
VImage::option()->set("dither", 0));
for (Image &image : coalesced) {
image.quantizeDither(false);
image.quantize();
if (delay != 0) image.animationDelay(delay);
}
writeImages(coalesced.begin(), coalesced.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", "gif");
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Snapchat(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -20,58 +18,64 @@ Napi::Value Snapchat(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> captioned;
Blob caption_blob;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
int size = width / 20;
int textWidth = width - ((width / 25) * 2);
string font_string = "Helvetica Neue " + to_string(size);
VImage textIn =
VImage::text(("<span foreground=\"white\" background=\"#000000B2\">" +
caption + "</span>")
.c_str(),
VImage::option()
->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE)
->set("font", font_string.c_str())
->set("width", textWidth));
int bgHeight = textIn.height() + (width / 25);
textIn =
((textIn == (vector<double>){0, 0, 0, 0}).bandand())
.ifthenelse({0, 0, 0, 178}, textIn)
.embed((width / 2) - (textIn.width() / 2),
(bgHeight / 2) - (textIn.height() / 2), width, bgHeight,
VImage::option()
->set("extend", "background")
->set("background", (vector<double>){0, 0, 0, 178}));
vector<VImage> img;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
type == "gif" ? in.crop(0, i * page_height, width, page_height) : in;
img_frame = img_frame.composite2(
textIn, VIPS_BLEND_MODE_OVER,
VImage::option()->set("x", 0)->set("y", page_height * pos));
img.push_back(img_frame);
}
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, page_height);
if (delay) final.set("delay", delay);
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("none"));
caption_image.backgroundColor(Color("none"));
caption_image.fillColor("white");
caption_image.font("Helvetica Neue");
caption_image.fontPointsize(width / 25);
caption_image.textGravity(Magick::CenterGravity);
caption_image.read("pango:" + caption);
caption_image.backgroundColor(Color("rgba(0, 0, 0, 0.7)"));
caption_image.extent(Geometry(width, caption_image.rows() + (width / 25)),
Magick::CenterGravity);
void *buf;
size_t length;
final.write_to_buffer(
("." + type).c_str(), &buf, &length,
type == "gif" ? VImage::option()->set("dither", 0) : 0);
coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) {
list<Image> images;
image.composite(caption_image, 0, height * pos, Magick::OverCompositeOp);
image.magick(type);
image.animationDelay(delay == 0 ? image.animationDelay() : delay);
captioned.push_back(image);
}
optimizeTransparency(captioned.begin(), captioned.end());
if (type == "gif") {
for (Image &image : captioned) {
image.quantizeDither(false);
image.quantize();
}
}
writeImages(captioned.begin(), captioned.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,10 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Sonic(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -14,25 +13,30 @@ Napi::Value Sonic(const Napi::CallbackInfo &info) {
string text = obj.Get("text").As<Napi::String>().Utf8Value();
string basePath = obj.Get("basePath").As<Napi::String>().Utf8Value();
Blob blob;
string assetPath = basePath + "assets/images/sonic.jpg";
VImage bg = VImage::new_from_file(assetPath.c_str());
Image image;
Image text_image;
text_image.backgroundColor("none");
text_image.fontPointsize(72);
text_image.textGravity(Magick::CenterGravity);
text_image.font("Bitstream Vera Sans");
text_image.read("pango:<span foreground='white'>" + text + "</span>");
text_image.resize(Geometry(474, 332));
text_image.extent(Geometry("1024x538-435-145"), Magick::CenterGravity);
image.read(basePath + "assets/images/sonic.jpg");
image.composite(text_image, Geometry("+160+10"), Magick::OverCompositeOp);
image.magick("PNG");
image.write(&blob);
VImage textImage =
VImage::text(("<span foreground=\"white\">" + text + "</span>").c_str(),
VImage::option()
->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE)
->set("font", "Bitstream Vera Sans")
->set("width", 542)
->set("height", 390))
.gravity(VIPS_COMPASS_DIRECTION_CENTRE, 542, 390);
VImage out = bg.composite2(textImage, VIPS_BLEND_MODE_OVER,
VImage::option()->set("x", 391)->set("y", 84));
void *buf;
size_t length;
out.write_to_buffer(".png", &buf, &length);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", "png");
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
void *memset16(void *m, uint16_t val, size_t count) {
uint16_t *buf = (uint16_t *)m;
@ -14,6 +12,36 @@ void *memset16(void *m, uint16_t val, size_t count) {
return m;
}
void vipsRemove(Napi::Env *env, Napi::Object *result, Napi::Buffer<char> data,
int speed) {
VOption *options = VImage::option()->set("access", "sequential");
VImage in = VImage::new_from_buffer(data.Data(), data.Length(), "",
options->set("n", -1))
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
vector<VImage> img;
for (int i = 0; i < n_pages; i += speed) {
VImage img_frame = in.crop(0, i * page_height, width, page_height);
img.push_back(img_frame);
}
VImage out = VImage::arrayjoin(img, VImage::option()->set("across", 1));
out.set(VIPS_META_PAGE_HEIGHT, page_height);
void *buf;
size_t length;
out.write_to_buffer(".gif", &buf, &length);
vips_thread_shutdown();
result->Set("data", Napi::Buffer<char>::Copy(*env, (char *)buf, length));
}
Napi::Value Speed(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -80,37 +108,7 @@ Napi::Value Speed(const Napi::CallbackInfo &info) {
result.Set("data",
Napi::Buffer<char>::Copy(env, fileData, data.Length()));
if (removeFrames) {
Blob blob;
list<Image> frames;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
for (list<Image>::iterator i = frames.begin(); i != frames.end(); ++i) {
int index = distance(frames.begin(), i);
if (index >= (int)old_delays.size()) {
old_delays.resize(index+1);
old_delays[index] = old_delays[index-1];
}
i->animationDelay(old_delays[index]);
}
for (int i = 0; i < speed - 1; ++i) {
auto it = frames.begin();
while(it != frames.end() && ++it != frames.end()) it = frames.erase(it);
}
for_each(frames.begin(), frames.end(), magickImage(type));
writeImages(frames.begin(), frames.end(), &blob);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
}
if (removeFrames) vipsRemove(&env, &result, data, speed);
} else {
char *lastPos;
@ -133,26 +131,7 @@ Napi::Value Speed(const Napi::CallbackInfo &info) {
}
if (removeFrames) {
Blob blob;
list<Image> frames;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
for (int i = 0; i < speed - 1; ++i) {
auto it = frames.begin();
while(it != frames.end() && ++it != frames.end()) it = frames.erase(it);
}
for_each(frames.begin(), frames.end(), magickImage(type));
writeImages(frames.begin(), frames.end(), &blob);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
vipsRemove(&env, &result, data, speed);
} else {
while (lastPos != NULL) {
if (memcmp(lastPos, match, 4) != 0) {
@ -178,4 +157,4 @@ Napi::Value Speed(const Napi::CallbackInfo &info) {
} catch (...) {
throw Napi::Error::New(env, "Unknown error");
}
}
}

View file

@ -1,74 +0,0 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
using namespace std;
using namespace Magick;
Napi::Value Trump(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
try {
Napi::Object obj = info[0].As<Napi::Object>();
Napi::Buffer<char> data = obj.Get("data").As<Napi::Buffer<char>>();
string type = obj.Get("type").As<Napi::String>().Utf8Value();
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
Image watermark;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
watermark.read("./assets/images/trump.png");
coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) {
Image watermark_new = watermark;
image.virtualPixelMethod(Magick::TransparentVirtualPixelMethod);
image.backgroundColor("none");
image.scale(Geometry("365x179!"));
double arguments[16] = {0, 0, 207, 268, 365, 0, 548, 271,
365, 179, 558, 450, 0, 179, 193, 450};
image.distort(Magick::PerspectiveDistortion, 16, arguments, true);
image.extent(Geometry("800x450"), Magick::CenterGravity);
watermark_new.composite(image, Geometry("-25+134"),
Magick::DstOverCompositeOp);
watermark_new.magick(type);
watermark_new.animationDelay(delay == 0 ? image.animationDelay() : delay);
watermark_new.gifDisposeMethod(Magick::BackgroundDispose);
mid.push_back(watermark_new);
}
optimizeTransparency(mid.begin(), mid.end());
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize();
}
}
writeImages(mid.begin(), mid.end(), &blob);
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("type", type);
return result;
} catch (std::exception const &err) {
throw Napi::Error::New(env, err.what());
} catch (...) {
throw Napi::Error::New(env, "Unknown error");
}
}

View file

@ -1,5 +0,0 @@
#pragma once
#include <napi.h>
Napi::Value Trump(const Napi::CallbackInfo& info);

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Uncaption(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -13,61 +11,51 @@ Napi::Value Uncaption(const Napi::CallbackInfo &info) {
try {
Napi::Object obj = info[0].As<Napi::Object>();
Napi::Buffer<char> data = obj.Get("data").As<Napi::Buffer<char>>();
float tolerance = obj.Has("tolerance") ? obj.Get("tolerance").As<Napi::Number>().FloatValue() : 0.95;
float tolerance = obj.Has("tolerance")
? obj.Get("tolerance").As<Napi::Number>().FloatValue()
: 0.5;
string type = obj.Get("type").As<Napi::String>().Utf8Value();
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option();
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1)->set("access", "sequential") : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
VImage first =
in.crop(0, 0, 3, page_height).colourspace(VIPS_INTERPRETATION_B_W) >
(255 * tolerance);
int top, captionWidth, captionHeight;
first.find_trim(&top, &captionWidth, &captionHeight);
vector<VImage> img;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
in.crop(0, (i * page_height) + top, width, page_height - top);
img.push_back(img_frame);
}
coalesceImages(&coalesced, frames.begin(), frames.end());
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, page_height - top);
if (delay) final.set("delay", delay);
Image firstImage = coalesced.front();
ssize_t columns = firstImage.columns();
ssize_t rows = firstImage.rows();
ssize_t row;
for (row = 0; row < rows; ++row) {
ColorGray color = firstImage.pixelColor(0, row);
if (color.shade() < tolerance) {
break;
}
}
Geometry geom = Geometry(columns, row == rows ? rows : rows - row, 0,
row == rows ? 0 : row);
void *buf;
size_t length;
final.write_to_buffer(
("." + type).c_str(), &buf, &length,
type == "gif" ? VImage::option()->set("dither", 0) : 0);
for (Image &image : coalesced) {
image.virtualPixelMethod(Magick::TransparentVirtualPixelMethod);
image.backgroundColor("none");
image.extent(geom);
image.magick(type);
mid.push_back(image);
}
optimizeTransparency(mid.begin(), mid.end());
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDither(false);
image.quantize();
if (delay != 0) image.animationDelay(delay);
}
}
writeImages(mid.begin(), mid.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Watermark(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -14,8 +12,7 @@ Napi::Value Watermark(const Napi::CallbackInfo &info) {
Napi::Object obj = info[0].As<Napi::Object>();
Napi::Buffer<char> data = obj.Get("data").As<Napi::Buffer<char>>();
string water = obj.Get("water").As<Napi::String>().Utf8Value();
Magick::GravityType gravity =
Magick::GravityType(obj.Get("gravity").As<Napi::Number>().Int64Value());
int gravity = obj.Get("gravity").As<Napi::Number>().Int64Value();
bool resize = obj.Has("resize")
? obj.Get("resize").As<Napi::Boolean>().Value()
: false;
@ -25,76 +22,141 @@ Napi::Value Watermark(const Napi::CallbackInfo &info) {
bool append = obj.Has("append")
? obj.Get("append").As<Napi::Boolean>().Value()
: false;
bool alpha =
obj.Has("alpha") ? obj.Get("alpha").As<Napi::Boolean>().Value() : false;
bool mc = obj.Has("mc") ? obj.Get("mc").As<Napi::Boolean>().Value() : false;
string basePath = obj.Get("basePath").As<Napi::String>().Utf8Value();
string type = obj.Get("type").As<Napi::String>().Utf8Value();
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
Image watermark;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
string merged = basePath + water;
watermark.read(merged);
VImage watermark = VImage::new_from_file(merged.c_str());
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
if (resize && append) {
string query(to_string(frames.front().baseColumns()) + "x");
watermark.scale(Geometry(query));
watermark = watermark.resize((double)width / (double)watermark.width());
} else if (resize && yscale) {
string query(to_string(frames.front().baseColumns()) + "x" +
to_string(frames.front().baseRows() * yscale) + "!");
watermark.resize(Geometry(query));
watermark = watermark.resize(
(double)width / (double)watermark.width(),
VImage::option()->set("vscale", (double)(page_height * yscale) /
(double)watermark.height()));
} else if (resize) {
string query("x" + to_string(frames.front().baseRows()));
watermark.scale(Geometry(query));
watermark =
watermark.resize((double)page_height / (double)watermark.height());
}
coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) {
Image final;
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<VImage> 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;
if (append) {
list<Image> to_append;
to_append.push_back(image);
to_append.push_back(watermark);
appendImages(&final, to_append.begin(), to_append.end(), true);
final.repage();
VImage appended = img_frame.join(watermark, VIPS_DIRECTION_VERTICAL,
VImage::option()->set("expand", true));
addedHeight = watermark.height();
img.push_back(appended);
} else if (mc) {
image.backgroundColor("white");
image.extent(Geometry(image.columns(), image.rows() + 15));
image.composite(watermark, gravity, Magick::OverCompositeOp);
final = image;
VImage padded =
img_frame.embed(0, 0, width, page_height + 15,
VImage::option()->set("background", 0xffffff));
VImage composited =
padded.composite2(watermark, VIPS_BLEND_MODE_OVER,
VImage::option()
->set("x", width - 190)
->set("y", padded.height() - 22));
addedHeight = 15;
img.push_back(composited);
} else {
image.composite(watermark, gravity, Magick::OverCompositeOp);
final = image;
}
image.magick(type);
final.animationDelay(delay == 0 ? image.animationDelay() : delay);
mid.push_back(final);
}
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));
optimizeTransparency(mid.begin(), mid.end());
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize();
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);
}
}
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, page_height + addedHeight);
if (delay) final.set("delay", delay);
writeImages(mid.begin(), mid.end(), &blob);
void *buf;
size_t length;
final.write_to_buffer(
("." + type).c_str(), &buf, &length,
type == "gif" ? VImage::option()->set("dither", 0) : 0);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,11 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <iostream>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Whisper(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -18,74 +16,80 @@ Napi::Value Whisper(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> captioned;
Blob caption_blob;
try {
readImages(&frames, Blob(data.Data(), data.Length()));
} catch (Magick::WarningCoder &warning) {
cerr << "Coder Warning: " << warning.what() << endl;
} catch (Magick::Warning &warning) {
cerr << "Warning: " << warning.what() << endl;
}
size_t width = frames.front().baseColumns();
size_t height = frames.front().baseRows();
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
int size = width / 6;
int dividedWidth = width / 175;
int rad = 1;
Image caption_image;
caption_image.size(Geometry(to_string(width) + "x" + to_string(height)));
caption_image.backgroundColor("none");
caption_image.fillColor("white");
caption_image.font("Upright");
caption_image.fontPointsize(width / 8);
caption_image.textGravity(Magick::CenterGravity);
caption_image.read("pango:" + caption);
caption_image.trim();
caption_image.repage();
Image caption_fill = caption_image;
caption_fill.extent(Geometry(width, height), Magick::CenterGravity);
caption_fill.channel(Magick::AlphaChannel);
caption_fill.morphology(Magick::EdgeOutMorphology, "Octagon",
dividedWidth != 0 ? dividedWidth : 1);
caption_fill.backgroundColor("black");
caption_fill.alphaChannel(Magick::ShapeAlphaChannel);
size_t fill_width = caption_fill.columns();
size_t fill_height = caption_fill.rows();
caption_image.extent(Geometry(fill_width, fill_height),
Magick::CenterGravity);
caption_image.composite(caption_fill, Magick::CenterGravity,
Magick::DstOverCompositeOp);
string font_string = "Upright " + to_string(size);
coalesceImages(&coalesced, frames.begin(), frames.end());
for (Image &image : coalesced) {
list<Image> images;
image.composite(caption_image, Magick::CenterGravity,
Magick::OverCompositeOp);
image.magick(type);
image.animationDelay(delay == 0 ? image.animationDelay() : delay);
captioned.push_back(image);
VImage mask;
if (dividedWidth >= 1) {
mask = VImage::black(dividedWidth * 2 + 1, dividedWidth * 2 + 1) + 128;
mask.draw_circle({255}, dividedWidth, dividedWidth, dividedWidth,
VImage::option()->set("fill", true));
} else {
mask = VImage::black(rad * 2 + 1, rad * 2 + 1) + 128;
mask.draw_circle({255}, rad, rad, rad,
VImage::option()->set("fill", true));
}
optimizeTransparency(captioned.begin(), captioned.end());
VImage textIn = VImage::text(
("<span foreground=\"white\">" + caption + "</span>").c_str(),
VImage::option()
->set("rgba", true)
->set("align", VIPS_ALIGN_CENTRE)
->set("font", font_string.c_str())
->set("width", width));
if (type == "gif") {
for (Image &image : captioned) {
image.quantizeDither(false);
image.quantize();
}
textIn = textIn.embed(rad + 10, rad + 10, (textIn.width() + 2 * rad) + 20,
(textIn.height() + 2 * rad) + 20);
VImage outline =
textIn.morph(mask, VIPS_OPERATION_MORPHOLOGY_DILATE)
.gaussblur(0.5, VImage::option()->set("min_ampl", 0.1));
outline = (outline == (vector<double>){0, 0, 0, 0});
VImage invert = outline.extract_band(3).invert();
outline =
outline.extract_band(0, VImage::option()->set("n", outline.bands() - 1))
.bandjoin(invert);
VImage textImg = outline.composite2(textIn, VIPS_BLEND_MODE_OVER);
vector<VImage> img;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
type == "gif" ? in.crop(0, i * page_height, width, page_height) : in;
img_frame = img_frame.composite2(
textImg, VIPS_BLEND_MODE_OVER,
VImage::option()
->set("x", (width / 2) - (textImg.width() / 2))
->set("y", (page_height / 2) - (textImg.height() / 2)));
img.push_back(img_frame);
}
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, page_height);
if (delay) final.set("delay", delay);
writeImages(captioned.begin(), captioned.end(), &blob);
void *buf;
size_t length;
final.write_to_buffer(
("." + type).c_str(), &buf, &length,
type == "gif" ? VImage::option()->set("dither", 0) : 0);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {

View file

@ -1,10 +1,9 @@
#include <Magick++.h>
#include <napi.h>
#include <list>
#include <vips/vips8>
using namespace std;
using namespace Magick;
using namespace vips;
Napi::Value Zamn(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
@ -17,42 +16,44 @@ Napi::Value Zamn(const Napi::CallbackInfo &info) {
int delay =
obj.Has("delay") ? obj.Get("delay").As<Napi::Number>().Int32Value() : 0;
Blob blob;
VOption *options = VImage::option()->set("access", "sequential");
list<Image> frames;
list<Image> coalesced;
list<Image> mid;
Image watermark;
readImages(&frames, Blob(data.Data(), data.Length()));
watermark.read(basePath + "assets/images/zamn.png");
coalesceImages(&coalesced, frames.begin(), frames.end());
VImage in =
VImage::new_from_buffer(data.Data(), data.Length(), "",
type == "gif" ? options->set("n", -1) : options)
.colourspace(VIPS_INTERPRETATION_sRGB);
if (!in.has_alpha()) in = in.bandjoin(255);
for (Image &image : coalesced) {
Image watermark_new = watermark;
image.backgroundColor("none");
image.scale(Geometry("303x438!"));
image.extent(Geometry("621x516-310-75"));
watermark_new.composite(image, Magick::CenterGravity,
Magick::OverCompositeOp);
watermark_new.magick(type);
watermark_new.animationDelay(delay == 0 ? image.animationDelay() : delay);
mid.push_back(watermark_new);
int width = in.width();
int page_height = vips_image_get_page_height(in.get_image());
int n_pages = vips_image_get_n_pages(in.get_image());
string assetPath = basePath + "assets/images/zamn.png";
VImage tmpl = VImage::new_from_file(assetPath.c_str());
vector<VImage> img;
for (int i = 0; i < n_pages; i++) {
VImage img_frame =
type == "gif" ? in.crop(0, i * page_height, width, page_height) : in;
VImage composited = tmpl.insert(
img_frame.extract_band(0, VImage::option()->set("n", 3)).bandjoin(255).resize(
303.0 / (double)width,
VImage::option()->set("vscale", 438.0 / (double)page_height)),
310, 76);
img.push_back(composited);
}
VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1));
final.set(VIPS_META_PAGE_HEIGHT, 516);
if (delay) final.set("delay", delay);
optimizeTransparency(mid.begin(), mid.end());
void *buf;
size_t length;
final.write_to_buffer(("." + type).c_str(), &buf, &length);
if (type == "gif") {
for (Image &image : mid) {
image.quantizeDitherMethod(FloydSteinbergDitherMethod);
image.quantize();
}
}
writeImages(mid.begin(), mid.end(), &blob);
vips_thread_shutdown();
Napi::Object result = Napi::Object::New(env);
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)blob.data(),
blob.length()));
result.Set("data", Napi::Buffer<char>::Copy(env, (char *)buf, length));
result.Set("type", type);
return result;
} catch (std::exception const &err) {