diff --git a/app.js b/app.js index d76641c..e5e4fc7 100644 --- a/app.js +++ b/app.js @@ -75,27 +75,27 @@ async function* getFiles(dir) { async function init() { await exec("git rev-parse HEAD").then(output => output.stdout.substring(0, 7), () => "unknown commit").then(o => process.env.GIT_REV = o); console.log(` - ,*\`$ z\`"v - F zBw\`% A ,W "W - ,\` ,EBBBWp"%. ,-=~~==-,+* 4BBE T - M BBBBBBBB* ,w=####Wpw 4BBBBB# 1 - F BBBBBBBMwBBBBBBBBBBBBB#wXBBBBBH E - F BBBBBBkBBBBBBBBBBBBBBBBBBBBE4BL k - # BFBBBBBBBBBBBBF" "RBBBW F - V ' 4BBBBBBBBBBM TBBL F - F BBBBBBBBBBF JBB L - F FBBBBBBBEB BBL 4 - E [BB4BBBBEBL BBL 4 - I #BBBBBBBEB 4BBH *w - A 4BBBBBBBBBEW, ,BBBB W [ -.A ,k 4BBBBBBBBBBBEBW####BBBBBBM BF F -k -#include -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Blur(string type, string *outType, char *BufferData, size_t BufferLength, - ArgumentMap Arguments, size_t *DataSize) { - bool sharp = GetArgument(Arguments, "sharp"); - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - - if (!in.has_alpha()) in = in.bandjoin(255); - - // 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); - - void *buf; - out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); - - return (char *)buf; -} +#include +#include +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Blur(string type, string *outType, char *BufferData, size_t BufferLength, + ArgumentMap Arguments, size_t *DataSize) { + bool sharp = GetArgument(Arguments, "sharp"); + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + + if (!in.has_alpha()) in = in.bandjoin(255); + + // 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); + + void *buf; + out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = out.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; +} diff --git a/natives/blur.h b/natives/blur.h index 8645e77..fb8fc6e 100644 --- a/natives/blur.h +++ b/natives/blur.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Blur(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Blur(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/bounce.cc b/natives/bounce.cc index 74d15b1..c683e17 100644 --- a/natives/bounce.cc +++ b/natives/bounce.cc @@ -1,51 +1,56 @@ -#include - -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Bounce(string type, string *outType, char *BufferData, - size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, - size_t *DataSize) { - VOption *options = VImage::option(); - - VImage in = - VImage::new_from_buffer( - BufferData, BufferLength, "", - 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 pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 15; - double mult = M_PI / nPages; - int halfHeight = pageHeight / 2; - - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - double height = halfHeight * ((abs(sin(i * mult)) * -1) + 1); - VImage embedded = - img_frame.embed(0, height, width, pageHeight + halfHeight); - img.push_back(embedded); - } - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, pageHeight + halfHeight); - if (type != "gif") { - vector delay(30, 50); - final.set("delay", delay); - } - - void *buf; - final.write_to_buffer(".gif", &buf, DataSize); - - *outType = "gif"; - - return (char *)buf; +#include + +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Bounce(string type, string *outType, char *BufferData, + size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, + size_t *DataSize) { + VOption *options = VImage::option(); + + VImage in = + VImage::new_from_buffer( + BufferData, BufferLength, "", + 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 pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 15; + double mult = M_PI / nPages; + int halfHeight = pageHeight / 2; + + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + double height = halfHeight * ((abs(sin(i * mult)) * -1) + 1); + VImage embedded = + img_frame.embed(0, height, width, pageHeight + halfHeight); + img.push_back(embedded); + } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight + halfHeight); + if (type != "gif") { + vector delay(30, 50); + final.set("delay", delay); + } + + void *buf; + final.write_to_buffer(".gif", &buf, DataSize); + + *outType = "gif"; + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = pageHeight + halfHeight; + + return output; } \ No newline at end of file diff --git a/natives/bounce.h b/natives/bounce.h index ef4c53a..64d5f93 100644 --- a/natives/bounce.h +++ b/natives/bounce.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Bounce(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Bounce(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/caption.cc b/natives/caption.cc index a9f9e45..6de162f 100644 --- a/natives/caption.cc +++ b/natives/caption.cc @@ -1,77 +1,82 @@ -#include -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Caption(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - string caption = GetArgument(Arguments, "caption"); - string font = GetArgument(Arguments, "font"); - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - 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 pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = 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 = "" + caption + ""; - - VImage text; - auto findResult = fontPaths.find(font); - if (findResult != fontPaths.end()) { - text = VImage::text( - ".", VImage::option()->set("fontfile", - (basePath + findResult->second).c_str())); - } - text = VImage::text( - captionText.c_str(), - VImage::option() - ->set("rgba", true) - ->set("align", VIPS_ALIGN_CENTRE) - ->set("font", font_string.c_str()) - ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) - ->set("width", textWidth)); - VImage captionImage = - ((text == (vector){0, 0, 0, 0}).bandand()) - .ifthenelse(255, text) - .gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, text.height() + size, - VImage::option()->set("extend", "white")); - - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : 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, pageHeight + captionImage.height()); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" - ? VImage::option()->set("dither", 0)->set("reoptimise", 1) - : 0); - - return (char *)buf; -} +#include +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Caption(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + string caption = GetArgument(Arguments, "caption"); + string font = GetArgument(Arguments, "font"); + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + 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 pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = 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 = "" + caption + ""; + + VImage text; + auto findResult = fontPaths.find(font); + if (findResult != fontPaths.end()) { + text = VImage::text( + ".", VImage::option()->set("fontfile", + (basePath + findResult->second).c_str())); + } + text = VImage::text( + captionText.c_str(), + VImage::option() + ->set("rgba", true) + ->set("align", VIPS_ALIGN_CENTRE) + ->set("font", font_string.c_str()) + ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) + ->set("width", textWidth)); + VImage captionImage = + ((text == (vector){0, 0, 0, 0}).bandand()) + .ifthenelse(255, text) + .gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, text.height() + size, + VImage::option()->set("extend", "white")); + + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : 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, pageHeight + captionImage.height()); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" + ? VImage::option()->set("dither", 0)->set("reoptimise", 1) + : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = final.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; +} diff --git a/natives/caption.h b/natives/caption.h index c8255ed..34184da 100644 --- a/natives/caption.h +++ b/natives/caption.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Caption(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Caption(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/caption2.cc b/natives/caption2.cc index 688f7f3..58077ab 100644 --- a/natives/caption2.cc +++ b/natives/caption2.cc @@ -1,81 +1,86 @@ -#include -#include -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *CaptionTwo(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - bool top = GetArgument(Arguments, "top"); - string caption = GetArgument(Arguments, "caption"); - string font = GetArgument(Arguments, "font"); - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - 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 pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - int textWidth = width - ((width / 25) * 2); - - string font_string = (font == "roboto" ? "Roboto Condensed" : font) + - ", Twemoji Color Emoji " + to_string(size); - - string captionText = "" + caption + ""; - - VImage text; - auto findResult = fontPaths.find(font); - if (findResult != fontPaths.end()) { - text = VImage::text( - ".", VImage::option()->set("fontfile", - (basePath + findResult->second).c_str())); - } - text = VImage::text( - captionText.c_str(), - VImage::option() - ->set("rgba", true) - ->set("font", font_string.c_str()) - ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) - ->set("align", VIPS_ALIGN_LOW) - ->set("width", textWidth)); - VImage captionImage = - ((text == (vector){0, 0, 0, 0}).bandand()) - .ifthenelse(255, text) - .embed(width / 25, width / 25, width, text.height() + size, - VImage::option()->set("extend", "white")); - - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : 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, pageHeight + captionImage.height()); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" - ? VImage::option()->set("dither", 0)->set("reoptimise", 1) - : 0); - - return (char *)buf; -} +#include +#include +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap CaptionTwo(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + bool top = GetArgument(Arguments, "top"); + string caption = GetArgument(Arguments, "caption"); + string font = GetArgument(Arguments, "font"); + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + 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 pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + int textWidth = width - ((width / 25) * 2); + + string font_string = (font == "roboto" ? "Roboto Condensed" : font) + + ", Twemoji Color Emoji " + to_string(size); + + string captionText = "" + caption + ""; + + VImage text; + auto findResult = fontPaths.find(font); + if (findResult != fontPaths.end()) { + text = VImage::text( + ".", VImage::option()->set("fontfile", + (basePath + findResult->second).c_str())); + } + text = VImage::text( + captionText.c_str(), + VImage::option() + ->set("rgba", true) + ->set("font", font_string.c_str()) + ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) + ->set("align", VIPS_ALIGN_LOW) + ->set("width", textWidth)); + VImage captionImage = + ((text == (vector){0, 0, 0, 0}).bandand()) + .ifthenelse(255, text) + .embed(width / 25, width / 25, width, text.height() + size, + VImage::option()->set("extend", "white")); + + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : 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, pageHeight + captionImage.height()); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" + ? VImage::option()->set("dither", 0)->set("reoptimise", 1) + : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = final.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; +} diff --git a/natives/caption2.h b/natives/caption2.h index f20b0bb..aef3130 100644 --- a/natives/caption2.h +++ b/natives/caption2.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* CaptionTwo(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap CaptionTwo(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/circle.cc b/natives/circle.cc index 7a7bb34..17ea610 100644 --- a/natives/circle.cc +++ b/natives/circle.cc @@ -1,54 +1,60 @@ -#include - -#include -#include -#include -#include -#include - -#include "common.h" - -using namespace std; -using namespace Magick; - -char *Circle(string type, string *outType, char *BufferData, - size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, - size_t *DataSize) { - Blob blob; - - list frames; - list coalesced; - list blurred; - try { - readImages(&frames, Blob(BufferData, BufferLength)); - } 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) { - image.rotationalBlur(10); - image.magick(*outType); - blurred.push_back(image); - } - - optimizeTransparency(blurred.begin(), blurred.end()); - - if (*outType == "gif") { - for (Image &image : blurred) { - image.quantizeDitherMethod(FloydSteinbergDitherMethod); - image.quantize(); - } - } - - writeImages(blurred.begin(), blurred.end(), &blob); - - *DataSize = blob.length(); - - // workaround because the data is tied to the blob - char *data = (char *)malloc(*DataSize); - memcpy(data, blob.data(), *DataSize); - return data; +#include + +#include +#include +#include +#include +#include + +#include "common.h" + +using namespace std; +using namespace Magick; + +ArgumentMap Circle(string type, string *outType, char *BufferData, + size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, + size_t *DataSize) { + Blob blob; + + list frames; + list coalesced; + list blurred; + try { + readImages(&frames, Blob(BufferData, BufferLength)); + } 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) { + image.rotationalBlur(10); + image.magick(*outType); + blurred.push_back(image); + } + + optimizeTransparency(blurred.begin(), blurred.end()); + + if (*outType == "gif") { + for (Image &image : blurred) { + image.quantizeDitherMethod(FloydSteinbergDitherMethod); + image.quantize(); + } + } + + writeImages(blurred.begin(), blurred.end(), &blob); + + *DataSize = blob.length(); + + // workaround because the data is tied to the blob + char *data = (char *)malloc(*DataSize); + memcpy(data, blob.data(), *DataSize); + + ArgumentMap output; + output["buf"] = data; + output["width"] = (int)blurred.front().columns(); + output["height"] = (int)blurred.front().rows(); + + return output; } \ No newline at end of file diff --git a/natives/circle.h b/natives/circle.h index 580b5d3..6af9699 100644 --- a/natives/circle.h +++ b/natives/circle.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Circle(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Circle(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/cli/image.cc b/natives/cli/image.cc index 92fcd67..344d0bb 100644 --- a/natives/cli/image.cc +++ b/natives/cli/image.cc @@ -1,26 +1,26 @@ -#include -#include - -#include "../common.h" - -void showUsage(char *path) { - std::cout << "Usage: " << path << " operation [--arg=\"param\"] [...]" << std::endl; -} - -int main(int argc, char *argv[]) { - if (argc < 1 || - (argc == 1 && !strcmp(argv[1], "-h"))) { - showUsage(argv[0]); -#ifdef _WIN32 - system("PAUSE"); -#endif - return 1; - } - - char *op = argv[1]; - - //handleArguments(argc, argv); - - std::cout << "This does nothing yet, but it might in the future!" << std::endl; - return 0; +#include +#include + +#include "../common.h" + +void showUsage(char *path) { + std::cout << "Usage: " << path << " operation [--arg=\"param\"] [...]" << std::endl; +} + +int main(int argc, char *argv[]) { + if (argc < 1 || + (argc == 1 && !strcmp(argv[1], "-h"))) { + showUsage(argv[0]); +#ifdef _WIN32 + system("PAUSE"); +#endif + return 1; + } + + char *op = argv[1]; + + //handleArguments(argc, argv); + + std::cout << "This does nothing yet, but it might in the future!" << std::endl; + return 0; } \ No newline at end of file diff --git a/natives/colors.cc b/natives/colors.cc index d7b7b01..7f3536d 100644 --- a/natives/colors.cc +++ b/natives/colors.cc @@ -1,36 +1,41 @@ -#include -#include -#include - -#include "common.h" - -using namespace std; -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); - -char *Colors(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - string color = GetArgument(Arguments, "color"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - - VImage out; - - if (color == "grayscale") { - out = in.colourspace(VIPS_INTERPRETATION_B_W); - } else if (color == "sepia") { - out = in.extract_band(0, VImage::option()->set("n", 3)).recomb(sepia); - } - - void *buf; - out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); - - return (char *)buf; -} +#include +#include +#include + +#include "common.h" + +using namespace std; +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); + +ArgumentMap Colors(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + string color = GetArgument(Arguments, "color"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + + VImage out; + + if (color == "grayscale") { + out = in.colourspace(VIPS_INTERPRETATION_B_W); + } else if (color == "sepia") { + out = in.extract_band(0, VImage::option()->set("n", 3)).recomb(sepia); + } + + void *buf; + out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = out.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; +} diff --git a/natives/colors.h b/natives/colors.h index 4f8a50c..fef9e15 100644 --- a/natives/colors.h +++ b/natives/colors.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Colors(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Colors(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/common.h b/natives/common.h index b051984..0f09cb4 100644 --- a/natives/common.h +++ b/natives/common.h @@ -1,130 +1,130 @@ -#pragma once - -#include -#include -#include -#include - -using std::map; -using std::string; -using std::variant; - -typedef variant ArgumentVariant; -typedef map ArgumentMap; - -#include "blur.h" -#include "bounce.h" -#include "caption.h" -#include "caption2.h" -#include "circle.h" -#include "colors.h" -#include "crop.h" -#include "deepfry.h" -#include "explode.h" -#include "flag.h" -#include "flip.h" -#include "freeze.h" -#include "gamexplain.h" -#include "globe.h" -#include "homebrew.h" -#include "invert.h" -#include "jpeg.h" -#include "magik.h" -#include "meme.h" -#include "mirror.h" -#include "motivate.h" -#include "reddit.h" -#include "resize.h" -#include "reverse.h" -#include "scott.h" -#include "snapchat.h" -#include "sonic.h" -#include "speed.h" -#include "spin.h" -#include "squish.h" -#include "swirl.h" -#include "tile.h" -#include "togif.h" -#include "uncanny.h" -#include "uncaption.h" -#include "wall.h" -#include "watermark.h" -#include "whisper.h" -#include "zamn.h" - -template -T GetArgument(ArgumentMap map, string key) { - try { - return std::get(map.at(key)); - } catch (std::bad_variant_access&) { - throw "Invalid requested type from variant."; - } -} - -template -T GetArgumentWithFallback(ArgumentMap map, string key, T fallback) { - try { - return std::get(map.at(key)); - } catch (...) { // this is, not great... - return fallback; - } -} - -#define MAP_HAS(ARRAY, KEY) (ARRAY.count(KEY) > 0) -#define MAP_GET(ARRAY, KEY, TYPE) \ - (MAP_HAS(ARRAY, KEY) ? get(ARRAY.at(KEY)) \ - : NULL) // C++ has forced my hand -#define MAP_GET_FALLBACK(ARRAY, KEY, TYPE, FALLBACK) \ - (MAP_HAS(ARRAY, KEY) ? get(ARRAY.at(KEY)) : FALLBACK) - -#define ARG_TYPES std::variant - -const std::unordered_map fontPaths{ - {"futura", "assets/fonts/caption.otf"}, - {"helvetica", "assets/fonts/caption2.ttf"}, - {"roboto", "assets/fonts/reddit.ttf"}}; - -const std::map - FunctionMap = {{"blur", &Blur}, - {"bounce", &Bounce}, - {"caption", &Caption}, - {"captionTwo", &CaptionTwo}, - {"circle", &Circle}, - {"colors", &Colors}, - {"crop", &Crop}, - {"deepfry", &Deepfry}, - {"explode", &Explode}, - {"flag", &Flag}, - {"flip", &Flip}, - {"freeze", &Freeze}, - {"gamexplain", Gamexplain}, - {"globe", Globe}, - {"invert", Invert}, - {"jpeg", Jpeg}, - {"magik", Magik}, - {"meme", Meme}, - {"mirror", Mirror}, - {"motivate", Motivate}, - {"reddit", Reddit}, - {"resize", Resize}, - {"reverse", Reverse}, - {"scott", Scott}, - {"snapchat", Snapchat}, - {"speed", &Speed}, - {"spin", Spin}, - {"squish", Squish}, - {"swirl", Swirl}, - {"tile", Tile}, - {"togif", ToGif}, - {"uncanny", Uncanny}, - {"uncaption", &Uncaption}, - {"wall", Wall}, - {"watermark", &Watermark}, - {"whisper", Whisper}, - {"zamn", Zamn}}; - -const std::map +#pragma once + +#include +#include +#include +#include + +using std::map; +using std::string; +using std::variant; + +typedef variant ArgumentVariant; +typedef map ArgumentMap; + +#include "blur.h" +#include "bounce.h" +#include "caption.h" +#include "caption2.h" +#include "circle.h" +#include "colors.h" +#include "crop.h" +#include "deepfry.h" +#include "explode.h" +#include "flag.h" +#include "flip.h" +#include "freeze.h" +#include "gamexplain.h" +#include "globe.h" +#include "homebrew.h" +#include "invert.h" +#include "jpeg.h" +#include "magik.h" +#include "meme.h" +#include "mirror.h" +#include "motivate.h" +#include "reddit.h" +#include "resize.h" +#include "reverse.h" +#include "scott.h" +#include "snapchat.h" +#include "sonic.h" +#include "speed.h" +#include "spin.h" +#include "squish.h" +#include "swirl.h" +#include "tile.h" +#include "togif.h" +#include "uncanny.h" +#include "uncaption.h" +#include "wall.h" +#include "watermark.h" +#include "whisper.h" +#include "zamn.h" + +template +T GetArgument(ArgumentMap map, string key) { + try { + return std::get(map.at(key)); + } catch (std::bad_variant_access&) { + throw "Invalid requested type from variant."; + } +} + +template +T GetArgumentWithFallback(ArgumentMap map, string key, T fallback) { + try { + return std::get(map.at(key)); + } catch (...) { // this is, not great... + return fallback; + } +} + +#define MAP_HAS(ARRAY, KEY) (ARRAY.count(KEY) > 0) +#define MAP_GET(ARRAY, KEY, TYPE) \ + (MAP_HAS(ARRAY, KEY) ? get(ARRAY.at(KEY)) \ + : NULL) // C++ has forced my hand +#define MAP_GET_FALLBACK(ARRAY, KEY, TYPE, FALLBACK) \ + (MAP_HAS(ARRAY, KEY) ? get(ARRAY.at(KEY)) : FALLBACK) + +#define ARG_TYPES std::variant + +const std::unordered_map fontPaths{ + {"futura", "assets/fonts/caption.otf"}, + {"helvetica", "assets/fonts/caption2.ttf"}, + {"roboto", "assets/fonts/reddit.ttf"}}; + +const std::map + FunctionMap = {{"blur", &Blur}, + {"bounce", &Bounce}, + {"caption", &Caption}, + {"captionTwo", &CaptionTwo}, + {"circle", &Circle}, + {"colors", &Colors}, + {"crop", &Crop}, + {"deepfry", &Deepfry}, + {"explode", &Explode}, + {"flag", &Flag}, + {"flip", &Flip}, + {"freeze", &Freeze}, + {"gamexplain", Gamexplain}, + {"globe", Globe}, + {"invert", Invert}, + {"jpeg", Jpeg}, + {"magik", Magik}, + {"meme", Meme}, + {"mirror", Mirror}, + {"motivate", Motivate}, + {"reddit", Reddit}, + {"resize", Resize}, + {"reverse", Reverse}, + {"scott", Scott}, + {"snapchat", Snapchat}, + {"speed", &Speed}, + {"spin", Spin}, + {"squish", Squish}, + {"swirl", Swirl}, + {"tile", Tile}, + {"togif", ToGif}, + {"uncanny", Uncanny}, + {"uncaption", &Uncaption}, + {"wall", Wall}, + {"watermark", &Watermark}, + {"whisper", Whisper}, + {"zamn", Zamn}}; + +const std::map NoInputFunctionMap = {{"homebrew", Homebrew}, {"sonic", Sonic}}; \ No newline at end of file diff --git a/natives/crop.cc b/natives/crop.cc index df720ea..bf53f6b 100644 --- a/natives/crop.cc +++ b/natives/crop.cc @@ -1,51 +1,56 @@ -#include -#include -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Crop(string type, string *outType, char *BufferData, size_t BufferLength, - [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - - vector img; - int finalHeight = 0; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : 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); - } - - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, finalHeight); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" - ? VImage::option()->set("dither", 0)->set("reoptimise", 1) - : 0); - - return (char *)buf; +#include +#include +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Crop(string type, string *outType, char *BufferData, size_t BufferLength, + [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + + vector img; + int finalHeight = 0; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : 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); + } + + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, finalHeight); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" + ? VImage::option()->set("dither", 0)->set("reoptimise", 1) + : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = finalHeight; + + return output; } \ No newline at end of file diff --git a/natives/crop.h b/natives/crop.h index 4c5270c..3201641 100644 --- a/natives/crop.h +++ b/natives/crop.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Crop(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Crop(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/deepfry.cc b/natives/deepfry.cc index 4d2cda9..c695fff 100644 --- a/natives/deepfry.cc +++ b/natives/deepfry.cc @@ -1,61 +1,66 @@ -#include -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Deepfry(string type, string *outType, char *BufferData, - size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, - size_t *DataSize) { - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - - if (!in.has_alpha()) in = in.bandjoin(255); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int totalHeight = in.height(); - int nPages = vips_image_get_n_pages(in.get_image()); - - VImage fried = (in * 1.3 - (255.0 * 1.3 - 255.0)) * 1.5; - - VImage final; - if (totalHeight > 65500 && type == "gif") { - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); - void *jpgBuf; - size_t jpgLength; - img_frame.write_to_buffer( - ".jpg", &jpgBuf, &jpgLength, - VImage::option()->set("Q", 1)->set("strip", true)); - VImage jpeged = VImage::new_from_buffer(jpgBuf, jpgLength, ""); - jpeged.set(VIPS_META_PAGE_HEIGHT, pageHeight); - jpeged.set("delay", in.get_array_int("delay")); - img.push_back(jpeged); - } - final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, pageHeight); - } else { - void *jpgBuf; - size_t jpgLength; - fried.write_to_buffer(".jpg", &jpgBuf, &jpgLength, - VImage::option()->set("Q", 1)->set("strip", true)); - final = VImage::new_from_buffer(jpgBuf, jpgLength, ""); - final.set(VIPS_META_PAGE_HEIGHT, pageHeight); - if (type == "gif") final.set("delay", fried.get_array_int("delay")); - } - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" ? VImage::option()->set("dither", 0) : 0); - - return (char *)buf; +#include +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Deepfry(string type, string *outType, char *BufferData, + size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, + size_t *DataSize) { + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + + if (!in.has_alpha()) in = in.bandjoin(255); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int totalHeight = in.height(); + int nPages = vips_image_get_n_pages(in.get_image()); + + VImage fried = (in * 1.3 - (255.0 * 1.3 - 255.0)) * 1.5; + + VImage final; + if (totalHeight > 65500 && type == "gif") { + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); + void *jpgBuf; + size_t jpgLength; + img_frame.write_to_buffer( + ".jpg", &jpgBuf, &jpgLength, + VImage::option()->set("Q", 1)->set("strip", true)); + VImage jpeged = VImage::new_from_buffer(jpgBuf, jpgLength, ""); + jpeged.set(VIPS_META_PAGE_HEIGHT, pageHeight); + jpeged.set("delay", in.get_array_int("delay")); + img.push_back(jpeged); + } + final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight); + } else { + void *jpgBuf; + size_t jpgLength; + fried.write_to_buffer(".jpg", &jpgBuf, &jpgLength, + VImage::option()->set("Q", 1)->set("strip", true)); + final = VImage::new_from_buffer(jpgBuf, jpgLength, ""); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight); + if (type == "gif") final.set("delay", fried.get_array_int("delay")); + } + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" ? VImage::option()->set("dither", 0) : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = pageHeight; + + return output; } \ No newline at end of file diff --git a/natives/deepfry.h b/natives/deepfry.h index b7d9057..b9c0eb9 100644 --- a/natives/deepfry.h +++ b/natives/deepfry.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Deepfry(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Deepfry(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/explode.cc b/natives/explode.cc index 2df37e9..081a55b 100644 --- a/natives/explode.cc +++ b/natives/explode.cc @@ -1,52 +1,57 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Explode(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - bool implode = GetArgumentWithFallback(Arguments, "implode", false); - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option(); - - VImage in = - VImage::new_from_buffer( - BufferData, BufferLength, "", - 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 pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - - string distortPath = basePath + "assets/images/" + - (implode ? "linearimplode.png" : "linearexplode.png"); - VImage distort = - (VImage::new_from_file(distortPath.c_str()) - .resize(width / 500.0, VImage::option() - ->set("vscale", pageHeight / 500.0) - ->set("kernel", VIPS_KERNEL_CUBIC)) / - 65535); - - VImage distortImage = (distort[0] * width).bandjoin(distort[1] * pageHeight); - - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - VImage mapped = img_frame.mapim(distortImage); - img.push_back(mapped); - } - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, pageHeight); - - void *buf; - final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Explode(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + bool implode = GetArgumentWithFallback(Arguments, "implode", false); + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option(); + + VImage in = + VImage::new_from_buffer( + BufferData, BufferLength, "", + 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 pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + + string distortPath = basePath + "assets/images/" + + (implode ? "linearimplode.png" : "linearexplode.png"); + VImage distort = + (VImage::new_from_file(distortPath.c_str()) + .resize(width / 500.0, VImage::option() + ->set("vscale", pageHeight / 500.0) + ->set("kernel", VIPS_KERNEL_CUBIC)) / + 65535); + + VImage distortImage = (distort[0] * width).bandjoin(distort[1] * pageHeight); + + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + VImage mapped = img_frame.mapim(distortImage); + img.push_back(mapped); + } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight); + + void *buf; + final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = pageHeight; + + return output; } \ No newline at end of file diff --git a/natives/explode.h b/natives/explode.h index 2915bb1..b07bf74 100644 --- a/natives/explode.h +++ b/natives/explode.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Explode(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Explode(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/flag.cc b/natives/flag.cc index 4406b2f..9d26991 100644 --- a/natives/flag.cc +++ b/natives/flag.cc @@ -1,49 +1,54 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Flag(string type, string *outType, char *BufferData, size_t BufferLength, - ArgumentMap Arguments, size_t *DataSize) { - string overlay = GetArgument(Arguments, "overlay"); - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - - if (!in.has_alpha()) in = in.bandjoin(255); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - - string assetPath = basePath + overlay; - VImage overlayInput = VImage::new_from_file(assetPath.c_str()); - VImage overlayImage = overlayInput.resize( - (double)width / (double)overlayInput.width(), - VImage::option()->set( - "vscale", (double)pageHeight / (double)overlayInput.height())); - if (!overlayImage.has_alpha()) { - overlayImage = overlayImage.bandjoin(127); - } else { - // this is a pretty cool line, just saying - overlayImage = overlayImage * vector{1, 1, 1, 0.5}; - } - VImage replicated = overlayImage.replicate(1, nPages); - VImage final = in.composite2(replicated, VIPS_BLEND_MODE_OVER); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" - ? VImage::option()->set("dither", 0)->set("reoptimise", 1) - : 0); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Flag(string type, string *outType, char *BufferData, size_t BufferLength, + ArgumentMap Arguments, size_t *DataSize) { + string overlay = GetArgument(Arguments, "overlay"); + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + + if (!in.has_alpha()) in = in.bandjoin(255); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + + string assetPath = basePath + overlay; + VImage overlayInput = VImage::new_from_file(assetPath.c_str()); + VImage overlayImage = overlayInput.resize( + (double)width / (double)overlayInput.width(), + VImage::option()->set( + "vscale", (double)pageHeight / (double)overlayInput.height())); + if (!overlayImage.has_alpha()) { + overlayImage = overlayImage.bandjoin(127); + } else { + // this is a pretty cool line, just saying + overlayImage = overlayImage * vector{1, 1, 1, 0.5}; + } + VImage replicated = overlayImage.replicate(1, nPages); + VImage final = in.composite2(replicated, VIPS_BLEND_MODE_OVER); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" + ? VImage::option()->set("dither", 0)->set("reoptimise", 1) + : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = pageHeight; + + return output; } \ No newline at end of file diff --git a/natives/flag.h b/natives/flag.h index ff33d87..2e8569a 100644 --- a/natives/flag.h +++ b/natives/flag.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Flag(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Flag(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/flip.cc b/natives/flip.cc index 4c23e6c..7fbf485 100644 --- a/natives/flip.cc +++ b/natives/flip.cc @@ -1,48 +1,53 @@ -#include -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Flip(string type, string *outType, char *BufferData, size_t BufferLength, - ArgumentMap Arguments, size_t *DataSize) { - bool flop = GetArgument(Arguments, "flop"); - - VImage in = VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" - ? VImage::option()->set("n", -1)->set( - "access", "sequential") - : 0) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - 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 img; - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - for (int i = 0; i < nPages; i++) { - VImage img_frame = in.crop(0, i * pageHeight, in.width(), pageHeight); - 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, pageHeight); - } else { - out = in.flip(VIPS_DIRECTION_VERTICAL); - } - - void *buf; - out.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" - ? VImage::option()->set("dither", 0)->set("reoptimise", 1) - : 0); - - return (char *)buf; +#include +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Flip(string type, string *outType, char *BufferData, size_t BufferLength, + ArgumentMap Arguments, size_t *DataSize) { + bool flop = GetArgumentWithFallback(Arguments, "flop", false); + + VImage in = VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" + ? VImage::option()->set("n", -1)->set( + "access", "sequential") + : 0) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + 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 img; + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + for (int i = 0; i < nPages; i++) { + VImage img_frame = in.crop(0, i * pageHeight, in.width(), pageHeight); + 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, pageHeight); + } else { + out = in.flip(VIPS_DIRECTION_VERTICAL); + } + + void *buf; + out.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" + ? VImage::option()->set("dither", 0)->set("reoptimise", 1) + : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = out.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; } \ No newline at end of file diff --git a/natives/flip.h b/natives/flip.h index c3327c8..92ce7da 100644 --- a/natives/flip.h +++ b/natives/flip.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Flip(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Flip(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/freeze.cc b/natives/freeze.cc index 2fcb4a8..1395431 100644 --- a/natives/freeze.cc +++ b/natives/freeze.cc @@ -1,83 +1,107 @@ -#include -#include -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Freeze(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - bool loop = GetArgumentWithFallback(Arguments, "loop", false); - int frame = GetArgumentWithFallback(Arguments, "frame", -1); - - char *fileData = (char *)malloc(BufferLength); - memcpy(fileData, BufferData, BufferLength); - - char *match = (char *)"\x21\xFF\x0BNETSCAPE2.0\x03\x01"; - char *descriptor = (char *)"\x2C\x00\x00\x00\x00"; - char *lastPos; - - bool none = true; - - if (loop) { - char *newData = (char *)malloc(BufferLength + 19); - memcpy(newData, fileData, BufferLength); - lastPos = (char *)memchr(newData, '\x2C', BufferLength); - while (lastPos != NULL) { - if (memcmp(lastPos, descriptor, 5) != 0) { - lastPos = (char *)memchr(lastPos + 1, '\x2C', - (BufferLength - (lastPos - newData)) - 1); - continue; - } - - memcpy(lastPos + 19, lastPos, (BufferLength - (lastPos - newData))); - memcpy(lastPos, match, 16); - memcpy(lastPos + 16, "\x00\x00\x00", 3); - - none = false; - *DataSize = BufferLength + 19; - break; - } - if (none) *DataSize = BufferLength; - - return newData; - } else if (frame >= 0 && !loop) { - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - int framePos = clamp(frame, 0, (int)nPages); - VImage out = in.crop(0, 0, in.width(), pageHeight * (framePos + 1)); - out.set(VIPS_META_PAGE_HEIGHT, pageHeight); - out.set("loop", 1); - - void *buf; - out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); - - return (char *)buf; - } else { - lastPos = (char *)memchr(fileData, '\x21', BufferLength); - while (lastPos != NULL) { - if (memcmp(lastPos, match, 16) != 0) { - lastPos = (char *)memchr(lastPos + 1, '\x21', - (BufferLength - (lastPos - fileData)) - 1); - continue; - } - memcpy(lastPos, lastPos + 19, (BufferLength - (lastPos - fileData)) - 19); - *DataSize = BufferLength - 19; - none = false; - break; - } - if (none) *DataSize = BufferLength; - - return fileData; - } -} +#include +#include +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Freeze(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + bool loop = GetArgumentWithFallback(Arguments, "loop", false); + int frame = GetArgumentWithFallback(Arguments, "frame", -1); + + char *fileData = (char *)malloc(BufferLength); + memcpy(fileData, BufferData, BufferLength); + + char *match = (char *)"\x21\xFF\x0BNETSCAPE2.0\x03\x01"; + char *descriptor = (char *)"\x2C\x00\x00\x00\x00"; + char *lastPos; + + bool none = true; + + if (loop) { + char *newData = (char *)malloc(BufferLength + 19); + memcpy(newData, fileData, BufferLength); + lastPos = (char *)memchr(newData, '\x2C', BufferLength); + while (lastPos != NULL) { + if (memcmp(lastPos, descriptor, 5) != 0) { + lastPos = (char *)memchr(lastPos + 1, '\x2C', + (BufferLength - (lastPos - newData)) - 1); + continue; + } + + memcpy(lastPos + 19, lastPos, (BufferLength - (lastPos - newData))); + memcpy(lastPos, match, 16); + memcpy(lastPos + 16, "\x00\x00\x00", 3); + + none = false; + *DataSize = BufferLength + 19; + break; + } + if (none) *DataSize = BufferLength; + + VImage in = + VImage::new_from_buffer(newData, *DataSize, "", + VImage::option()->set("access", "sequential")); + + ArgumentMap output; + output["buf"] = newData; + output["width"] = in.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; + } else if (frame >= 0 && !loop) { + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + int framePos = clamp(frame, 0, (int)nPages); + VImage out = in.crop(0, 0, in.width(), pageHeight * (framePos + 1)); + out.set(VIPS_META_PAGE_HEIGHT, pageHeight); + out.set("loop", 1); + + void *buf; + out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = out.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; + + } else { + lastPos = (char *)memchr(fileData, '\x21', BufferLength); + while (lastPos != NULL) { + if (memcmp(lastPos, match, 16) != 0) { + lastPos = (char *)memchr(lastPos + 1, '\x21', + (BufferLength - (lastPos - fileData)) - 1); + continue; + } + memcpy(lastPos, lastPos + 19, (BufferLength - (lastPos - fileData)) - 19); + *DataSize = BufferLength - 19; + none = false; + break; + } + if (none) *DataSize = BufferLength; + + VImage in = + VImage::new_from_buffer(fileData, *DataSize, "", + VImage::option()->set("access", "sequential")); + + ArgumentMap output; + output["buf"] = fileData; + output["width"] = in.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; + } +} diff --git a/natives/freeze.h b/natives/freeze.h index 7c3d7ef..61454ef 100644 --- a/natives/freeze.h +++ b/natives/freeze.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Freeze(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Freeze(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/gamexplain.cc b/natives/gamexplain.cc index c2d9986..5cf10ab 100644 --- a/natives/gamexplain.cc +++ b/natives/gamexplain.cc @@ -1,50 +1,55 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Gamexplain(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - string assetPath = basePath + "assets/images/gamexplain.png"; - VImage tmpl = VImage::new_from_file(assetPath.c_str()); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - VImage resized = - img_frame - .resize(1181.0 / (double)width, - VImage::option()->set("vscale", 571.0 / (double)pageHeight)) - .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); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" - ? VImage::option()->set("dither", 0)->set("reoptimise", 1) - : 0); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Gamexplain(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + string assetPath = basePath + "assets/images/gamexplain.png"; + VImage tmpl = VImage::new_from_file(assetPath.c_str()); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + VImage resized = + img_frame + .resize(1181.0 / (double)width, + VImage::option()->set("vscale", 571.0 / (double)pageHeight)) + .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); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" + ? VImage::option()->set("dither", 0)->set("reoptimise", 1) + : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = final.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; } \ No newline at end of file diff --git a/natives/gamexplain.h b/natives/gamexplain.h index d4a113a..0822ad1 100644 --- a/natives/gamexplain.h +++ b/natives/gamexplain.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Gamexplain(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Gamexplain(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/globe.cc b/natives/globe.cc index 025252b..db1ff04 100644 --- a/natives/globe.cc +++ b/natives/globe.cc @@ -1,73 +1,78 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Globe(string type, string *outType, char *BufferData, size_t BufferLength, - ArgumentMap Arguments, size_t *DataSize) { - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option(); - - VImage in = - VImage::new_from_buffer( - BufferData, BufferLength, "", - 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 pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30; - - double size = min(width, pageHeight); - - 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 img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - VImage resized = img_frame.resize( - size / (double)width, - VImage::option()->set("vscale", size / (double)pageHeight)); - VImage rolled = img_frame.wrap( - VImage::option()->set("x", width * i / nPages)->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); - } - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, size); - if (type != "gif") { - vector delay(30, 50); - final.set("delay", delay); - } - - void *buf; - final.write_to_buffer(".gif", &buf, DataSize); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Globe(string type, string *outType, char *BufferData, size_t BufferLength, + ArgumentMap Arguments, size_t *DataSize) { + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option(); + + VImage in = + VImage::new_from_buffer( + BufferData, BufferLength, "", + 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 pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30; + + double size = min(width, pageHeight); + + 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 img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + VImage resized = img_frame.resize( + size / (double)width, + VImage::option()->set("vscale", size / (double)pageHeight)); + VImage rolled = img_frame.wrap( + VImage::option()->set("x", width * i / nPages)->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); + } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, size); + if (type != "gif") { + vector delay(30, 50); + final.set("delay", delay); + } + + void *buf; + final.write_to_buffer(".gif", &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = final.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; } \ No newline at end of file diff --git a/natives/globe.h b/natives/globe.h index f51022c..45689e0 100644 --- a/natives/globe.h +++ b/natives/globe.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Globe(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Globe(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/homebrew.cc b/natives/homebrew.cc index 503e42f..b31af3b 100644 --- a/natives/homebrew.cc +++ b/natives/homebrew.cc @@ -1,37 +1,42 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Homebrew(string type, string *outType, ArgumentMap Arguments, - size_t *DataSize) { - string caption = GetArgument(Arguments, "caption"); - string basePath = GetArgument(Arguments, "basePath"); - - string assetPath = basePath + "assets/images/hbc.png"; - VImage bg = VImage::new_from_file(assetPath.c_str()); - - VImage text = VImage::text( - ".", VImage::option()->set("fontfile", - (basePath + "assets/fonts/hbc.ttf").c_str())); - text = VImage::text( - ("" + caption + "") - .c_str(), - VImage::option() - ->set("rgba", true) - ->set("align", VIPS_ALIGN_CENTRE) - ->set("font", "PF Square Sans Pro, Twemoji Color Font 96") - ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())); - - 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; - out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Homebrew(string type, string *outType, ArgumentMap Arguments, + size_t *DataSize) { + string caption = GetArgument(Arguments, "caption"); + string basePath = GetArgument(Arguments, "basePath"); + + string assetPath = basePath + "assets/images/hbc.png"; + VImage bg = VImage::new_from_file(assetPath.c_str()); + + VImage text = VImage::text( + ".", VImage::option()->set("fontfile", + (basePath + "assets/fonts/hbc.ttf").c_str())); + text = VImage::text( + ("" + caption + "") + .c_str(), + VImage::option() + ->set("rgba", true) + ->set("align", VIPS_ALIGN_CENTRE) + ->set("font", "PF Square Sans Pro, Twemoji Color Font 96") + ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str())); + + 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; + out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = bg.width(); + output["height"] = vips_image_get_page_height(bg.get_image()); + + return output; } \ No newline at end of file diff --git a/natives/homebrew.h b/natives/homebrew.h index 61bbb10..e95baf0 100644 --- a/natives/homebrew.h +++ b/natives/homebrew.h @@ -1,7 +1,7 @@ -#pragma once - -#include "common.h" - -using std::string; - -char *Homebrew(string type, string *outType, ArgumentMap Arguments, size_t *DataSize); \ No newline at end of file +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Homebrew(string type, string *outType, ArgumentMap Arguments, size_t *DataSize); \ No newline at end of file diff --git a/natives/invert.cc b/natives/invert.cc index 570428c..24ec1a5 100644 --- a/natives/invert.cc +++ b/natives/invert.cc @@ -1,28 +1,33 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Invert(string type, string *outType, char *BufferData, - size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, - size_t *DataSize) { - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - 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)); - - void *buf; - out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Invert(string type, string *outType, char *BufferData, + size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, + size_t *DataSize) { + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + 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)); + + void *buf; + out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = out.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; } \ No newline at end of file diff --git a/natives/invert.h b/natives/invert.h index 84dd3d4..8f5ea69 100644 --- a/natives/invert.h +++ b/natives/invert.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Invert(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Invert(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/jpeg.cc b/natives/jpeg.cc index 5133014..e7eee07 100644 --- a/natives/jpeg.cc +++ b/natives/jpeg.cc @@ -1,75 +1,83 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Jpeg(string type, string *outType, char *BufferData, size_t BufferLength, - ArgumentMap Arguments, size_t *DataSize) { - int quality = GetArgumentWithFallback(Arguments, "quality", 0); - - void *buf; - - if (type == "gif") { - VImage in = VImage::new_from_buffer( - BufferData, BufferLength, "", - VImage::option()->set("access", "sequential")->set("n", -1)) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int totalHeight = in.height(); - int nPages = vips_image_get_n_pages(in.get_image()); - - VImage final; - - if (totalHeight > 65500) { - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); - void *jpgBuf; - size_t jpgLength; - img_frame.write_to_buffer( - ".jpg", &jpgBuf, &jpgLength, - VImage::option()->set("Q", quality)->set("strip", true)); - VImage jpeged = VImage::new_from_buffer(jpgBuf, jpgLength, ""); - jpeged.set(VIPS_META_PAGE_HEIGHT, pageHeight); - jpeged.set("delay", in.get_array_int("delay")); - img.push_back(jpeged); - } - final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, pageHeight); - } else { - void *jpgBuf; - size_t jpgLength; - in.write_to_buffer( - ".jpg", &jpgBuf, &jpgLength, - VImage::option()->set("Q", quality)->set("strip", true)); - final = VImage::new_from_buffer(jpgBuf, jpgLength, ""); - final.set(VIPS_META_PAGE_HEIGHT, pageHeight); - final.set("delay", in.get_array_int("delay")); - } - - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" ? VImage::option()->set("dither", 0) : 0); - } else { - VImage in = VImage::new_from_buffer(BufferData, BufferLength, ""); - void *jpgBuf; - in.write_to_buffer(".jpg", &jpgBuf, DataSize, - VImage::option()->set("Q", quality)->set("strip", true)); - if (*outType == "gif") { - VImage gifIn = VImage::new_from_buffer((char *)jpgBuf, *DataSize, ""); - gifIn.write_to_buffer( - ".gif", &buf, DataSize, - VImage::option()->set("Q", quality)->set("strip", true)); - } else { - *outType = "jpg"; - buf = jpgBuf; - } - } - - return (char *)buf; -} +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Jpeg(string type, string *outType, char *BufferData, size_t BufferLength, + ArgumentMap Arguments, size_t *DataSize) { + int quality = GetArgumentWithFallback(Arguments, "quality", 0); + + void *buf; + ArgumentMap output; + + + if (type == "gif") { + VImage in = VImage::new_from_buffer( + BufferData, BufferLength, "", + VImage::option()->set("access", "sequential")->set("n", -1)) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int totalHeight = in.height(); + int nPages = vips_image_get_n_pages(in.get_image()); + + VImage final; + + if (totalHeight > 65500) { + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); + void *jpgBuf; + size_t jpgLength; + img_frame.write_to_buffer( + ".jpg", &jpgBuf, &jpgLength, + VImage::option()->set("Q", quality)->set("strip", true)); + VImage jpeged = VImage::new_from_buffer(jpgBuf, jpgLength, ""); + jpeged.set(VIPS_META_PAGE_HEIGHT, pageHeight); + jpeged.set("delay", in.get_array_int("delay")); + img.push_back(jpeged); + } + final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight); + } else { + void *jpgBuf; + size_t jpgLength; + in.write_to_buffer( + ".jpg", &jpgBuf, &jpgLength, + VImage::option()->set("Q", quality)->set("strip", true)); + final = VImage::new_from_buffer(jpgBuf, jpgLength, ""); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight); + final.set("delay", in.get_array_int("delay")); + } + + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" ? VImage::option()->set("dither", 0) : 0); + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = pageHeight; + } else { + VImage in = VImage::new_from_buffer(BufferData, BufferLength, ""); + void *jpgBuf; + in.write_to_buffer(".jpg", &jpgBuf, DataSize, + VImage::option()->set("Q", quality)->set("strip", true)); + if (*outType == "gif") { + VImage gifIn = VImage::new_from_buffer((char *)jpgBuf, *DataSize, ""); + gifIn.write_to_buffer( + ".gif", &buf, DataSize, + VImage::option()->set("Q", quality)->set("strip", true)); + } else { + *outType = "jpg"; + buf = jpgBuf; + } + output["buf"] = (char *)buf; + output["width"] = in.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + } + + return output; +} diff --git a/natives/jpeg.h b/natives/jpeg.h index 7282133..0b12800 100644 --- a/natives/jpeg.h +++ b/natives/jpeg.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Jpeg(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Jpeg(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/magik.cc b/natives/magik.cc index 92e7ba1..693fbce 100644 --- a/natives/magik.cc +++ b/natives/magik.cc @@ -1,52 +1,58 @@ -#include - -#include -#include -#include - -#include "common.h" - -using namespace std; -using namespace Magick; - -char *Magik(string type, string *outType, char *BufferData, size_t BufferLength, - [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { - Blob blob; - - list frames; - list coalesced; - list blurred; - try { - readImages(&frames, Blob(BufferData, BufferLength)); - } 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) { - image.scale(Geometry("350x350")); - image.liquidRescale(Geometry("175x175")); - image.liquidRescale(Geometry("350x350")); - image.magick(*outType); - blurred.push_back(image); - } - - optimizeTransparency(blurred.begin(), blurred.end()); - - if (*outType == "gif") { - for (Image &image : blurred) { - image.quantizeDitherMethod(FloydSteinbergDitherMethod); - image.quantize(); - } - } - - writeImages(blurred.begin(), blurred.end(), &blob); - - *DataSize = blob.length(); - - char *data = (char *)malloc(*DataSize); - memcpy(data, blob.data(), *DataSize); - return data; +#include + +#include +#include +#include + +#include "common.h" + +using namespace std; +using namespace Magick; + +ArgumentMap Magik(string type, string *outType, char *BufferData, size_t BufferLength, + [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { + Blob blob; + + list frames; + list coalesced; + list blurred; + try { + readImages(&frames, Blob(BufferData, BufferLength)); + } 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) { + image.scale(Geometry("350x350")); + image.liquidRescale(Geometry("175x175")); + image.liquidRescale(Geometry("350x350")); + image.magick(*outType); + blurred.push_back(image); + } + + optimizeTransparency(blurred.begin(), blurred.end()); + + if (*outType == "gif") { + for (Image &image : blurred) { + image.quantizeDitherMethod(FloydSteinbergDitherMethod); + image.quantize(); + } + } + + writeImages(blurred.begin(), blurred.end(), &blob); + + *DataSize = blob.length(); + + char *data = (char *)malloc(*DataSize); + memcpy(data, blob.data(), *DataSize); + + ArgumentMap output; + output["buf"] = data; + output["width"] = (int)blurred.front().columns(); + output["height"] = (int)blurred.front().rows(); + + return output; } \ No newline at end of file diff --git a/natives/magik.h b/natives/magik.h index 19dc463..b7831f3 100644 --- a/natives/magik.h +++ b/natives/magik.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Magik(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Magik(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/meme.cc b/natives/meme.cc index 850576d..a0dd634 100644 --- a/natives/meme.cc +++ b/natives/meme.cc @@ -1,139 +1,144 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Meme(string type, string *outType, char *BufferData, size_t BufferLength, - ArgumentMap Arguments, size_t *DataSize) { - string top = GetArgument(Arguments, "top"); - string bottom = GetArgument(Arguments, "bottom"); - string font = GetArgument(Arguments, "font"); - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - int size = width / 9; - int dividedWidth = width / 1000; - int rad = 1; - vector zeroVec = {0, 0, 0, 0}; - - string font_string = - (font == "roboto" ? "Roboto Condensed" : font) + ", Twemoji Color Font " + - (font != "impact" ? "bold" : "normal") + " " + to_string(size); - - 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)); - } - - auto findResult = fontPaths.find(font); - if (findResult != fontPaths.end()) { - VImage::text(".", VImage::option()->set( - "fontfile", (basePath + findResult->second).c_str())); - } - - VImage topText; - if (top != "") { - VImage topIn = VImage::text( - ("" + top + "").c_str(), - VImage::option() - ->set("rgba", true) - ->set("align", VIPS_ALIGN_CENTRE) - ->set("font", font_string.c_str()) - ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) - ->set("width", width)); - - topIn = topIn.embed(rad + 10, rad + 10, (topIn.width() + 2 * rad) + 20, - (topIn.height() + 2 * rad) + 20); - - 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 == zeroVec); - 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); - } - - VImage bottomText; - if (bottom != "") { - VImage bottomIn = VImage::text( - ("" + bottom + "").c_str(), - VImage::option() - ->set("rgba", true) - ->set("align", VIPS_ALIGN_CENTRE) - ->set("font", font_string.c_str()) - ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").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 == zeroVec); - 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 img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : 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", pageHeight - bottomText.height())); - } - img.push_back(img_frame); - } - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, pageHeight); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" - ? VImage::option()->set("dither", 0)->set("reoptimise", 1) - : 0); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Meme(string type, string *outType, char *BufferData, size_t BufferLength, + ArgumentMap Arguments, size_t *DataSize) { + string top = GetArgument(Arguments, "top"); + string bottom = GetArgument(Arguments, "bottom"); + string font = GetArgument(Arguments, "font"); + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + int size = width / 9; + int dividedWidth = width / 1000; + int rad = 1; + vector zeroVec = {0, 0, 0, 0}; + + string font_string = + (font == "roboto" ? "Roboto Condensed" : font) + ", Twemoji Color Font " + + (font != "impact" ? "bold" : "normal") + " " + to_string(size); + + 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)); + } + + auto findResult = fontPaths.find(font); + if (findResult != fontPaths.end()) { + VImage::text(".", VImage::option()->set( + "fontfile", (basePath + findResult->second).c_str())); + } + + VImage topText; + if (top != "") { + VImage topIn = VImage::text( + ("" + top + "").c_str(), + VImage::option() + ->set("rgba", true) + ->set("align", VIPS_ALIGN_CENTRE) + ->set("font", font_string.c_str()) + ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) + ->set("width", width)); + + topIn = topIn.embed(rad + 10, rad + 10, (topIn.width() + 2 * rad) + 20, + (topIn.height() + 2 * rad) + 20); + + 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 == zeroVec); + 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); + } + + VImage bottomText; + if (bottom != "") { + VImage bottomIn = VImage::text( + ("" + bottom + "").c_str(), + VImage::option() + ->set("rgba", true) + ->set("align", VIPS_ALIGN_CENTRE) + ->set("font", font_string.c_str()) + ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").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 == zeroVec); + 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 img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : 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", pageHeight - bottomText.height())); + } + img.push_back(img_frame); + } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" + ? VImage::option()->set("dither", 0)->set("reoptimise", 1) + : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = pageHeight; + + return output; } \ No newline at end of file diff --git a/natives/meme.h b/natives/meme.h index 2071331..94d8b6a 100644 --- a/natives/meme.h +++ b/natives/meme.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Meme(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Meme(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/mirror.cc b/natives/mirror.cc index a90efe3..4ffc6e0 100644 --- a/natives/mirror.cc +++ b/natives/mirror.cc @@ -1,64 +1,69 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Mirror(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - bool vertical = GetArgumentWithFallback(Arguments, "vertical", false); - bool first = GetArgumentWithFallback(Arguments, "first", false); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - VImage out; - - if (vertical) { - if (type == "gif") { - // once again, libvips gif handling is both a blessing and a curse - vector img; - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - bool isOdd = pageHeight % 2; - for (int i = 0; i < nPages; i++) { - int x = (i * pageHeight) + (first ? 0 : (pageHeight / 2)); - VImage cropped = in.crop(0, x, in.width(), pageHeight / 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, pageHeight - (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 { - if (first) { - 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 { - 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}); - } - } - - void *buf; - out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); - - return (char *)buf; -} +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Mirror(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + bool vertical = GetArgumentWithFallback(Arguments, "vertical", false); + bool first = GetArgumentWithFallback(Arguments, "first", false); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + VImage out; + + if (vertical) { + if (type == "gif") { + // once again, libvips gif handling is both a blessing and a curse + vector img; + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + bool isOdd = pageHeight % 2; + for (int i = 0; i < nPages; i++) { + int x = (i * pageHeight) + (first ? 0 : (pageHeight / 2)); + VImage cropped = in.crop(0, x, in.width(), pageHeight / 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, pageHeight - (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 { + if (first) { + 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 { + 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}); + } + } + + void *buf; + out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = in.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; +} diff --git a/natives/mirror.h b/natives/mirror.h index 6501394..5dd3a4f 100644 --- a/natives/mirror.h +++ b/natives/mirror.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Mirror(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Mirror(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/motivate.cc b/natives/motivate.cc index fdb8de3..242ef6d 100644 --- a/natives/motivate.cc +++ b/natives/motivate.cc @@ -1,123 +1,128 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Motivate(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - string top_text = GetArgument(Arguments, "top"); - string bottom_text = GetArgument(Arguments, "bottom"); - string font = GetArgument(Arguments, "font"); - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - 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 / 5; - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - int textWidth = width - ((width / 25) * 2); - - string font_string = - (font == "roboto" ? "Roboto Condensed" : font) + ", Twemoji Color Font"; - - auto findResult = fontPaths.find(font); - if (findResult != fontPaths.end()) { - VImage::text(".", VImage::option()->set( - "fontfile", (basePath + findResult->second).c_str())); - } - - VImage topImage; - if (top_text != "") { - string topText = "" + - top_text + ""; - - 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("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) - ->set("width", textWidth)); - } - - VImage bottomImage; - if (bottom_text != "") { - string bottomText = "" + - bottom_text + ""; - - 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("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) - ->set("width", textWidth)); - } - - vector img; - int height; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - - int borderSize = max(2, width / 66); - int borderSize2 = borderSize * 0.5; - VImage bordered = - img_frame.embed(borderSize, borderSize, width + (borderSize * 2), - pageHeight + (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")); - - int addition = width / 8; - int sideAddition = pageHeight * 0.4; - - VImage bordered3 = bordered2.embed( - sideAddition / 2, addition / 2, bordered2.width() + sideAddition, - bordered2.height() + addition, - VImage::option()->set("extend", "black")); - VImage frame; - if (top_text != "") { - 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 != "") { - if (top_text == "") frame = bordered3; - 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); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" ? VImage::option()->set("dither", 1) : 0); - - return (char *)buf; -} +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Motivate(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + string top_text = GetArgument(Arguments, "top"); + string bottom_text = GetArgument(Arguments, "bottom"); + string font = GetArgument(Arguments, "font"); + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + 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 / 5; + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + int textWidth = width - ((width / 25) * 2); + + string font_string = + (font == "roboto" ? "Roboto Condensed" : font) + ", Twemoji Color Font"; + + auto findResult = fontPaths.find(font); + if (findResult != fontPaths.end()) { + VImage::text(".", VImage::option()->set( + "fontfile", (basePath + findResult->second).c_str())); + } + + VImage topImage; + if (top_text != "") { + string topText = "" + + top_text + ""; + + 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("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) + ->set("width", textWidth)); + } + + VImage bottomImage; + if (bottom_text != "") { + string bottomText = "" + + bottom_text + ""; + + 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("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) + ->set("width", textWidth)); + } + + vector img; + int height; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + + int borderSize = max(2, width / 66); + int borderSize2 = borderSize * 0.5; + VImage bordered = + img_frame.embed(borderSize, borderSize, width + (borderSize * 2), + pageHeight + (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")); + + int addition = width / 8; + int sideAddition = pageHeight * 0.4; + + VImage bordered3 = bordered2.embed( + sideAddition / 2, addition / 2, bordered2.width() + sideAddition, + bordered2.height() + addition, + VImage::option()->set("extend", "black")); + VImage frame; + if (top_text != "") { + 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 != "") { + if (top_text == "") frame = bordered3; + 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); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" ? VImage::option()->set("dither", 1) : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = in.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; +} diff --git a/natives/motivate.h b/natives/motivate.h index 422b0f7..ac81eb7 100644 --- a/natives/motivate.h +++ b/natives/motivate.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Motivate(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Motivate(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/node/image.cc b/natives/node/image.cc index 75613f3..5bee5e9 100644 --- a/natives/node/image.cc +++ b/natives/node/image.cc @@ -1,124 +1,130 @@ -#include - -#include -#include -#include - -#include "../common.h" - -#ifdef _WIN32 -#include -#endif -#include - -using namespace std; - -bool isNapiValueInt(Napi::Env& env, Napi::Value& num) { - return env.Global() - .Get("Number") - .ToObject() - .Get("isInteger") - .As() - .Call({num}) - .ToBoolean() - .Value(); -} - -Napi::Value ProcessImage(const Napi::CallbackInfo& info) { - Napi::Env env = info.Env(); - Napi::Object result = Napi::Object::New(env); - - try { - string command = info[0].As().Utf8Value(); - Napi::Object obj = info[1].As(); - string type = - obj.Has("type") ? obj.Get("type").As().Utf8Value() : "png"; - - Napi::Array properties = obj.GetPropertyNames(); - - ArgumentMap Arguments; - - for (unsigned int i = 0; i < properties.Length(); i++) { - string property = - properties.Get(uint32_t(i)).As().Utf8Value(); - - if (property == "data") { - continue; - } - - auto val = obj.Get(property); - if (val.IsBoolean()) { - Arguments[property] = val.ToBoolean().Value(); - } else if (val.IsString()) { - Arguments[property] = val.ToString().As().Utf8Value(); - } else if (val.IsNumber()) { - auto num = val.ToNumber(); - if (isNapiValueInt(env, num)) { - Arguments[property] = num.Int32Value(); - } else { - Arguments[property] = num.FloatValue(); - } - } else { - throw "Unimplemented value type passed to image native."; - // Arguments[property] = val; - } - } - - string outType = GetArgument(Arguments, "togif") ? "gif" : type; - - size_t length = 0; - char* buf; - if (obj.Has("data")) { - Napi::Buffer data = obj.Has("data") - ? obj.Get("data").As>() - : Napi::Buffer::New(env, 0); - buf = FunctionMap.at(command)(type, &outType, data.Data(), data.Length(), - Arguments, &length); - } else { - buf = NoInputFunctionMap.at(command)(type, &outType, Arguments, &length); - } - - vips_error_clear(); - vips_thread_shutdown(); - - result.Set("data", - Napi::Buffer::New(env, buf, length, - []([[maybe_unused]] Napi::Env env, - void* data) { free(data); })); - result.Set("type", outType); - } catch (std::exception const& err) { - Napi::Error::New(env, err.what()).ThrowAsJavaScriptException(); - } catch (...) { - Napi::Error::New(env, "Unknown error").ThrowAsJavaScriptException(); - } - - return result; -} - -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, "image"), - Napi::Function::New(env, ProcessImage)); // new function handler - - Napi::Array arr = Napi::Array::New(env); - size_t i = 0; - for (auto const& imap : FunctionMap) { - Napi::HandleScope scope(env); - arr[i] = Napi::String::New(env, imap.first); - i++; - } - for (auto const& imap : NoInputFunctionMap) { - Napi::HandleScope scope(env); - arr[i] = Napi::String::New(env, imap.first); - i++; - } - - exports.Set(Napi::String::New(env, "funcs"), arr); - - return exports; -} - -NODE_API_MODULE(addon, Init) +#include + +#include +#include +#include + +#include "../common.h" + +#ifdef _WIN32 +#include +#endif +#include + +using namespace std; + +bool isNapiValueInt(Napi::Env& env, Napi::Value& num) { + return env.Global() + .Get("Number") + .ToObject() + .Get("isInteger") + .As() + .Call({num}) + .ToBoolean() + .Value(); +} + +Napi::Value ProcessImage(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::Object result = Napi::Object::New(env); + + try { + string command = info[0].As().Utf8Value(); + Napi::Object obj = info[1].As(); + string type = + obj.Has("type") ? obj.Get("type").As().Utf8Value() : "png"; + + Napi::Array properties = obj.GetPropertyNames(); + + ArgumentMap Arguments; + + for (unsigned int i = 0; i < properties.Length(); i++) { + string property = + properties.Get(uint32_t(i)).As().Utf8Value(); + + if (property == "data") { + continue; + } + + auto val = obj.Get(property); + if (val.IsBoolean()) { + Arguments[property] = val.ToBoolean().Value(); + } else if (val.IsString()) { + Arguments[property] = val.ToString().As().Utf8Value(); + } else if (val.IsNumber()) { + auto num = val.ToNumber(); + if (isNapiValueInt(env, num)) { + Arguments[property] = num.Int32Value(); + } else { + Arguments[property] = num.FloatValue(); + } + } else { + throw "Unimplemented value type passed to image native."; + // Arguments[property] = val; + } + } + + string outType = GetArgument(Arguments, "togif") ? "gif" : type; + + size_t length = 0; + ArgumentMap outMap; + if (obj.Has("data")) { + Napi::Buffer data = obj.Has("data") + ? obj.Get("data").As>() + : Napi::Buffer::New(env, 0); + outMap = FunctionMap.at(command)(type, &outType, data.Data(), data.Length(), + Arguments, &length); + } else { + outMap = NoInputFunctionMap.at(command)(type, &outType, Arguments, &length); + } + + vips_error_clear(); + vips_thread_shutdown(); + + char* buf = GetArgument(outMap, "buf"); + int width = GetArgument(outMap, "width"); + int height = GetArgument(outMap, "height"); + + result.Set("data", + Napi::Buffer::New(env, buf, length, + []([[maybe_unused]] Napi::Env env, + void* data) { free(data); })); + result.Set("type", outType); + result.Set("width", width); + result.Set("height", height); + } catch (std::exception const& err) { + Napi::Error::New(env, err.what()).ThrowAsJavaScriptException(); + } catch (...) { + Napi::Error::New(env, "Unknown error").ThrowAsJavaScriptException(); + } + + return result; +} + +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, "image"), + Napi::Function::New(env, ProcessImage)); // new function handler + + Napi::Array arr = Napi::Array::New(env); + size_t i = 0; + for (auto const& imap : FunctionMap) { + Napi::HandleScope scope(env); + arr[i] = Napi::String::New(env, imap.first); + i++; + } + for (auto const& imap : NoInputFunctionMap) { + Napi::HandleScope scope(env); + arr[i] = Napi::String::New(env, imap.first); + i++; + } + + exports.Set(Napi::String::New(env, "funcs"), arr); + + return exports; +} + +NODE_API_MODULE(addon, Init) diff --git a/natives/reddit.cc b/natives/reddit.cc index a09027f..ca20d61 100644 --- a/natives/reddit.cc +++ b/natives/reddit.cc @@ -1,67 +1,72 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Reddit(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - string text = GetArgumentWithFallback(Arguments, "text", ""); - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - 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 pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - - string captionText = "" + text + ""; - - VImage textImage = VImage::text( - ".", VImage::option()->set( - "fontfile", (basePath + "assets/fonts/reddit.ttf").c_str())); - textImage = VImage::text( - captionText.c_str(), - VImage::option() - ->set("rgba", true) - ->set("font", "Roboto, Twemoji Color Font 62") - ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) - ->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 img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : 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, pageHeight + watermark.height()); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" - ? VImage::option()->set("dither", 0)->set("reoptimise", 1) - : 0); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Reddit(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + string text = GetArgument(Arguments, "caption"); + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + 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 pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + + string captionText = "" + text + ""; + + VImage textImage = VImage::text( + ".", VImage::option()->set( + "fontfile", (basePath + "assets/fonts/reddit.ttf").c_str())); + textImage = VImage::text( + captionText.c_str(), + VImage::option() + ->set("rgba", true) + ->set("font", "Roboto, Twemoji Color Font 62") + ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) + ->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 img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : 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, pageHeight + watermark.height()); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" + ? VImage::option()->set("dither", 0)->set("reoptimise", 1) + : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = pageHeight + watermark.height(); + + return output; } \ No newline at end of file diff --git a/natives/reddit.h b/natives/reddit.h index 5c79cc0..e588e96 100644 --- a/natives/reddit.h +++ b/natives/reddit.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Reddit(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Reddit(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/resize.cc b/natives/resize.cc index 4e3eee7..9a5299a 100644 --- a/natives/resize.cc +++ b/natives/resize.cc @@ -1,54 +1,59 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Resize(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - bool stretch = GetArgumentWithFallback(Arguments, "stretch", false); - bool wide = GetArgumentWithFallback(Arguments, "wide", false); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - - VImage out; - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - - int finalHeight; - if (stretch) { - out = - in.resize(512.0 / (double)width, - VImage::option()->set("vscale", 512.0 / (double)pageHeight)); - finalHeight = 512; - } else if (wide) { - out = in.resize(9.5, VImage::option()->set("vscale", 0.5)); - finalHeight = pageHeight / 2; - } else { - // Pain. Pain. Pain. Pain. Pain. - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - VImage resized = img_frame.resize(0.1).resize( - 10, VImage::option()->set("kernel", VIPS_KERNEL_NEAREST)); - img.push_back(resized); - finalHeight = resized.height(); - } - out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - } - out.set(VIPS_META_PAGE_HEIGHT, finalHeight); - - void *buf; - out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Resize(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + bool stretch = GetArgumentWithFallback(Arguments, "stretch", false); + bool wide = GetArgumentWithFallback(Arguments, "wide", false); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + + VImage out; + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + + int finalHeight; + if (stretch) { + out = + in.resize(512.0 / (double)width, + VImage::option()->set("vscale", 512.0 / (double)pageHeight)); + finalHeight = 512; + } else if (wide) { + out = in.resize(9.5, VImage::option()->set("vscale", 0.5)); + finalHeight = pageHeight / 2; + } else { + // Pain. Pain. Pain. Pain. Pain. + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + VImage resized = img_frame.resize(0.1).resize( + 10, VImage::option()->set("kernel", VIPS_KERNEL_NEAREST)); + img.push_back(resized); + finalHeight = resized.height(); + } + out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + } + out.set(VIPS_META_PAGE_HEIGHT, finalHeight); + + void *buf; + out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = finalHeight; + + return output; } \ No newline at end of file diff --git a/natives/resize.h b/natives/resize.h index 171b248..6debefc 100644 --- a/natives/resize.h +++ b/natives/resize.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Resize(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Resize(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/reverse.cc b/natives/reverse.cc index 48b162f..b74a0bd 100644 --- a/natives/reverse.cc +++ b/natives/reverse.cc @@ -1,59 +1,64 @@ -#include -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Reverse(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - bool soos = GetArgumentWithFallback(Arguments, "soos", false); - - VOption *options = - VImage::option()->set("access", "sequential")->set("n", -1); - - VImage in = VImage::new_from_buffer(BufferData, BufferLength, "", options) - .colourspace(VIPS_INTERPRETATION_sRGB); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - - vector split; - // todo: find a better way of getting individual frames (or at least getting - // the frames in reverse order) - for (int i = 0; i < nPages; i++) { - VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); - split.push_back(img_frame); - } - - vector delays = in.get_array_int("delay"); - if (soos) { - vector copy = split; - vector copy2 = delays; - reverse(copy.begin(), copy.end()); - reverse(copy2.begin(), copy2.end()); - copy.pop_back(); - 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 { - reverse(split.begin(), split.end()); - reverse(delays.begin(), delays.end()); - } - - VImage final = VImage::arrayjoin(split, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, pageHeight); - final.set("delay", delays); - - void *buf; - final.write_to_buffer(".gif", &buf, DataSize, - VImage::option()->set("dither", 0)); - - *outType = "gif"; - - return (char *)buf; +#include +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Reverse(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + bool soos = GetArgumentWithFallback(Arguments, "soos", false); + + VOption *options = + VImage::option()->set("access", "sequential")->set("n", -1); + + VImage in = VImage::new_from_buffer(BufferData, BufferLength, "", options) + .colourspace(VIPS_INTERPRETATION_sRGB); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + + vector split; + // todo: find a better way of getting individual frames (or at least getting + // the frames in reverse order) + for (int i = 0; i < nPages; i++) { + VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); + split.push_back(img_frame); + } + + vector delays = in.get_array_int("delay"); + if (soos) { + vector copy = split; + vector copy2 = delays; + reverse(copy.begin(), copy.end()); + reverse(copy2.begin(), copy2.end()); + copy.pop_back(); + 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 { + reverse(split.begin(), split.end()); + reverse(delays.begin(), delays.end()); + } + + VImage final = VImage::arrayjoin(split, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight); + final.set("delay", delays); + + void *buf; + final.write_to_buffer(".gif", &buf, DataSize, + VImage::option()->set("dither", 0)); + + *outType = "gif"; + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = pageHeight; + + return output; } \ No newline at end of file diff --git a/natives/reverse.h b/natives/reverse.h index 55d2ac5..455bb25 100644 --- a/natives/reverse.h +++ b/natives/reverse.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Reverse(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Reverse(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/scott.cc b/natives/scott.cc index f281024..5847196 100644 --- a/natives/scott.cc +++ b/natives/scott.cc @@ -1,55 +1,61 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Scott(string type, string *outType, char *BufferData, size_t BufferLength, - ArgumentMap Arguments, size_t *DataSize) { - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - - string assetPath = basePath + "assets/images/scott.png"; - VImage bg = VImage::new_from_file(assetPath.c_str()); - - string distortPath = basePath + "assets/images/scottmap.png"; - VImage distort = VImage::new_from_file(distortPath.c_str()); - - VImage distortImage = - ((distort[1] / 255) * 414).bandjoin((distort[0] / 255) * 233); - - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - VImage resized = img_frame.resize( - 415 / (double)width, - VImage::option()->set("vscale", 234 / (double)pageHeight)); - VImage mapped = resized.mapim(distortImage) - .extract_band(0, VImage::option()->set("n", 3)) - .bandjoin(distort[2]); - VImage offset = mapped.embed(127, 181, 864, 481); - VImage composited = bg.composite2(offset, VIPS_BLEND_MODE_OVER); - img.push_back(composited); - } - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, 481); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" ? VImage::option()->set("dither", 1) : 0); - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Scott(string type, string *outType, char *BufferData, size_t BufferLength, + ArgumentMap Arguments, size_t *DataSize) { + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + + string assetPath = basePath + "assets/images/scott.png"; + VImage bg = VImage::new_from_file(assetPath.c_str()); + + string distortPath = basePath + "assets/images/scottmap.png"; + VImage distort = VImage::new_from_file(distortPath.c_str()); + + VImage distortImage = + ((distort[1] / 255) * 414).bandjoin((distort[0] / 255) * 233); + + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + VImage resized = img_frame.resize( + 415 / (double)width, + VImage::option()->set("vscale", 234 / (double)pageHeight)); + VImage mapped = resized.mapim(distortImage) + .extract_band(0, VImage::option()->set("n", 3)) + .bandjoin(distort[2]); + VImage offset = mapped.embed(127, 181, 864, 481); + VImage composited = bg.composite2(offset, VIPS_BLEND_MODE_OVER); + img.push_back(composited); + } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, 481); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" ? VImage::option()->set("dither", 1) : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = pageHeight; + + return output; } \ No newline at end of file diff --git a/natives/scott.h b/natives/scott.h index 6197037..298c685 100644 --- a/natives/scott.h +++ b/natives/scott.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Scott(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Scott(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/snapchat.cc b/natives/snapchat.cc index c129d37..fbbbbbe 100644 --- a/natives/snapchat.cc +++ b/natives/snapchat.cc @@ -1,72 +1,77 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Snapchat(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - string caption = GetArgument(Arguments, "caption"); - float pos = GetArgumentWithFallback(Arguments, "pos", 0.5); - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - int size = width / 20; - int textWidth = width - ((width / 25) * 2); - - string font_string = "Helvetica Neue, Twemoji Color Font " + to_string(size); - - VImage textIn = VImage::text( - ".", VImage::option()->set( - "fontfile", (basePath + "assets/fonts/caption2.ttf").c_str())); - textIn = VImage::text( - ("" + caption + - "") - .c_str(), - VImage::option() - ->set("rgba", true) - ->set("align", VIPS_ALIGN_CENTRE) - ->set("font", font_string.c_str()) - ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) - ->set("width", textWidth)); - int bgHeight = textIn.height() + (width / 25); - textIn = ((textIn == (vector){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){0, 0, 0, 178})); - - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - img_frame = img_frame.composite2( - textIn, VIPS_BLEND_MODE_OVER, - VImage::option()->set("x", 0)->set("y", pageHeight * pos)); - img.push_back(img_frame); - } - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, pageHeight); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" - ? VImage::option()->set("dither", 0)->set("reoptimise", 1) - : 0); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Snapchat(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + string caption = GetArgument(Arguments, "caption"); + float pos = GetArgumentWithFallback(Arguments, "pos", 0.5); + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + int size = width / 20; + int textWidth = width - ((width / 25) * 2); + + string font_string = "Helvetica Neue, Twemoji Color Font " + to_string(size); + + VImage textIn = VImage::text( + ".", VImage::option()->set( + "fontfile", (basePath + "assets/fonts/caption2.ttf").c_str())); + textIn = VImage::text( + ("" + caption + + "") + .c_str(), + VImage::option() + ->set("rgba", true) + ->set("align", VIPS_ALIGN_CENTRE) + ->set("font", font_string.c_str()) + ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) + ->set("width", textWidth)); + int bgHeight = textIn.height() + (width / 25); + textIn = ((textIn == (vector){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){0, 0, 0, 178})); + + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + img_frame = img_frame.composite2( + textIn, VIPS_BLEND_MODE_OVER, + VImage::option()->set("x", 0)->set("y", pageHeight * pos)); + img.push_back(img_frame); + } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" + ? VImage::option()->set("dither", 0)->set("reoptimise", 1) + : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = pageHeight; + + return output; } \ No newline at end of file diff --git a/natives/snapchat.h b/natives/snapchat.h index 713589e..a93ee1c 100644 --- a/natives/snapchat.h +++ b/natives/snapchat.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Snapchat(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Snapchat(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/sonic.cc b/natives/sonic.cc index eafdd22..0d078b0 100644 --- a/natives/sonic.cc +++ b/natives/sonic.cc @@ -1,35 +1,40 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Sonic(string type, string *outType, ArgumentMap Arguments, - size_t *DataSize) { - string text = GetArgument(Arguments, "text"); - string basePath = GetArgument(Arguments, "basePath"); - - string assetPath = basePath + "assets/images/sonic.jpg"; - VImage bg = VImage::new_from_file(assetPath.c_str()); - - VImage textImage = - VImage::text( - ("" + text + "").c_str(), - VImage::option() - ->set("rgba", true) - ->set("align", VIPS_ALIGN_CENTRE) - ->set("font", "Verdana, Twemoji Color Font") - ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) - ->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; - out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Sonic(string type, string *outType, ArgumentMap Arguments, + size_t *DataSize) { + string text = GetArgument(Arguments, "text"); + string basePath = GetArgument(Arguments, "basePath"); + + string assetPath = basePath + "assets/images/sonic.jpg"; + VImage bg = VImage::new_from_file(assetPath.c_str()); + + VImage textImage = + VImage::text( + ("" + text + "").c_str(), + VImage::option() + ->set("rgba", true) + ->set("align", VIPS_ALIGN_CENTRE) + ->set("font", "Verdana, Twemoji Color Font") + ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) + ->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; + out.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = out.width(); + output["height"] = vips_image_get_page_height(out.get_image()); + + return output; } \ No newline at end of file diff --git a/natives/sonic.h b/natives/sonic.h index af988f1..73b3f36 100644 --- a/natives/sonic.h +++ b/natives/sonic.h @@ -1,7 +1,7 @@ -#pragma once - -#include "common.h" - -using std::string; - -char *Sonic(string type, string *outType, ArgumentMap Arguments, size_t *DataSize); \ No newline at end of file +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Sonic(string type, string *outType, ArgumentMap Arguments, size_t *DataSize); \ No newline at end of file diff --git a/natives/speed.cc b/natives/speed.cc index 166236f..6c05d59 100644 --- a/natives/speed.cc +++ b/natives/speed.cc @@ -1,102 +1,109 @@ -#include -#include -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -void *memset16(void *m, uint16_t val, size_t count) { - uint16_t *buf = (uint16_t *)m; - - while (count--) *buf++ = val; - return m; -} - -char *vipsRemove(char *data, size_t length, size_t *DataSize, int speed) { - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = VImage::new_from_buffer(data, length, "", options->set("n", -1)) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - - vector img; - for (int i = 0; i < nPages; i += speed) { - VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); - img.push_back(img_frame); - } - VImage out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - out.set(VIPS_META_PAGE_HEIGHT, pageHeight); - - void *buf; - out.write_to_buffer(".gif", &buf, DataSize); - - return (char *)buf; -} - -char *Speed([[maybe_unused]] string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - bool slow = GetArgumentWithFallback(Arguments, "slow", false); - int speed = GetArgumentWithFallback(Arguments, "speed", 2); - - char *fileData = (char *)malloc(BufferLength); - memcpy(fileData, BufferData, BufferLength); - - char *match = (char *)"\x00\x21\xF9\x04"; - - vector old_delays; - bool removeFrames = false; - char *lastPos; - - // int amount = 0; - - lastPos = (char *)memchr(fileData, '\x00', BufferLength); - while (lastPos != NULL) { - if (memcmp(lastPos, match, 4) != 0) { - lastPos = (char *)memchr(lastPos + 1, '\x00', - (BufferLength - (lastPos - fileData)) - 1); - continue; - } - //++amount; - uint16_t old_delay; - memcpy(&old_delay, lastPos + 5, 2); - old_delays.push_back(old_delay); - lastPos = (char *)memchr(lastPos + 1, '\x00', - (BufferLength - (lastPos - fileData)) - 1); - } - - int currentFrame = 0; - lastPos = (char *)memchr(fileData, '\x00', BufferLength); - while (lastPos != NULL) { - if (memcmp(lastPos, match, 4) != 0) { - lastPos = (char *)memchr(lastPos + 1, '\x00', - (BufferLength - (lastPos - fileData)) - 1); - continue; - } - uint16_t new_delay = slow ? old_delays[currentFrame] * speed - : old_delays[currentFrame] / speed; - if (!slow && new_delay <= 1) { - removeFrames = true; - break; - } - - memset16(lastPos + 5, new_delay, 1); - - lastPos = (char *)memchr(lastPos + 1, '\x00', - (BufferLength - (lastPos - fileData)) - 1); - ++currentFrame; - } - - if (removeFrames) { - fileData = vipsRemove(BufferData, BufferLength, DataSize, speed); - } else { - *DataSize = BufferLength; - } - - return fileData; +#include +#include +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +void *memset16(void *m, uint16_t val, size_t count) { + uint16_t *buf = (uint16_t *)m; + + while (count--) *buf++ = val; + return m; +} + +ArgumentMap vipsHandle(char *data, size_t length, size_t *DataSize, int speed, bool removeFrames) { + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = VImage::new_from_buffer(data, length, "", options->set("n", -1)) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + + VImage out; + if (removeFrames) { + vector img; + for (int i = 0; i < nPages; i += speed) { + VImage img_frame = in.crop(0, i * pageHeight, width, pageHeight); + img.push_back(img_frame); + } + out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + out.set(VIPS_META_PAGE_HEIGHT, pageHeight); + } else { + out = in; + } + + void *buf; + out.write_to_buffer(".gif", &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = pageHeight; + + return output; +} + +ArgumentMap Speed([[maybe_unused]] string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + bool slow = GetArgumentWithFallback(Arguments, "slow", false); + int speed = GetArgumentWithFallback(Arguments, "speed", 2); + + char *fileData = (char *)malloc(BufferLength); + memcpy(fileData, BufferData, BufferLength); + + char *match = (char *)"\x00\x21\xF9\x04"; + + vector old_delays; + bool removeFrames = false; + char *lastPos; + + // int amount = 0; + + lastPos = (char *)memchr(fileData, '\x00', BufferLength); + while (lastPos != NULL) { + if (memcmp(lastPos, match, 4) != 0) { + lastPos = (char *)memchr(lastPos + 1, '\x00', + (BufferLength - (lastPos - fileData)) - 1); + continue; + } + //++amount; + uint16_t old_delay; + memcpy(&old_delay, lastPos + 5, 2); + old_delays.push_back(old_delay); + lastPos = (char *)memchr(lastPos + 1, '\x00', + (BufferLength - (lastPos - fileData)) - 1); + } + + int currentFrame = 0; + lastPos = (char *)memchr(fileData, '\x00', BufferLength); + while (lastPos != NULL) { + if (memcmp(lastPos, match, 4) != 0) { + lastPos = (char *)memchr(lastPos + 1, '\x00', + (BufferLength - (lastPos - fileData)) - 1); + continue; + } + uint16_t new_delay = slow ? old_delays[currentFrame] * speed + : old_delays[currentFrame] / speed; + if (!slow && new_delay <= 1) { + removeFrames = true; + break; + } + + memset16(lastPos + 5, new_delay, 1); + + lastPos = (char *)memchr(lastPos + 1, '\x00', + (BufferLength - (lastPos - fileData)) - 1); + ++currentFrame; + } + + // TODO: this is cursed, fix it later + ArgumentMap output = vipsHandle(BufferData, BufferLength, DataSize, speed, removeFrames); + + return output; } \ No newline at end of file diff --git a/natives/speed.h b/natives/speed.h index c101447..a3bd8bb 100644 --- a/natives/speed.h +++ b/natives/speed.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Speed(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Speed(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/spin.cc b/natives/spin.cc index 1fdde8a..6d8dd2f 100644 --- a/natives/spin.cc +++ b/natives/spin.cc @@ -1,72 +1,78 @@ -#include - -#include -#include -#include - -#include "common.h" - -using namespace std; -using namespace Magick; - -char *Spin(string type, string *outType, char *BufferData, size_t BufferLength, - [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { - int delay = GetArgumentWithFallback(Arguments, "delay", 0); - - Blob blob; - - list frames; - list coalesced; - list mid; - try { - readImages(&frames, Blob(BufferData, BufferLength)); - } 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()); - - if (type != "gif") { - list::iterator it = coalesced.begin(); - for (int i = 0; i < 29; ++i) { - coalesced.push_back(*it); - } - } - - int i = 0; - for (Image &image : coalesced) { - image.virtualPixelMethod(Magick::TransparentVirtualPixelMethod); - image.scale(Geometry("256x256")); - image.alphaChannel(Magick::SetAlphaChannel); - double rotation[1] = {(double)360 * i / coalesced.size()}; - image.distort(Magick::ScaleRotateTranslateDistortion, 1, rotation); - image.magick("GIF"); - mid.push_back(image); - i++; - } - - for_each(mid.begin(), mid.end(), - gifDisposeMethodImage(Magick::BackgroundDispose)); - - 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); - - *outType = "gif"; - *DataSize = blob.length(); - - char *data = (char *)malloc(*DataSize); - memcpy(data, blob.data(), *DataSize); - return data; -} +#include + +#include +#include +#include + +#include "common.h" + +using namespace std; +using namespace Magick; + +ArgumentMap Spin(string type, string *outType, char *BufferData, size_t BufferLength, + [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { + int delay = GetArgumentWithFallback(Arguments, "delay", 0); + + Blob blob; + + list frames; + list coalesced; + list mid; + try { + readImages(&frames, Blob(BufferData, BufferLength)); + } 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()); + + if (type != "gif") { + list::iterator it = coalesced.begin(); + for (int i = 0; i < 29; ++i) { + coalesced.push_back(*it); + } + } + + int i = 0; + for (Image &image : coalesced) { + image.virtualPixelMethod(Magick::TransparentVirtualPixelMethod); + image.scale(Geometry("256x256")); + image.alphaChannel(Magick::SetAlphaChannel); + double rotation[1] = {(double)360 * i / coalesced.size()}; + image.distort(Magick::ScaleRotateTranslateDistortion, 1, rotation); + image.magick("GIF"); + mid.push_back(image); + i++; + } + + for_each(mid.begin(), mid.end(), + gifDisposeMethodImage(Magick::BackgroundDispose)); + + 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); + + *outType = "gif"; + *DataSize = blob.length(); + + char *data = (char *)malloc(*DataSize); + memcpy(data, blob.data(), *DataSize); + + ArgumentMap output; + output["buf"] = data; + output["width"] = (int)mid.front().columns(); + output["height"] = (int)mid.front().rows(); + + return output; +} diff --git a/natives/spin.h b/natives/spin.h index 423c5b2..a51a8b5 100644 --- a/natives/spin.h +++ b/natives/spin.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Spin(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Spin(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/squish.cc b/natives/squish.cc index a64da94..4804d56 100644 --- a/natives/squish.cc +++ b/natives/squish.cc @@ -1,52 +1,57 @@ -#include - -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Squish(string type, string *outType, char *BufferData, - size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, - size_t *DataSize) { - VOption *options = VImage::option(); - - VImage in = - VImage::new_from_buffer( - BufferData, BufferLength, "", - 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 pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30; - double mult = (2 * M_PI) / nPages; - - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - double newWidth = (sin(i * mult) / 4) + 0.75; - double newHeight = (cos(i * mult) / 4) + 0.75; - VImage resized = - img_frame.resize(newWidth, VImage::option()->set("vscale", newHeight)) - .gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, pageHeight); - img.push_back(resized); - } - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, pageHeight); - if (type != "gif") { - vector delay(30, 50); - final.set("delay", delay); - } - - void *buf; - final.write_to_buffer(".gif", &buf, DataSize); - - *outType = "gif"; - - return (char *)buf; +#include + +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Squish(string type, string *outType, char *BufferData, + size_t BufferLength, [[maybe_unused]] ArgumentMap Arguments, + size_t *DataSize) { + VOption *options = VImage::option(); + + VImage in = + VImage::new_from_buffer( + BufferData, BufferLength, "", + 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 pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = type == "gif" ? vips_image_get_n_pages(in.get_image()) : 30; + double mult = (2 * M_PI) / nPages; + + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + double newWidth = (sin(i * mult) / 4) + 0.75; + double newHeight = (cos(i * mult) / 4) + 0.75; + VImage resized = + img_frame.resize(newWidth, VImage::option()->set("vscale", newHeight)) + .gravity(VIPS_COMPASS_DIRECTION_CENTRE, width, pageHeight); + img.push_back(resized); + } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight); + if (type != "gif") { + vector delay(30, 50); + final.set("delay", delay); + } + + void *buf; + final.write_to_buffer(".gif", &buf, DataSize); + + *outType = "gif"; + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = final.width(); + output["height"] = vips_image_get_page_height(final.get_image()); + + return output; } \ No newline at end of file diff --git a/natives/squish.h b/natives/squish.h index b145b64..48f3edb 100644 --- a/natives/squish.h +++ b/natives/squish.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Squish(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Squish(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/swirl.cc b/natives/swirl.cc index ac58ae1..beeeb76 100644 --- a/natives/swirl.cc +++ b/natives/swirl.cc @@ -1,76 +1,81 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Swirl(string type, string *outType, char *BufferData, size_t BufferLength, - [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - int width = in.width(); - double newWidth = width * 3; - double newHeight = pageHeight * 3; - vector divSize = {newWidth / 2, newHeight / 2}; - - VImage index = VImage::xyz(newWidth, newHeight); - VImage center = index - divSize; - VImage polar = center - .copy(VImage::option() - ->set("format", VIPS_FORMAT_COMPLEX) - ->set("bands", 1)) - .polar() - .copy(VImage::option() - ->set("format", VIPS_FORMAT_FLOAT) - ->set("bands", 2)); - - int size = min(width, pageHeight) / 2; - - VImage test = (1 - polar.extract_band(0) / size); - VImage degrees = test.cast(VIPS_FORMAT_FLOAT).pow(2); - - VImage angle = polar.extract_band(1) + degrees * 180; - - VImage distortion = polar.extract_band(0) - .bandjoin(angle) - .copy(VImage::option() - ->set("format", VIPS_FORMAT_COMPLEX) - ->set("bands", 1)) - .rect() - .copy(VImage::option() - ->set("format", VIPS_FORMAT_FLOAT) - ->set("bands", 2)) + - divSize; - - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - - VImage distort = - img_frame - .gravity(VIPS_COMPASS_DIRECTION_CENTRE, newWidth, newHeight, - VImage::option()->set("extend", VIPS_EXTEND_COPY)) - .mapim(distortion, - VImage::option()->set( - "interpolate", VInterpolate::new_from_name("bicubic"))); - VImage frame = distort.crop(width, pageHeight, width, pageHeight); - img.push_back(frame); - } - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, pageHeight); - - void *buf; - final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Swirl(string type, string *outType, char *BufferData, size_t BufferLength, + [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + int width = in.width(); + double newWidth = width * 3; + double newHeight = pageHeight * 3; + vector divSize = {newWidth / 2, newHeight / 2}; + + VImage index = VImage::xyz(newWidth, newHeight); + VImage center = index - divSize; + VImage polar = center + .copy(VImage::option() + ->set("format", VIPS_FORMAT_COMPLEX) + ->set("bands", 1)) + .polar() + .copy(VImage::option() + ->set("format", VIPS_FORMAT_FLOAT) + ->set("bands", 2)); + + int size = min(width, pageHeight) / 2; + + VImage test = (1 - polar.extract_band(0) / size); + VImage degrees = test.cast(VIPS_FORMAT_FLOAT).pow(2); + + VImage angle = polar.extract_band(1) + degrees * 180; + + VImage distortion = polar.extract_band(0) + .bandjoin(angle) + .copy(VImage::option() + ->set("format", VIPS_FORMAT_COMPLEX) + ->set("bands", 1)) + .rect() + .copy(VImage::option() + ->set("format", VIPS_FORMAT_FLOAT) + ->set("bands", 2)) + + divSize; + + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + + VImage distort = + img_frame + .gravity(VIPS_COMPASS_DIRECTION_CENTRE, newWidth, newHeight, + VImage::option()->set("extend", VIPS_EXTEND_COPY)) + .mapim(distortion, + VImage::option()->set( + "interpolate", VInterpolate::new_from_name("bicubic"))); + VImage frame = distort.crop(width, pageHeight, width, pageHeight); + img.push_back(frame); + } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight); + + void *buf; + final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = final.width(); + output["height"] = vips_image_get_page_height(final.get_image()); + + return output; } \ No newline at end of file diff --git a/natives/swirl.h b/natives/swirl.h index 536314f..0686148 100644 --- a/natives/swirl.h +++ b/natives/swirl.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Swirl(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Swirl(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/tile.cc b/natives/tile.cc index 6f1c0a3..cae67ae 100644 --- a/natives/tile.cc +++ b/natives/tile.cc @@ -1,65 +1,71 @@ -#include - -#include -#include -#include - -#include "common.h" - -using namespace std; -using namespace Magick; - -char *Tile(string type, string *outType, char *BufferData, size_t BufferLength, - [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { - Blob blob; - - list frames; - list coalesced; - list mid; - try { - readImages(&frames, Blob(BufferData, BufferLength)); - } 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) { - list duplicated; - Image appended; - list montage; - Image frame; - image.magick(*outType); - for (int i = 0; i < 5; ++i) { - duplicated.push_back(image); - } - appendImages(&appended, duplicated.begin(), duplicated.end()); - appended.repage(); - for (int i = 0; i < 5; ++i) { - montage.push_back(appended); - } - appendImages(&frame, montage.begin(), montage.end(), true); - frame.repage(); - frame.scale(Geometry("800x800>")); - frame.animationDelay(image.animationDelay()); - mid.push_back(frame); - } - - optimizeTransparency(mid.begin(), mid.end()); - - if (*outType == "gif") { - for (Image &image : mid) { - image.quantizeDitherMethod(FloydSteinbergDitherMethod); - image.quantize(); - } - } - - writeImages(mid.begin(), mid.end(), &blob); - - *DataSize = blob.length(); - - char *data = (char *)malloc(*DataSize); - memcpy(data, blob.data(), *DataSize); - return data; +#include + +#include +#include +#include + +#include "common.h" + +using namespace std; +using namespace Magick; + +ArgumentMap Tile(string type, string *outType, char *BufferData, size_t BufferLength, + [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { + Blob blob; + + list frames; + list coalesced; + list mid; + try { + readImages(&frames, Blob(BufferData, BufferLength)); + } 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) { + list duplicated; + Image appended; + list montage; + Image frame; + image.magick(*outType); + for (int i = 0; i < 5; ++i) { + duplicated.push_back(image); + } + appendImages(&appended, duplicated.begin(), duplicated.end()); + appended.repage(); + for (int i = 0; i < 5; ++i) { + montage.push_back(appended); + } + appendImages(&frame, montage.begin(), montage.end(), true); + frame.repage(); + frame.scale(Geometry("800x800>")); + frame.animationDelay(image.animationDelay()); + mid.push_back(frame); + } + + optimizeTransparency(mid.begin(), mid.end()); + + if (*outType == "gif") { + for (Image &image : mid) { + image.quantizeDitherMethod(FloydSteinbergDitherMethod); + image.quantize(); + } + } + + writeImages(mid.begin(), mid.end(), &blob); + + *DataSize = blob.length(); + + char *data = (char *)malloc(*DataSize); + memcpy(data, blob.data(), *DataSize); + + ArgumentMap output; + output["buf"] = data; + output["width"] = (int)mid.front().columns(); + output["height"] = (int)mid.front().rows(); + + return output; } \ No newline at end of file diff --git a/natives/tile.h b/natives/tile.h index 929b9dc..f6323c2 100644 --- a/natives/tile.h +++ b/natives/tile.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Tile(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Tile(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/togif.cc b/natives/togif.cc index 7119254..e15ab37 100644 --- a/natives/togif.cc +++ b/natives/togif.cc @@ -1,28 +1,38 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *ToGif(string type, string *outType, char *BufferData, size_t BufferLength, - [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { - if (type == "gif") { - *DataSize = BufferLength; - char *data = (char *)malloc(BufferLength); - memcpy(data, BufferData, BufferLength); - return data; - } else { - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = VImage::new_from_buffer( - BufferData, BufferLength, "", - type == "webp" ? options->set("n", -1) : options); - - void *buf; - in.write_to_buffer(".gif", &buf, DataSize); - *outType = "gif"; - - return (char *)buf; - } +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap ToGif(string type, string *outType, char *BufferData, size_t BufferLength, + [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { + if (type == "gif") { + *DataSize = BufferLength; + char *data = (char *)malloc(BufferLength); + memcpy(data, BufferData, BufferLength); + + ArgumentMap output; + output["buf"] = data; + + return output; + + } else { + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = VImage::new_from_buffer( + BufferData, BufferLength, "", + type == "webp" ? options->set("n", -1) : options); + + void *buf; + in.write_to_buffer(".gif", &buf, DataSize); + *outType = "gif"; + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = in.width(); + output["height"] = vips_image_get_page_height(in.get_image()); + + return output; + } } \ No newline at end of file diff --git a/natives/togif.h b/natives/togif.h index 0cf3fc1..ee12e06 100644 --- a/natives/togif.h +++ b/natives/togif.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* ToGif(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap ToGif(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/uncanny.cc b/natives/uncanny.cc index 79534f2..30b7adb 100644 --- a/natives/uncanny.cc +++ b/natives/uncanny.cc @@ -1,101 +1,106 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Uncanny(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - string caption = GetArgument(Arguments, "caption"); - string caption2 = GetArgument(Arguments, "caption2"); - string font = GetArgument(Arguments, "font"); - string path = GetArgument(Arguments, "path"); - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB) - .extract_band(0, VImage::option()->set("n", 3)); - - VImage base = VImage::black(1280, 720, VImage::option()->set("bands", 3)); - - string font_string = (font == "roboto" ? "Roboto Condensed" : font) + - ", Twemoji Color Font " + - (font != "impact" ? "bold" : "normal") + " 72"; - - string captionText = - "" + caption + ""; - string caption2Text = - "" + caption2 + ""; - - auto findResult = fontPaths.find(font); - if (findResult != fontPaths.end()) { - VImage::text(".", VImage::option()->set( - "fontfile", (basePath + findResult->second).c_str())); - } - - VImage text = VImage::text( - captionText.c_str(), - VImage::option() - ->set("rgba", true) - ->set("align", VIPS_ALIGN_CENTRE) - ->set("font", font_string.c_str()) - ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) - ->set("width", 588) - ->set("height", 90)); - VImage captionImage = - text.extract_band(0, VImage::option()->set("n", 3)) - .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40, - VImage::option()->set("extend", "black")); - - VImage text2 = VImage::text( - caption2Text.c_str(), - VImage::option() - ->set("rgba", true) - ->set("align", VIPS_ALIGN_CENTRE) - ->set("font", font_string.c_str()) - ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) - ->set("width", 588) - ->set("height", 90)); - VImage caption2Image = - text2.extract_band(0, VImage::option()->set("n", 3)) - .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40, - VImage::option()->set("extend", "black")); - - base = base.insert(captionImage, 0, 0).insert(caption2Image, 640, 0); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - - VImage uncanny = VImage::new_from_file((basePath + path).c_str()); - - base = base.insert(uncanny, 0, 130); - - vector img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - VImage resized = img_frame.resize(690.0 / (double)width); - if (resized.height() > 590) { - double vscale = 590.0 / (double)resized.height(); - resized = resized.resize(vscale, VImage::option()->set("vscale", vscale)); - } - VImage composited = base.insert(resized, 935 - (resized.width() / 2), - 425 - (resized.height() / 2)); - img.push_back(composited); - } - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, 720); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" ? VImage::option()->set("reoptimise", 1) : 0); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Uncanny(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + string caption = GetArgument(Arguments, "caption"); + string caption2 = GetArgument(Arguments, "caption2"); + string font = GetArgument(Arguments, "font"); + string path = GetArgument(Arguments, "path"); + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB) + .extract_band(0, VImage::option()->set("n", 3)); + + VImage base = VImage::black(1280, 720, VImage::option()->set("bands", 3)); + + string font_string = (font == "roboto" ? "Roboto Condensed" : font) + + ", Twemoji Color Font " + + (font != "impact" ? "bold" : "normal") + " 72"; + + string captionText = + "" + caption + ""; + string caption2Text = + "" + caption2 + ""; + + auto findResult = fontPaths.find(font); + if (findResult != fontPaths.end()) { + VImage::text(".", VImage::option()->set( + "fontfile", (basePath + findResult->second).c_str())); + } + + VImage text = VImage::text( + captionText.c_str(), + VImage::option() + ->set("rgba", true) + ->set("align", VIPS_ALIGN_CENTRE) + ->set("font", font_string.c_str()) + ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) + ->set("width", 588) + ->set("height", 90)); + VImage captionImage = + text.extract_band(0, VImage::option()->set("n", 3)) + .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40, + VImage::option()->set("extend", "black")); + + VImage text2 = VImage::text( + caption2Text.c_str(), + VImage::option() + ->set("rgba", true) + ->set("align", VIPS_ALIGN_CENTRE) + ->set("font", font_string.c_str()) + ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) + ->set("width", 588) + ->set("height", 90)); + VImage caption2Image = + text2.extract_band(0, VImage::option()->set("n", 3)) + .gravity(VIPS_COMPASS_DIRECTION_CENTRE, 640, text.height() + 40, + VImage::option()->set("extend", "black")); + + base = base.insert(captionImage, 0, 0).insert(caption2Image, 640, 0); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + + VImage uncanny = VImage::new_from_file((basePath + path).c_str()); + + base = base.insert(uncanny, 0, 130); + + vector img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + VImage resized = img_frame.resize(690.0 / (double)width); + if (resized.height() > 590) { + double vscale = 590.0 / (double)resized.height(); + resized = resized.resize(vscale, VImage::option()->set("vscale", vscale)); + } + VImage composited = base.insert(resized, 935 - (resized.width() / 2), + 425 - (resized.height() / 2)); + img.push_back(composited); + } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, 720); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" ? VImage::option()->set("reoptimise", 1) : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = final.width(); + output["height"] = vips_image_get_page_height(final.get_image()); + + return output; } \ No newline at end of file diff --git a/natives/uncanny.h b/natives/uncanny.h index f7e975d..4a82a49 100644 --- a/natives/uncanny.h +++ b/natives/uncanny.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Uncanny(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Uncanny(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/uncaption.cc b/natives/uncaption.cc index 2da184b..5db8b7a 100644 --- a/natives/uncaption.cc +++ b/natives/uncaption.cc @@ -1,54 +1,59 @@ -#include -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Uncaption(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - float tolerance = GetArgumentWithFallback(Arguments, "tolerance", 0.5); - - VOption *options = VImage::option(); - - VImage in = - VImage::new_from_buffer( - BufferData, BufferLength, "", - 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 pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - - VImage first = - in.crop(0, 0, 3, pageHeight).colourspace(VIPS_INTERPRETATION_B_W) > - (255 * tolerance); - int top, captionWidth, captionHeight; - first.find_trim(&top, &captionWidth, &captionHeight); - - vector img; - int newHeight = pageHeight - top; - if (top == pageHeight) { - newHeight = pageHeight; - top = 0; - } - for (int i = 0; i < nPages; i++) { - VImage img_frame = in.crop(0, (i * pageHeight) + top, width, newHeight); - img.push_back(img_frame); - } - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, newHeight); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" - ? VImage::option()->set("dither", 0)->set("reoptimise", 1) - : 0); - - return (char *)buf; -} +#include +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Uncaption(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + float tolerance = GetArgumentWithFallback(Arguments, "tolerance", 0.5); + + VOption *options = VImage::option(); + + VImage in = + VImage::new_from_buffer( + BufferData, BufferLength, "", + 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 pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + + VImage first = + in.crop(0, 0, 3, pageHeight).colourspace(VIPS_INTERPRETATION_B_W) > + (255 * tolerance); + int top, captionWidth, captionHeight; + first.find_trim(&top, &captionWidth, &captionHeight); + + vector img; + int newHeight = pageHeight - top; + if (top == pageHeight) { + newHeight = pageHeight; + top = 0; + } + for (int i = 0; i < nPages; i++) { + VImage img_frame = in.crop(0, (i * pageHeight) + top, width, newHeight); + img.push_back(img_frame); + } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, newHeight); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" + ? VImage::option()->set("dither", 0)->set("reoptimise", 1) + : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = final.width(); + output["height"] = vips_image_get_page_height(final.get_image()); + + return output; +} diff --git a/natives/uncaption.h b/natives/uncaption.h index 6a283ff..4bc8c8a 100644 --- a/natives/uncaption.h +++ b/natives/uncaption.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Uncaption(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Uncaption(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/wall.cc b/natives/wall.cc index d1dc800..c4a4165 100644 --- a/natives/wall.cc +++ b/natives/wall.cc @@ -1,58 +1,64 @@ -#include - -#include -#include -#include - -#include "common.h" - -using namespace std; -using namespace Magick; - -char *Wall(string type, string *outType, char *BufferData, size_t BufferLength, - [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { - Blob blob; - - list frames; - list coalesced; - list mid; - try { - readImages(&frames, Blob(BufferData, BufferLength)); - } 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) { - image.resize(Geometry("128x128")); - image.virtualPixelMethod(Magick::TileVirtualPixelMethod); - image.matteColor("none"); - image.backgroundColor("none"); - image.scale(Geometry("512x512")); - double arguments[16] = {0, 0, 57, 42, 0, 128, 63, 130, - 128, 0, 140, 60, 128, 128, 140, 140}; - image.distort(Magick::PerspectiveDistortion, 16, arguments); - image.scale(Geometry("800x800>")); - image.magick(*outType); - mid.push_back(image); - } - - optimizeTransparency(mid.begin(), mid.end()); - - if (*outType == "gif") { - for (Image &image : mid) { - image.quantizeDitherMethod(FloydSteinbergDitherMethod); - image.quantize(); - } - } - - writeImages(mid.begin(), mid.end(), &blob); - - *DataSize = blob.length(); - - char *data = (char *)malloc(*DataSize); - memcpy(data, blob.data(), *DataSize); - return data; +#include + +#include +#include +#include + +#include "common.h" + +using namespace std; +using namespace Magick; + +ArgumentMap Wall(string type, string *outType, char *BufferData, size_t BufferLength, + [[maybe_unused]] ArgumentMap Arguments, size_t *DataSize) { + Blob blob; + + list frames; + list coalesced; + list mid; + try { + readImages(&frames, Blob(BufferData, BufferLength)); + } 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) { + image.resize(Geometry("128x128")); + image.virtualPixelMethod(Magick::TileVirtualPixelMethod); + image.matteColor("none"); + image.backgroundColor("none"); + image.scale(Geometry("512x512")); + double arguments[16] = {0, 0, 57, 42, 0, 128, 63, 130, + 128, 0, 140, 60, 128, 128, 140, 140}; + image.distort(Magick::PerspectiveDistortion, 16, arguments); + image.scale(Geometry("800x800>")); + image.magick(*outType); + mid.push_back(image); + } + + optimizeTransparency(mid.begin(), mid.end()); + + if (*outType == "gif") { + for (Image &image : mid) { + image.quantizeDitherMethod(FloydSteinbergDitherMethod); + image.quantize(); + } + } + + writeImages(mid.begin(), mid.end(), &blob); + + *DataSize = blob.length(); + + char *data = (char *)malloc(*DataSize); + memcpy(data, blob.data(), *DataSize); + + ArgumentMap output; + output["buf"] = data; + output["width"] = (int)mid.front().columns(); + output["height"] = (int)mid.front().rows(); + + return output; } \ No newline at end of file diff --git a/natives/wall.h b/natives/wall.h index 917fc99..25e9f9e 100644 --- a/natives/wall.h +++ b/natives/wall.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Wall(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Wall(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/watermark.cc b/natives/watermark.cc index 4b0ac4e..0587b11 100644 --- a/natives/watermark.cc +++ b/natives/watermark.cc @@ -1,154 +1,159 @@ -#include -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Watermark(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - string water = GetArgument(Arguments, "water"); - int gravity = GetArgument(Arguments, "gravity"); - - bool resize = GetArgumentWithFallback(Arguments, "resize", false); - - float yscale = GetArgumentWithFallback(Arguments, "yscale", false); - - bool append = GetArgumentWithFallback(Arguments, "append", false); - - bool alpha = GetArgumentWithFallback(Arguments, "alpha", false); - bool flip = GetArgumentWithFallback(Arguments, "flip", false); - - bool mc = MAP_HAS(Arguments, "mc"); - - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - string merged = basePath + water; - VImage watermark = VImage::new_from_file(merged.c_str()); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - - if (flip) { - watermark = watermark.flip(VIPS_DIRECTION_HORIZONTAL); - } - - if (resize && append) { - watermark = watermark.resize((double)width / (double)watermark.width()); - } else if (resize && yscale) { - watermark = watermark.resize( - (double)width / (double)watermark.width(), - VImage::option()->set("vscale", (double)(pageHeight * yscale) / - (double)watermark.height())); - } else if (resize) { - watermark = - watermark.resize((double)pageHeight / (double)watermark.height()); - } - - int x = 0, y = 0; - switch (gravity) { - case 1: - break; - case 2: - x = (width / 2) - (watermark.width() / 2); - break; - case 3: - x = width - watermark.width(); - break; - case 5: - x = (width / 2) - (watermark.width() / 2); - y = (pageHeight / 2) - (watermark.height() / 2); - break; - case 6: - x = width - watermark.width(); - y = (pageHeight / 2) - (watermark.height() / 2); - break; - case 8: - x = (width / 2) - (watermark.width() / 2); - y = pageHeight - watermark.height(); - break; - case 9: - x = width - watermark.width(); - y = pageHeight - watermark.height(); - break; - } - - vector img; - int addedHeight = 0; - VImage contentAlpha; - VImage frameAlpha; - VImage bg; - VImage frame; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - if (append) { - VImage appended = img_frame.join(watermark, VIPS_DIRECTION_VERTICAL, - VImage::option()->set("expand", true)); - addedHeight = watermark.height(); - img.push_back(appended); - } else if (mc) { - VImage padded = - img_frame.embed(0, 0, width, pageHeight + 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 { - VImage composited; - if (alpha) { - if (i == 0) { - contentAlpha = watermark.extract_band(0).embed( - x, y, width, pageHeight, - VImage::option()->set("extend", "white")); - frameAlpha = watermark.extract_band(1).embed( - x, y, width, pageHeight, - 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 (*outType == "jpg" || *outType == "jpeg") { - *outType = "png"; - } - } - VImage content = - img_frame.extract_band(0, VImage::option()->set("n", 3)) - .bandjoin(contentAlpha & img_frame.extract_band(3)); - - composited = - content.composite2(frame, VIPS_BLEND_MODE_OVER, - VImage::option()->set("x", x)->set("y", y)); - } else { - composited = - img_frame.composite2(watermark, VIPS_BLEND_MODE_OVER, - VImage::option()->set("x", x)->set("y", y)); - } - img.push_back(composited); - } - } - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, pageHeight + addedHeight); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" - ? VImage::option()->set("dither", 0)->set("reoptimise", 1) - : 0); - - return (char *)buf; -} +#include +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Watermark(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + string water = GetArgument(Arguments, "water"); + int gravity = GetArgument(Arguments, "gravity"); + + bool resize = GetArgumentWithFallback(Arguments, "resize", false); + + float yscale = GetArgumentWithFallback(Arguments, "yscale", false); + + bool append = GetArgumentWithFallback(Arguments, "append", false); + + bool alpha = GetArgumentWithFallback(Arguments, "alpha", false); + bool flip = GetArgumentWithFallback(Arguments, "flip", false); + + bool mc = MAP_HAS(Arguments, "mc"); + + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + string merged = basePath + water; + VImage watermark = VImage::new_from_file(merged.c_str()); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + + if (flip) { + watermark = watermark.flip(VIPS_DIRECTION_HORIZONTAL); + } + + if (resize && append) { + watermark = watermark.resize((double)width / (double)watermark.width()); + } else if (resize && yscale) { + watermark = watermark.resize( + (double)width / (double)watermark.width(), + VImage::option()->set("vscale", (double)(pageHeight * yscale) / + (double)watermark.height())); + } else if (resize) { + watermark = + watermark.resize((double)pageHeight / (double)watermark.height()); + } + + int x = 0, y = 0; + switch (gravity) { + case 1: + break; + case 2: + x = (width / 2) - (watermark.width() / 2); + break; + case 3: + x = width - watermark.width(); + break; + case 5: + x = (width / 2) - (watermark.width() / 2); + y = (pageHeight / 2) - (watermark.height() / 2); + break; + case 6: + x = width - watermark.width(); + y = (pageHeight / 2) - (watermark.height() / 2); + break; + case 8: + x = (width / 2) - (watermark.width() / 2); + y = pageHeight - watermark.height(); + break; + case 9: + x = width - watermark.width(); + y = pageHeight - watermark.height(); + break; + } + + vector img; + int addedHeight = 0; + VImage contentAlpha; + VImage frameAlpha; + VImage bg; + VImage frame; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + if (append) { + VImage appended = img_frame.join(watermark, VIPS_DIRECTION_VERTICAL, + VImage::option()->set("expand", true)); + addedHeight = watermark.height(); + img.push_back(appended); + } else if (mc) { + VImage padded = + img_frame.embed(0, 0, width, pageHeight + 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 { + VImage composited; + if (alpha) { + if (i == 0) { + contentAlpha = watermark.extract_band(0).embed( + x, y, width, pageHeight, + VImage::option()->set("extend", "white")); + frameAlpha = watermark.extract_band(1).embed( + x, y, width, pageHeight, + 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 (*outType == "jpg" || *outType == "jpeg") { + *outType = "png"; + } + } + VImage content = + img_frame.extract_band(0, VImage::option()->set("n", 3)) + .bandjoin(contentAlpha & img_frame.extract_band(3)); + + composited = + content.composite2(frame, VIPS_BLEND_MODE_OVER, + VImage::option()->set("x", x)->set("y", y)); + } else { + composited = + img_frame.composite2(watermark, VIPS_BLEND_MODE_OVER, + VImage::option()->set("x", x)->set("y", y)); + } + img.push_back(composited); + } + } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight + addedHeight); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" + ? VImage::option()->set("dither", 0)->set("reoptimise", 1) + : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = final.width(); + output["height"] = vips_image_get_page_height(final.get_image()); + + return output; +} diff --git a/natives/watermark.h b/natives/watermark.h index bd861a7..3613cdb 100644 --- a/natives/watermark.h +++ b/natives/watermark.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Watermark(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Watermark(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/whisper.cc b/natives/whisper.cc index abfd01f..a5ad737 100644 --- a/natives/whisper.cc +++ b/natives/whisper.cc @@ -1,86 +1,91 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Whisper(string type, string *outType, char *BufferData, - size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { - string caption = GetArgument(Arguments, "caption"); - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = vips_image_get_n_pages(in.get_image()); - int size = width / 6; - int dividedWidth = width / 175; - int rad = 1; - - string font_string = "Upright, Twemoji Color Font " + to_string(size); - - 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)); - } - - VImage textIn = VImage::text( - ".", VImage::option()->set( - "fontfile", (basePath + "assets/fonts/whisper.otf").c_str())); - textIn = VImage::text( - ("" + caption + "").c_str(), - VImage::option() - ->set("rgba", true) - ->set("align", VIPS_ALIGN_CENTRE) - ->set("font", font_string.c_str()) - ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) - ->set("width", width)); - - 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){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 img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; - img_frame = img_frame.composite2( - textImg, VIPS_BLEND_MODE_OVER, - VImage::option() - ->set("x", (width / 2) - (textImg.width() / 2)) - ->set("y", (pageHeight / 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, pageHeight); - - void *buf; - final.write_to_buffer( - ("." + *outType).c_str(), &buf, DataSize, - *outType == "gif" - ? VImage::option()->set("dither", 0)->set("reoptimise", 1) - : 0); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Whisper(string type, string *outType, char *BufferData, + size_t BufferLength, ArgumentMap Arguments, size_t *DataSize) { + string caption = GetArgument(Arguments, "caption"); + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = vips_image_get_n_pages(in.get_image()); + int size = width / 6; + int dividedWidth = width / 175; + int rad = 1; + + string font_string = "Upright, Twemoji Color Font " + to_string(size); + + 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)); + } + + VImage textIn = VImage::text( + ".", VImage::option()->set( + "fontfile", (basePath + "assets/fonts/whisper.otf").c_str())); + textIn = VImage::text( + ("" + caption + "").c_str(), + VImage::option() + ->set("rgba", true) + ->set("align", VIPS_ALIGN_CENTRE) + ->set("font", font_string.c_str()) + ->set("fontfile", (basePath + "assets/fonts/twemoji.otf").c_str()) + ->set("width", width)); + + 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){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 img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : in; + img_frame = img_frame.composite2( + textImg, VIPS_BLEND_MODE_OVER, + VImage::option() + ->set("x", (width / 2) - (textImg.width() / 2)) + ->set("y", (pageHeight / 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, pageHeight); + + void *buf; + final.write_to_buffer( + ("." + *outType).c_str(), &buf, DataSize, + *outType == "gif" + ? VImage::option()->set("dither", 0)->set("reoptimise", 1) + : 0); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = width; + output["height"] = pageHeight; + + return output; } \ No newline at end of file diff --git a/natives/whisper.h b/natives/whisper.h index 6a008b8..cfeaa30 100644 --- a/natives/whisper.h +++ b/natives/whisper.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Whisper(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Whisper(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/natives/zamn.cc b/natives/zamn.cc index 564807e..a569c4f 100644 --- a/natives/zamn.cc +++ b/natives/zamn.cc @@ -1,47 +1,52 @@ -#include - -#include "common.h" - -using namespace std; -using namespace vips; - -char *Zamn(string type, string *outType, char *BufferData, size_t BufferLength, - ArgumentMap Arguments, size_t *DataSize) { - string basePath = GetArgument(Arguments, "basePath"); - - VOption *options = VImage::option()->set("access", "sequential"); - - VImage in = - VImage::new_from_buffer(BufferData, BufferLength, "", - type == "gif" ? options->set("n", -1) : options) - .colourspace(VIPS_INTERPRETATION_sRGB); - if (!in.has_alpha()) in = in.bandjoin(255); - - int width = in.width(); - int pageHeight = vips_image_get_page_height(in.get_image()); - int nPages = 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 img; - for (int i = 0; i < nPages; i++) { - VImage img_frame = - type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : 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)pageHeight)), - 310, 76); - img.push_back(composited); - } - VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); - final.set(VIPS_META_PAGE_HEIGHT, 516); - - void *buf; - final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); - - return (char *)buf; +#include + +#include "common.h" + +using namespace std; +using namespace vips; + +ArgumentMap Zamn(string type, string *outType, char *BufferData, size_t BufferLength, + ArgumentMap Arguments, size_t *DataSize) { + string basePath = GetArgument(Arguments, "basePath"); + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(BufferData, BufferLength, "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + int width = in.width(); + int pageHeight = vips_image_get_page_height(in.get_image()); + int nPages = 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 img; + for (int i = 0; i < nPages; i++) { + VImage img_frame = + type == "gif" ? in.crop(0, i * pageHeight, width, pageHeight) : 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)pageHeight)), + 310, 76); + img.push_back(composited); + } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, 516); + + void *buf; + final.write_to_buffer(("." + *outType).c_str(), &buf, DataSize); + + ArgumentMap output; + output["buf"] = (char *)buf; + output["width"] = final.width(); + output["height"] = vips_image_get_page_height(final.get_image()); + + return output; } \ No newline at end of file diff --git a/natives/zamn.h b/natives/zamn.h index 2478066..9ba1663 100644 --- a/natives/zamn.h +++ b/natives/zamn.h @@ -1,8 +1,8 @@ -#pragma once - -#include "common.h" - -using std::string; - -char* Zamn(string type, string* outType, char* BufferData, size_t BufferLength, +#pragma once + +#include "common.h" + +using std::string; + +ArgumentMap Zamn(string type, string* outType, char* BufferData, size_t BufferLength, ArgumentMap Arguments, size_t* DataSize); \ No newline at end of file diff --git a/utils/image-runner.js b/utils/image-runner.js index 3ca2ab2..1cde3ef 100644 --- a/utils/image-runner.js +++ b/utils/image-runner.js @@ -50,7 +50,9 @@ export default function run(object) { const result = img.image(object.cmd, objectWithFixedType); const returnObject = { buffer: result.data, - fileExtension: result.type + fileExtension: result.type, + width: result.width, + height: result.height }; resolve(returnObject); } catch (e) { diff --git a/utils/image.js b/utils/image.js index a4d2391..178c652 100644 --- a/utils/image.js +++ b/utils/image.js @@ -135,7 +135,9 @@ function waitForWorker(worker) { worker.once("message", (data) => { resolve({ buffer: Buffer.from([...data.buffer]), - type: data.fileExtension + type: data.fileExtension, + width: data.width, + height: data.height }); }); worker.once("error", reject);