UNPKG

8.92 kBtext/x-cView Raw
1// Copyright 2013 Lovell Fuller and others.
2// SPDX-License-Identifier: Apache-2.0
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 Get and set cache limits
18*/
19Napi::Value cache(const Napi::CallbackInfo& info) {
20 Napi::Env env = info.Env();
21
22 // Set memory limit
23 if (info[size_t(0)].IsNumber()) {
24 vips_cache_set_max_mem(info[size_t(0)].As<Napi::Number>().Int32Value() * 1048576);
25 }
26 // Set file limit
27 if (info[size_t(1)].IsNumber()) {
28 vips_cache_set_max_files(info[size_t(1)].As<Napi::Number>().Int32Value());
29 }
30 // Set items limit
31 if (info[size_t(2)].IsNumber()) {
32 vips_cache_set_max(info[size_t(2)].As<Napi::Number>().Int32Value());
33 }
34
35 // Get memory stats
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 // Get file stats
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 // Get item stats
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 Get and set size of thread pool
59*/
60Napi::Value concurrency(const Napi::CallbackInfo& info) {
61 // Set concurrency
62 if (info[size_t(0)].IsNumber()) {
63 vips_concurrency_set(info[size_t(0)].As<Napi::Number>().Int32Value());
64 }
65 // Get concurrency
66 return Napi::Number::New(info.Env(), vips_concurrency_get());
67}
68
69/*
70 Get internal counters (queued tasks, processing tasks)
71*/
72Napi::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 Get and set use of SIMD vector unit instructions
81*/
82Napi::Value simd(const Napi::CallbackInfo& info) {
83 // Set state
84 if (info[size_t(0)].IsBoolean()) {
85 vips_vector_set_enabled(info[size_t(0)].As<Napi::Boolean>().Value());
86 }
87 // Get state
88 return Napi::Boolean::New(info.Env(), vips_vector_isenabled());
89}
90
91/*
92 Get libvips version
93*/
94Napi::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 Get available input/output file/buffer/stream formats
116*/
117Napi::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 // Input
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 // Output
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 // Other attributes
154 Napi::Object container = Napi::Object::New(env);
155 container.Set("id", f);
156 container.Set("input", input);
157 container.Set("output", output);
158 // Add to set of formats
159 format.Set(f, container);
160 }
161
162 // Raw, uncompressed data
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 (Un)block libvips operations at runtime.
184*/
185void 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 Synchronous, internal-only method used by some of the functional tests.
195 Calculates the maximum colour distance using the DE2000 algorithm
196 between two images of the same dimensions and number of channels.
197*/
198Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {
199 Napi::Env env = info.Env();
200
201 // Open input files
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 // Ensure same number of channels
225 if (image1.bands() != image2.bands()) {
226 throw Napi::Error::New(env, "mismatchedBands");
227 }
228 // Ensure same dimensions
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 // Premultiply and remove alpha
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 // Calculate colour distance
243 maxColourDistance = image1.dE00(image2).max();
244 } catch (vips::VError const &err) {
245 throw Napi::Error::New(env, err.what());
246 }
247
248 // Clean up libvips' per-request data and threads
249 vips_error_clear();
250 vips_thread_shutdown();
251
252 return Napi::Number::New(env, maxColourDistance);
253}
254
255#if defined(__GNUC__)
256// mallctl will be resolved by the runtime linker when jemalloc is being used
257extern "C" {
258 int mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) __attribute__((weak));
259}
260Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info) {
261 Napi::Env env = info.Env();
262 return Napi::Boolean::New(env, mallctl != nullptr);
263}
264#else
265Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info) {
266 Napi::Env env = info.Env();
267 return Napi::Boolean::New(env, false);
268}
269#endif