Merge branch 'vips'
This commit is contained in:
commit
d3b3e67104
45 changed files with 1243 additions and 1396 deletions
|
@ -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})
|
||||
|
|
BIN
assets/images/globediffuse.png
Normal file
BIN
assets/images/globediffuse.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
BIN
assets/images/globespec.png
Normal file
BIN
assets/images/globespec.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
BIN
assets/images/speech.png
Normal file
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 |
|
@ -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("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%");
|
||||
return {
|
||||
text: wrap(cleanedMessage, {width: 15, indent: ""})
|
||||
text: cleanedMessage
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%");
|
||||
let newCaption = newArgs.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").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,
|
||||
|
|
|
@ -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("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%") : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" "),
|
||||
caption: newArgs && newArgs.trim() ? newArgs.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").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"
|
||||
};
|
||||
|
|
|
@ -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;
|
|
@ -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("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%"),
|
||||
bottom: bottomText ? (this.specialArgs.case ? bottomText : bottomText.toUpperCase()).replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%") : "",
|
||||
top: (this.specialArgs.case ? topText : topText.toUpperCase()).replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\\n", "\n"),
|
||||
bottom: bottomText ? (this.specialArgs.case ? bottomText : bottomText.toUpperCase()).replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\\n", "\n") : "",
|
||||
font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "impact"
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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"];
|
||||
|
|
|
@ -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;
|
|
@ -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("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%")
|
||||
caption: newArgs.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\\n", "\n")
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
116
natives/globe.cc
116
natives/globe.cc
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <napi.h>
|
||||
|
||||
Napi::Value Leak(const Napi::CallbackInfo& info);
|
179
natives/meme.cc
179
natives/meme.cc
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <napi.h>
|
||||
|
||||
Napi::Value Trump(const Napi::CallbackInfo& info);
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue