From 4a3d880a61bf0fbab54cbefa0c3e10689ce8b032 Mon Sep 17 00:00:00 2001 From: Essem Date: Sun, 27 Feb 2022 15:37:51 -0600 Subject: [PATCH] Port invert, jpeg, and mirror --- natives/invert.cc | 50 ++++++++------------- natives/jpeg.cc | 87 ++++++++++++++++++------------------ natives/mirror.cc | 109 +++++++++++++++++++++------------------------- 3 files changed, 111 insertions(+), 135 deletions(-) diff --git a/natives/invert.cc b/natives/invert.cc index 7b06a6f..474de70 100644 --- a/natives/invert.cc +++ b/natives/invert.cc @@ -1,11 +1,11 @@ -#include #include #include #include +#include using namespace std; -using namespace Magick; +using namespace vips; Napi::Value Invert(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); @@ -17,43 +17,29 @@ Napi::Value Invert(const Napi::CallbackInfo &info) { int delay = obj.Has("delay") ? obj.Get("delay").As().Int32Value() : 0; - Blob blob; + VOption *options = VImage::option()->set("access", "sequential"); - list frames; - list coalesced; - list mid; - try { - readImages(&frames, Blob(data.Data(), data.Length())); - } catch (Magick::WarningCoder &warning) { - cerr << "Coder Warning: " << warning.what() << endl; - } catch (Magick::Warning &warning) { - cerr << "Warning: " << warning.what() << endl; - } - coalesceImages(&coalesced, frames.begin(), frames.end()); + VImage in = + VImage::new_from_buffer(data.Data(), data.Length(), "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); - for_each(coalesced.begin(), coalesced.end(), negateImage()); - for (Image &image : coalesced) { - image.negateChannel(Magick::AlphaChannel); - mid.push_back(image); - } - // Magick::ChannelType(Magick::CompositeChannels ^ Magick::AlphaChannel) - for_each(mid.begin(), mid.end(), magickImage(type)); + VImage noAlpha = + in.extract_band(0, VImage::option()->set("n", in.bands() - 1)); + VImage inverted = noAlpha.invert(); + VImage out = inverted.bandjoin(in.extract_band(3)); - optimizeTransparency(mid.begin(), mid.end()); + if (delay) out.set("delay", delay); - if (type == "gif") { - for (Image &image : mid) { - image.quantizeDitherMethod(FloydSteinbergDitherMethod); - image.quantize(); - if (delay != 0) image.animationDelay(delay); - } - } + void *buf; + size_t length; + out.write_to_buffer(("." + type).c_str(), &buf, &length); - writeImages(mid.begin(), mid.end(), &blob); + vips_thread_shutdown(); Napi::Object result = Napi::Object::New(env); - result.Set("data", Napi::Buffer::Copy(env, (char *)blob.data(), - blob.length())); + result.Set("data", Napi::Buffer::Copy(env, (char *)buf, length)); result.Set("type", type); return result; } catch (std::exception const &err) { diff --git a/natives/jpeg.cc b/natives/jpeg.cc index c77415a..2cecd12 100644 --- a/natives/jpeg.cc +++ b/natives/jpeg.cc @@ -1,11 +1,11 @@ -#include #include #include #include +#include using namespace std; -using namespace Magick; +using namespace vips; Napi::Value Jpeg(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); @@ -13,61 +13,62 @@ Napi::Value Jpeg(const Napi::CallbackInfo &info) { try { Napi::Object obj = info[0].As(); Napi::Buffer data = obj.Get("data").As>(); - int quality = - obj.Has("quality") ? obj.Get("quality").As().Int32Value() : 0; + int quality = obj.Has("quality") + ? obj.Get("quality").As().Int32Value() + : 0; string type = obj.Get("type").As().Utf8Value(); int delay = obj.Has("delay") ? obj.Get("delay").As().Int32Value() : 0; - Blob blob; - Napi::Object result = Napi::Object::New(env); if (type == "gif") { - list frames; - list coalesced; - list jpeged; - try { - readImages(&frames, Blob(data.Data(), data.Length())); - } catch (Magick::WarningCoder &warning) { - cerr << "Coder Warning: " << warning.what() << endl; - } catch (Magick::Warning &warning) { - cerr << "Warning: " << warning.what() << endl; + VImage in = + VImage::new_from_buffer( + data.Data(), data.Length(), "", + VImage::option()->set("access", "sequential")->set("n", -1)) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); + + int width = in.width(); + int page_height = vips_image_get_page_height(in.get_image()); + int n_pages = vips_image_get_n_pages(in.get_image()); + + vector img; + for (int i = 0; i < n_pages; i++) { + VImage img_frame = in.crop(0, i * page_height, width, page_height); + void *buf; + size_t length; + img_frame.write_to_buffer( + ".jpg", &buf, &length, + VImage::option()->set("Q", quality)->set("strip", true)); + VImage jpeg = VImage::new_from_buffer(buf, length, ""); + img.push_back(jpeg); } - coalesceImages(&coalesced, frames.begin(), frames.end()); + VImage final = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + final.set(VIPS_META_PAGE_HEIGHT, page_height); + if (delay) final.set("delay", delay); - for (Image &image : coalesced) { - Blob temp; - image.quality(quality); - image.magick("JPEG"); - image.write(&temp); - Image newImage(temp); - newImage.magick(type); - newImage.animationDelay(delay == 0 ? image.animationDelay() : delay); - jpeged.push_back(newImage); - } + void *buf; + size_t length; + final.write_to_buffer(("." + type).c_str(), &buf, &length, + VImage::option()->set("dither", 0)); - optimizeTransparency(jpeged.begin(), jpeged.end()); + vips_thread_shutdown(); - for (Image &image : jpeged) { - image.quantizeDither(false); - image.quantize(); - } - - writeImages(jpeged.begin(), jpeged.end(), &blob); - - result.Set("data", Napi::Buffer::Copy(env, (char *)blob.data(), - blob.length())); + result.Set("data", Napi::Buffer::Copy(env, (char *)buf, length)); result.Set("type", type); } else { - Image image; - image.read(Blob(data.Data(), data.Length())); - image.quality(1); - image.magick("JPEG"); - image.write(&blob); + VImage in = VImage::new_from_buffer(data.Data(), data.Length(), ""); + void *buf; + size_t length; + in.write_to_buffer( + ".jpg", &buf, &length, + VImage::option()->set("Q", quality)->set("strip", true)); - result.Set("data", Napi::Buffer::Copy(env, (char *)blob.data(), - blob.length())); + vips_thread_shutdown(); + + result.Set("data", Napi::Buffer::Copy(env, (char *)buf, length)); result.Set("type", "jpg"); } diff --git a/natives/mirror.cc b/natives/mirror.cc index fcd77a0..c64b93a 100644 --- a/natives/mirror.cc +++ b/natives/mirror.cc @@ -1,11 +1,11 @@ -#include #include #include #include +#include using namespace std; -using namespace Magick; +using namespace vips; Napi::Value Mirror(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); @@ -22,74 +22,63 @@ Napi::Value Mirror(const Napi::CallbackInfo &info) { int delay = obj.Has("delay") ? obj.Get("delay").As().Int32Value() : 0; - Blob blob; + VOption *options = VImage::option()->set("access", "sequential"); - list frames; - list coalesced; - list mid; - MagickCore::GravityType gravity; - try { - readImages(&frames, Blob(data.Data(), data.Length())); - } catch (Magick::WarningCoder &warning) { - cerr << "Coder Warning: " << warning.what() << endl; - } catch (Magick::Warning &warning) { - cerr << "Warning: " << warning.what() << endl; - } - coalesceImages(&coalesced, frames.begin(), frames.end()); + VImage in = + VImage::new_from_buffer(data.Data(), data.Length(), "", + type == "gif" ? options->set("n", -1) : options) + .colourspace(VIPS_INTERPRETATION_sRGB); + if (!in.has_alpha()) in = in.bandjoin(255); - if (vertical && first) { - gravity = Magick::NorthGravity; - } else if (!vertical && first) { - gravity = Magick::WestGravity; - } else if (vertical && !first) { - gravity = Magick::SouthGravity; + VImage out; + + if (vertical) { + if (type == "gif") { + // once again, libvips gif handling is both a blessing and a curse + vector img; + int page_height = vips_image_get_page_height(in.get_image()); + int n_pages = vips_image_get_n_pages(in.get_image()); + bool isOdd = page_height % 2; + for (int i = 0; i < n_pages; i++) { + int x = (i * page_height) + (first ? 0 : (page_height / 2)); + VImage cropped = in.crop(0, x, in.width(), page_height / 2); + VImage flipped = cropped.flip(VIPS_DIRECTION_VERTICAL); + VImage final = VImage::arrayjoin( + {first ? cropped : flipped, first ? flipped : cropped}, + VImage::option()->set("across", 1)); + img.push_back(final); + } + out = VImage::arrayjoin(img, VImage::option()->set("across", 1)); + out.set(VIPS_META_PAGE_HEIGHT, page_height - (isOdd ? 1 : 0)); + } else { + VImage cropped = in.extract_area(0, 0, in.width(), in.height() / 2); + VImage flipped = cropped.flip(VIPS_DIRECTION_VERTICAL); + out = VImage::arrayjoin({cropped, flipped}, + VImage::option()->set("across", 1)); + } } else { - gravity = Magick::EastGravity; - } - - for (Image &image : coalesced) { - image.colorSpace(Magick::sRGBColorspace); - list mirrored; - Image final; - image.extent(Geometry(to_string(vertical ? image.baseColumns() - : image.baseColumns() / 2) + - "x" + - to_string(vertical ? image.baseRows() / 2 - : image.baseRows())), - gravity); - mirrored.push_back(image); - Image mirror = image; - if (vertical) { - mirror.flip(); - } else { - mirror.flop(); - } if (first) { - mirrored.push_back(mirror); + VImage cropped = in.extract_area(0, 0, in.width() / 2, in.height()); + VImage flipped = cropped.flip(VIPS_DIRECTION_HORIZONTAL); + out = VImage::arrayjoin({cropped, flipped}); } else { - mirrored.push_front(mirror); - } - appendImages(&final, mirrored.begin(), mirrored.end(), vertical); - final.repage(); - final.magick(type); - final.animationDelay(delay == 0 ? image.animationDelay() : delay); - mid.push_back(final); - } - - optimizeTransparency(mid.begin(), mid.end()); - - if (type == "gif") { - for (Image &image : mid) { - image.quantizeDither(false); - image.quantize(); + int size = in.width() / 2; + VImage cropped = in.extract_area(size, 0, size, in.height()); + VImage flipped = cropped.flip(VIPS_DIRECTION_HORIZONTAL); + out = VImage::arrayjoin({flipped, cropped}); } } - writeImages(mid.begin(), mid.end(), &blob); + if (delay) out.set("delay", delay); + + void *buf; + size_t length; + out.write_to_buffer(("." + type).c_str(), &buf, &length); + + vips_thread_shutdown(); Napi::Object result = Napi::Object::New(env); - result.Set("data", Napi::Buffer::Copy(env, (char *)blob.data(), - blob.length())); + result.Set("data", Napi::Buffer::Copy(env, (char *)buf, length)); result.Set("type", type); return result; } catch (std::exception const &err) {