utilities.cc 9.2 KB

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