1 |
|
2 |
|
3 |
|
4 | #include <cmath>
|
5 | #include <string>
|
6 | #include <cstdio>
|
7 |
|
8 | #include <napi.h>
|
9 | #include <vips/vips8>
|
10 | #include <vips/vector.h>
|
11 |
|
12 | #include "common.h"
|
13 | #include "operations.h"
|
14 | #include "utilities.h"
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | Napi::Value cache(const Napi::CallbackInfo& info) {
|
20 | Napi::Env env = info.Env();
|
21 |
|
22 |
|
23 | if (info[size_t(0)].IsNumber()) {
|
24 | vips_cache_set_max_mem(info[size_t(0)].As<Napi::Number>().Int32Value() * 1048576);
|
25 | }
|
26 |
|
27 | if (info[size_t(1)].IsNumber()) {
|
28 | vips_cache_set_max_files(info[size_t(1)].As<Napi::Number>().Int32Value());
|
29 | }
|
30 |
|
31 | if (info[size_t(2)].IsNumber()) {
|
32 | vips_cache_set_max(info[size_t(2)].As<Napi::Number>().Int32Value());
|
33 | }
|
34 |
|
35 |
|
36 | Napi::Object memory = Napi::Object::New(env);
|
37 | memory.Set("current", round(vips_tracked_get_mem() / 1048576));
|
38 | memory.Set("high", round(vips_tracked_get_mem_highwater() / 1048576));
|
39 | memory.Set("max", round(vips_cache_get_max_mem() / 1048576));
|
40 |
|
41 | Napi::Object files = Napi::Object::New(env);
|
42 | files.Set("current", vips_tracked_get_files());
|
43 | files.Set("max", vips_cache_get_max_files());
|
44 |
|
45 |
|
46 | Napi::Object items = Napi::Object::New(env);
|
47 | items.Set("current", vips_cache_get_size());
|
48 | items.Set("max", vips_cache_get_max());
|
49 |
|
50 | Napi::Object cache = Napi::Object::New(env);
|
51 | cache.Set("memory", memory);
|
52 | cache.Set("files", files);
|
53 | cache.Set("items", items);
|
54 | return cache;
|
55 | }
|
56 |
|
57 |
|
58 |
|
59 |
|
60 | Napi::Value concurrency(const Napi::CallbackInfo& info) {
|
61 |
|
62 | if (info[size_t(0)].IsNumber()) {
|
63 | vips_concurrency_set(info[size_t(0)].As<Napi::Number>().Int32Value());
|
64 | }
|
65 |
|
66 | return Napi::Number::New(info.Env(), vips_concurrency_get());
|
67 | }
|
68 |
|
69 |
|
70 |
|
71 |
|
72 | Napi::Value counters(const Napi::CallbackInfo& info) {
|
73 | Napi::Object counters = Napi::Object::New(info.Env());
|
74 | counters.Set("queue", static_cast<int>(sharp::counterQueue));
|
75 | counters.Set("process", static_cast<int>(sharp::counterProcess));
|
76 | return counters;
|
77 | }
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | Napi::Value simd(const Napi::CallbackInfo& info) {
|
83 |
|
84 | if (info[size_t(0)].IsBoolean()) {
|
85 | vips_vector_set_enabled(info[size_t(0)].As<Napi::Boolean>().Value());
|
86 | }
|
87 |
|
88 | return Napi::Boolean::New(info.Env(), vips_vector_isenabled());
|
89 | }
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | Napi::Value libvipsVersion(const Napi::CallbackInfo& info) {
|
95 | Napi::Env env = info.Env();
|
96 | Napi::Object version = Napi::Object::New(env);
|
97 |
|
98 | char semver[9];
|
99 | std::snprintf(semver, sizeof(semver), "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
|
100 | version.Set("semver", Napi::String::New(env, semver));
|
101 | #ifdef SHARP_USE_GLOBAL_LIBVIPS
|
102 | version.Set("isGlobal", Napi::Boolean::New(env, true));
|
103 | #else
|
104 | version.Set("isGlobal", Napi::Boolean::New(env, false));
|
105 | #endif
|
106 | #ifdef __EMSCRIPTEN__
|
107 | version.Set("isWasm", Napi::Boolean::New(env, true));
|
108 | #else
|
109 | version.Set("isWasm", Napi::Boolean::New(env, false));
|
110 | #endif
|
111 | return version;
|
112 | }
|
113 |
|
114 |
|
115 |
|
116 |
|
117 | Napi::Value format(const Napi::CallbackInfo& info) {
|
118 | Napi::Env env = info.Env();
|
119 | Napi::Object format = Napi::Object::New(env);
|
120 | for (std::string const f : {
|
121 | "jpeg", "png", "webp", "tiff", "magick", "openslide", "dz",
|
122 | "ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k", "jxl"
|
123 | }) {
|
124 |
|
125 | const VipsObjectClass *oc = vips_class_find("VipsOperation", (f + "load").c_str());
|
126 | Napi::Boolean hasInputFile = Napi::Boolean::New(env, oc);
|
127 | Napi::Boolean hasInputBuffer =
|
128 | Napi::Boolean::New(env, vips_type_find("VipsOperation", (f + "load_buffer").c_str()));
|
129 | Napi::Object input = Napi::Object::New(env);
|
130 | input.Set("file", hasInputFile);
|
131 | input.Set("buffer", hasInputBuffer);
|
132 | input.Set("stream", hasInputBuffer);
|
133 | if (hasInputFile) {
|
134 | const VipsForeignClass *fc = VIPS_FOREIGN_CLASS(oc);
|
135 | if (fc->suffs) {
|
136 | Napi::Array fileSuffix = Napi::Array::New(env);
|
137 | const char **suffix = fc->suffs;
|
138 | for (int i = 0; *suffix; i++, suffix++) {
|
139 | fileSuffix.Set(i, Napi::String::New(env, *suffix));
|
140 | }
|
141 | input.Set("fileSuffix", fileSuffix);
|
142 | }
|
143 | }
|
144 |
|
145 | Napi::Boolean hasOutputFile =
|
146 | Napi::Boolean::New(env, vips_type_find("VipsOperation", (f + "save").c_str()));
|
147 | Napi::Boolean hasOutputBuffer =
|
148 | Napi::Boolean::New(env, vips_type_find("VipsOperation", (f + "save_buffer").c_str()));
|
149 | Napi::Object output = Napi::Object::New(env);
|
150 | output.Set("file", hasOutputFile);
|
151 | output.Set("buffer", hasOutputBuffer);
|
152 | output.Set("stream", hasOutputBuffer);
|
153 |
|
154 | Napi::Object container = Napi::Object::New(env);
|
155 | container.Set("id", f);
|
156 | container.Set("input", input);
|
157 | container.Set("output", output);
|
158 |
|
159 | format.Set(f, container);
|
160 | }
|
161 |
|
162 |
|
163 | Napi::Boolean supported = Napi::Boolean::New(env, true);
|
164 | Napi::Boolean unsupported = Napi::Boolean::New(env, false);
|
165 | Napi::Object rawInput = Napi::Object::New(env);
|
166 | rawInput.Set("file", unsupported);
|
167 | rawInput.Set("buffer", supported);
|
168 | rawInput.Set("stream", supported);
|
169 | Napi::Object rawOutput = Napi::Object::New(env);
|
170 | rawOutput.Set("file", unsupported);
|
171 | rawOutput.Set("buffer", supported);
|
172 | rawOutput.Set("stream", supported);
|
173 | Napi::Object raw = Napi::Object::New(env);
|
174 | raw.Set("id", "raw");
|
175 | raw.Set("input", rawInput);
|
176 | raw.Set("output", rawOutput);
|
177 | format.Set("raw", raw);
|
178 |
|
179 | return format;
|
180 | }
|
181 |
|
182 |
|
183 |
|
184 |
|
185 | void block(const Napi::CallbackInfo& info) {
|
186 | Napi::Array ops = info[size_t(0)].As<Napi::Array>();
|
187 | bool const state = info[size_t(1)].As<Napi::Boolean>().Value();
|
188 | for (unsigned int i = 0; i < ops.Length(); i++) {
|
189 | vips_operation_block_set(ops.Get(i).As<Napi::String>().Utf8Value().c_str(), state);
|
190 | }
|
191 | }
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 |
|
198 | Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {
|
199 | Napi::Env env = info.Env();
|
200 |
|
201 |
|
202 | VImage image1;
|
203 | sharp::ImageType imageType1 = sharp::DetermineImageType(info[size_t(0)].As<Napi::String>().Utf8Value().data());
|
204 | if (imageType1 != sharp::ImageType::UNKNOWN) {
|
205 | try {
|
206 | image1 = VImage::new_from_file(info[size_t(0)].As<Napi::String>().Utf8Value().c_str());
|
207 | } catch (...) {
|
208 | throw Napi::Error::New(env, "Input file 1 has corrupt header");
|
209 | }
|
210 | } else {
|
211 | throw Napi::Error::New(env, "Input file 1 is of an unsupported image format");
|
212 | }
|
213 | VImage image2;
|
214 | sharp::ImageType imageType2 = sharp::DetermineImageType(info[size_t(1)].As<Napi::String>().Utf8Value().data());
|
215 | if (imageType2 != sharp::ImageType::UNKNOWN) {
|
216 | try {
|
217 | image2 = VImage::new_from_file(info[size_t(1)].As<Napi::String>().Utf8Value().c_str());
|
218 | } catch (...) {
|
219 | throw Napi::Error::New(env, "Input file 2 has corrupt header");
|
220 | }
|
221 | } else {
|
222 | throw Napi::Error::New(env, "Input file 2 is of an unsupported image format");
|
223 | }
|
224 |
|
225 | if (image1.bands() != image2.bands()) {
|
226 | throw Napi::Error::New(env, "mismatchedBands");
|
227 | }
|
228 |
|
229 | if (image1.width() != image2.width() || image1.height() != image2.height()) {
|
230 | throw Napi::Error::New(env, "mismatchedDimensions");
|
231 | }
|
232 |
|
233 | double maxColourDistance;
|
234 | try {
|
235 |
|
236 | if (sharp::HasAlpha(image1)) {
|
237 | image1 = image1.premultiply().extract_band(1, VImage::option()->set("n", image1.bands() - 1));
|
238 | }
|
239 | if (sharp::HasAlpha(image2)) {
|
240 | image2 = image2.premultiply().extract_band(1, VImage::option()->set("n", image2.bands() - 1));
|
241 | }
|
242 |
|
243 | maxColourDistance = image1.dE00(image2).max();
|
244 | } catch (vips::VError const &err) {
|
245 | throw Napi::Error::New(env, err.what());
|
246 | }
|
247 |
|
248 |
|
249 | vips_error_clear();
|
250 | vips_thread_shutdown();
|
251 |
|
252 | return Napi::Number::New(env, maxColourDistance);
|
253 | }
|
254 |
|
255 | #if defined(__GNUC__)
|
256 |
|
257 | extern "C" {
|
258 | int mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) __attribute__((weak));
|
259 | }
|
260 | Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info) {
|
261 | Napi::Env env = info.Env();
|
262 | return Napi::Boolean::New(env, mallctl != nullptr);
|
263 | }
|
264 | #else
|
265 | Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info) {
|
266 | Napi::Env env = info.Env();
|
267 | return Napi::Boolean::New(env, false);
|
268 | }
|
269 | #endif
|