diff --git a/.gitignore b/.gitignore index 4d28cd9..956ff9a 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ build/ # Dependency directories node_modules/ jspm_packages/ +libvips/ # Optional npm cache directory .npm @@ -127,4 +128,4 @@ local.settings.json # Azurite artifacts __blobstorage__ __queuestorage__ -__azurite_db*__.json \ No newline at end of file +__azurite_db*__.json diff --git a/commands/image-editing/togif.js b/commands/image-editing/togif.js new file mode 100644 index 0000000..d305a99 --- /dev/null +++ b/commands/image-editing/togif.js @@ -0,0 +1,11 @@ +import ImageCommand from "../../classes/imageCommand.js"; + +class ToGIFCommand extends ImageCommand { + static description = "Turns an image into a gif"; + static aliases = ["tgif", "gifify"]; + + static noImage = "You need to provide an image to turn into a GIF!"; + static command = "togif"; +} + +export default ToGIFCommand; diff --git a/natives/image.cc b/natives/image.cc index dab0e2a..b7a5b89 100644 --- a/natives/image.cc +++ b/natives/image.cc @@ -29,6 +29,7 @@ #include "sonic.h" #include "spin.h" #include "tile.h" +#include "togif.h" #include "uncaption.h" #include "wall.h" #include "watermark.h" @@ -77,6 +78,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) exports.Set(Napi::String::New(env, "spin"), Napi::Function::New(env, Spin)); exports.Set(Napi::String::New(env, "swirl"), Napi::Function::New(env, Swirl)); exports.Set(Napi::String::New(env, "tile"), Napi::Function::New(env, Tile)); + exports.Set(Napi::String::New(env, "togif"), Napi::Function::New(env, ToGif)); exports.Set(Napi::String::New(env, "uncaption"), Napi::Function::New(env, Uncaption)); exports.Set(Napi::String::New(env, "wall"), Napi::Function::New(env, Wall)); exports.Set(Napi::String::New(env, "watermark"), Napi::Function::New(env, Watermark)); diff --git a/natives/togif.cc b/natives/togif.cc new file mode 100644 index 0000000..e52751e --- /dev/null +++ b/natives/togif.cc @@ -0,0 +1,56 @@ +#include + +#include + +using namespace std; +using namespace vips; + +Napi::Value ToGif(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + + try { + Napi::Object obj = info[0].As(); + Napi::Buffer data = obj.Get("data").As>(); + string type = obj.Get("type").As().Utf8Value(); + + if (type == "gif") { + Napi::Object result = Napi::Object::New(env); + result.Set("data", data); + result.Set("type", "gif"); + return result; + } + + VOption *options = VImage::option()->set("access", "sequential"); + + VImage in = + VImage::new_from_buffer(data.Data(), data.Length(), "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + int pageHeight = vips_image_get_page_height(in.get_image()); + + vector img; + + img.push_back(in); + + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, pageHeight + in.height()); + + void *buf; + size_t length; + final.write_to_buffer( + ("." + type).c_str(), &buf, &length, VImage::option()->set("dither", 0)); + + vips_thread_shutdown(); + + Napi::Object result = Napi::Object::New(env); + result.Set("data", Napi::Buffer::Copy(env, (char *)buf, length)); + result.Set("type", "gif"); + return result; + } catch (std::exception const &err) { + 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/togif.h b/natives/togif.h new file mode 100644 index 0000000..e03210c --- /dev/null +++ b/natives/togif.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +Napi::Value ToGif(const Napi::CallbackInfo& info);