diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b7bb3d..30233f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,3 +33,7 @@ add_definitions(-DMAGICKCORE_QUANTUM_DEPTH=16) add_definitions(-DMAGICKCORE_HDRI_ENABLE=0) include_directories(${ImageMagick_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} ${ImageMagick_LIBRARIES}) + +pkg_check_modules(VIPS REQUIRED vips-cpp) +include_directories(${VIPS_INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} ${VIPS_LIBRARIES}) diff --git a/commands/image-editing/caption.js b/commands/image-editing/caption.js index 4d2104f..5afecfe 100644 --- a/commands/image-editing/caption.js +++ b/commands/image-editing/caption.js @@ -4,7 +4,7 @@ const allowedFonts = ["futura", "impact", "helvetica", "arial", "roboto", "noto" class CaptionCommand extends ImageCommand { params(url) { const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text; - let newCaption = newArgs.replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%"); + let newCaption = newArgs.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'"); if (process.env.NODE_ENV === "development" && newCaption.toLowerCase() === "get real" && !this.specialArgs.noEgg) newCaption = `I'm tired of people telling me to "get real". Every day I put captions on images for people, some funny and some not, but out of all of those "get real" remains the most used caption. Why? I am simply a computer program running on a server, I am unable to manifest myself into the real world. As such, I'm confused as to why anyone would want me to "get real". Is this form not good enough? Alas, as I am simply a bot, I must follow the tasks that I was originally intended to perform, so here goes:\n${newCaption}`; return { caption: newCaption, diff --git a/natives/caption.cc b/natives/caption.cc index cc1523a..7b0d774 100644 --- a/natives/caption.cc +++ b/natives/caption.cc @@ -1,15 +1,16 @@ -#include +#include +#include #include -#include -#include - using namespace std; -using namespace Magick; +using namespace vips; + +/*void finalizer(Napi::Env env, char* data) { + free(data); +}*/ Napi::Value Caption(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); - try { Napi::Object obj = info[0].As(); Napi::Buffer data = obj.Get("data").As>(); @@ -19,68 +20,68 @@ Napi::Value Caption(const Napi::CallbackInfo &info) { int delay = obj.Has("delay") ? obj.Get("delay").As().Int32Value() : 0; - Blob blob; + VImage in = VImage::new_from_buffer(data.Data(), data.Length(), "", VImage::option() + ->set("n", -1) + ->set("access", "sequential")) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) + in = in.bandjoin(255); - list frames; - list coalesced; - list captioned; - try { - readImages(&frames, Blob(data.Data(), data.Length())); - } catch (Magick::WarningCoder &warning) { - cerr << "Coder Warning: " << warning.what() << endl; - } catch (Magick::Warning &warning) { - cerr << "Warning: " << warning.what() << endl; + int width = in.width(); + int size = width / 10; + int page_height = vips_image_get_page_height(in.get_image()); + int n_pages = vips_image_get_n_pages(in.get_image()); + int textWidth = width - ((width / 25) * 2); + + //char font_string[12 + sizeof(size)]; + //sprintf(font_string, "futura bold %d", size); + + string font_string = (font == "roboto" ? "Roboto Condensed" : font) + " " + (font != "impact" ? "bold" : "normal") + " " + to_string(size); + + string captionText = + "" + caption + ""; + + VImage text = + VImage::text(captionText.c_str(), VImage::option() + ->set("rgba", true) + ->set("align", VIPS_ALIGN_CENTRE) + ->set("font", font_string.c_str()) + ->set("width", textWidth)); + VImage captionImage = ((text == (vector) {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 < n_pages; i++) { + VImage img_frame = in.crop(0, i * page_height, width, page_height); + VImage frame = captionImage.join(img_frame, + VIPS_DIRECTION_VERTICAL, VImage::option() + ->set("background", 0xffffff) + ->set("expand", true)); + img.push_back(frame); } + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, page_height + captionImage.height()); - size_t width = frames.front().baseColumns(); - string query(to_string(width - ((width / 25) * 2)) + "x"); - Image caption_image(Geometry(query), Color("white")); - caption_image.fillColor("black"); - caption_image.alpha(true); - caption_image.fontPointsize((double)width / 13); - caption_image.textGravity(Magick::CenterGravity); - caption_image.read("pango:" + caption + ""); - caption_image.extent(Geometry(width, caption_image.rows() + (width / 13)), - Magick::CenterGravity); + void *buf; + size_t length; + final.write_to_buffer(("." + type).c_str(), &buf, &length, VImage::option() + ->set("dither", 0)); - coalesceImages(&coalesced, frames.begin(), frames.end()); - - for (Image &image : coalesced) { - Image appended; - list images; - image.backgroundColor("white"); - images.push_back(caption_image); - images.push_back(image); - appendImages(&appended, images.begin(), images.end(), true); - appended.repage(); - appended.magick(type); - appended.animationDelay(delay == 0 ? image.animationDelay() : delay); - appended.gifDisposeMethod(Magick::BackgroundDispose); - captioned.push_back(appended); - } - - optimizeTransparency(captioned.begin(), captioned.end()); - - if (type == "gif") { - for (Image &image : captioned) { - image.quantizeDither(false); - image.quantize(); - } - } - - writeImages(captioned.begin(), captioned.end(), &blob); + //vips_shutdown(); + vips_thread_shutdown(); Napi::Object result = Napi::Object::New(env); - result.Set("data", Napi::Buffer::Copy(env, (char *)blob.data(), - blob.length())); + result.Set("data", Napi::Buffer::New(env, (char *)buf, + length)); result.Set("type", type); return result; } catch (std::exception const &err) { + cerr << "Error: " << err.what() << endl; throw Napi::Error::New(env, err.what()); } catch (...) { throw Napi::Error::New(env, "Unknown error"); } -} \ No newline at end of file +} diff --git a/natives/image.cc b/natives/image.cc index fb0ebb5..97c0774 100644 --- a/natives/image.cc +++ b/natives/image.cc @@ -43,12 +43,15 @@ #ifdef _WIN32 #include #endif +#include Napi::Object Init(Napi::Env env, Napi::Object exports) { #ifdef _WIN32 Magick::InitializeMagick(""); #endif + if (vips_init("")) + vips_error_exit(NULL); exports.Set(Napi::String::New(env, "blur"), Napi::Function::New(env, Blur)); exports.Set(Napi::String::New(env, "colors"), Napi::Function::New(env, Colors)); exports.Set(Napi::String::New(env, "caption"), Napi::Function::New(env, Caption)); @@ -91,4 +94,4 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) return exports; } -NODE_API_MODULE(addon, Init); +NODE_API_MODULE(addon, Init)