utilities.cc 8.7 KB

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