@basemaps/lambda-tiler 8.9.0 → 8.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/CHANGELOG.md +56 -22
  2. package/build/__tests__/config.data.js +14 -0
  3. package/build/__tests__/config.data.js.map +1 -1
  4. package/build/cli/render.tile.js +25 -9
  5. package/build/cli/render.tile.js.map +1 -1
  6. package/build/routes/tile.style.json.js +27 -5
  7. package/build/routes/tile.style.json.js.map +1 -1
  8. package/package.json +8 -8
  9. package/src/__tests__/config.data.ts +14 -0
  10. package/src/cli/render.tile.ts +27 -8
  11. package/src/routes/tile.style.json.ts +26 -5
  12. package/tsconfig.tsbuildinfo +1 -1
  13. package/dist/index.js +0 -94950
  14. package/dist/node_modules/.package-lock.json +0 -179
  15. package/dist/node_modules/@img/sharp-libvips-linux-arm64/README.md +0 -47
  16. package/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/glib-2.0/include/glibconfig.h +0 -219
  17. package/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/index.js +0 -1
  18. package/dist/node_modules/@img/sharp-libvips-linux-arm64/lib/libvips-cpp.so.42 +0 -0
  19. package/dist/node_modules/@img/sharp-libvips-linux-arm64/package.json +0 -45
  20. package/dist/node_modules/@img/sharp-libvips-linux-arm64/versions.json +0 -31
  21. package/dist/node_modules/@img/sharp-linux-arm64/LICENSE +0 -191
  22. package/dist/node_modules/@img/sharp-linux-arm64/README.md +0 -18
  23. package/dist/node_modules/@img/sharp-linux-arm64/lib/sharp-linux-arm64.node +0 -0
  24. package/dist/node_modules/@img/sharp-linux-arm64/package.json +0 -47
  25. package/dist/node_modules/color/LICENSE +0 -21
  26. package/dist/node_modules/color/README.md +0 -123
  27. package/dist/node_modules/color/index.js +0 -496
  28. package/dist/node_modules/color/package.json +0 -47
  29. package/dist/node_modules/color-convert/CHANGELOG.md +0 -54
  30. package/dist/node_modules/color-convert/LICENSE +0 -21
  31. package/dist/node_modules/color-convert/README.md +0 -68
  32. package/dist/node_modules/color-convert/conversions.js +0 -839
  33. package/dist/node_modules/color-convert/index.js +0 -81
  34. package/dist/node_modules/color-convert/package.json +0 -48
  35. package/dist/node_modules/color-convert/route.js +0 -97
  36. package/dist/node_modules/color-name/LICENSE +0 -8
  37. package/dist/node_modules/color-name/README.md +0 -11
  38. package/dist/node_modules/color-name/index.js +0 -152
  39. package/dist/node_modules/color-name/package.json +0 -28
  40. package/dist/node_modules/color-string/LICENSE +0 -21
  41. package/dist/node_modules/color-string/README.md +0 -62
  42. package/dist/node_modules/color-string/index.js +0 -242
  43. package/dist/node_modules/color-string/package.json +0 -39
  44. package/dist/node_modules/detect-libc/LICENSE +0 -201
  45. package/dist/node_modules/detect-libc/README.md +0 -163
  46. package/dist/node_modules/detect-libc/index.d.ts +0 -14
  47. package/dist/node_modules/detect-libc/lib/detect-libc.js +0 -267
  48. package/dist/node_modules/detect-libc/lib/filesystem.js +0 -41
  49. package/dist/node_modules/detect-libc/lib/process.js +0 -24
  50. package/dist/node_modules/detect-libc/package.json +0 -41
  51. package/dist/node_modules/is-arrayish/LICENSE +0 -21
  52. package/dist/node_modules/is-arrayish/README.md +0 -16
  53. package/dist/node_modules/is-arrayish/index.js +0 -9
  54. package/dist/node_modules/is-arrayish/package.json +0 -45
  55. package/dist/node_modules/is-arrayish/yarn-error.log +0 -1443
  56. package/dist/node_modules/lerc/CHANGELOG.md +0 -69
  57. package/dist/node_modules/lerc/LercDecode.d.ts +0 -61
  58. package/dist/node_modules/lerc/LercDecode.es.d.ts +0 -61
  59. package/dist/node_modules/lerc/LercDecode.es.js +0 -434
  60. package/dist/node_modules/lerc/LercDecode.es.min.js +0 -17
  61. package/dist/node_modules/lerc/LercDecode.js +0 -448
  62. package/dist/node_modules/lerc/LercDecode.min.js +0 -17
  63. package/dist/node_modules/lerc/README.md +0 -123
  64. package/dist/node_modules/lerc/lerc-wasm.wasm +0 -0
  65. package/dist/node_modules/lerc/package.json +0 -30
  66. package/dist/node_modules/semver/LICENSE +0 -15
  67. package/dist/node_modules/semver/README.md +0 -664
  68. package/dist/node_modules/semver/bin/semver.js +0 -191
  69. package/dist/node_modules/semver/classes/comparator.js +0 -143
  70. package/dist/node_modules/semver/classes/index.js +0 -7
  71. package/dist/node_modules/semver/classes/range.js +0 -556
  72. package/dist/node_modules/semver/classes/semver.js +0 -319
  73. package/dist/node_modules/semver/functions/clean.js +0 -8
  74. package/dist/node_modules/semver/functions/cmp.js +0 -54
  75. package/dist/node_modules/semver/functions/coerce.js +0 -62
  76. package/dist/node_modules/semver/functions/compare-build.js +0 -9
  77. package/dist/node_modules/semver/functions/compare-loose.js +0 -5
  78. package/dist/node_modules/semver/functions/compare.js +0 -7
  79. package/dist/node_modules/semver/functions/diff.js +0 -60
  80. package/dist/node_modules/semver/functions/eq.js +0 -5
  81. package/dist/node_modules/semver/functions/gt.js +0 -5
  82. package/dist/node_modules/semver/functions/gte.js +0 -5
  83. package/dist/node_modules/semver/functions/inc.js +0 -21
  84. package/dist/node_modules/semver/functions/lt.js +0 -5
  85. package/dist/node_modules/semver/functions/lte.js +0 -5
  86. package/dist/node_modules/semver/functions/major.js +0 -5
  87. package/dist/node_modules/semver/functions/minor.js +0 -5
  88. package/dist/node_modules/semver/functions/neq.js +0 -5
  89. package/dist/node_modules/semver/functions/parse.js +0 -18
  90. package/dist/node_modules/semver/functions/patch.js +0 -5
  91. package/dist/node_modules/semver/functions/prerelease.js +0 -8
  92. package/dist/node_modules/semver/functions/rcompare.js +0 -5
  93. package/dist/node_modules/semver/functions/rsort.js +0 -5
  94. package/dist/node_modules/semver/functions/satisfies.js +0 -12
  95. package/dist/node_modules/semver/functions/sort.js +0 -5
  96. package/dist/node_modules/semver/functions/valid.js +0 -8
  97. package/dist/node_modules/semver/index.js +0 -91
  98. package/dist/node_modules/semver/internal/constants.js +0 -37
  99. package/dist/node_modules/semver/internal/debug.js +0 -11
  100. package/dist/node_modules/semver/internal/identifiers.js +0 -25
  101. package/dist/node_modules/semver/internal/lrucache.js +0 -42
  102. package/dist/node_modules/semver/internal/parse-options.js +0 -17
  103. package/dist/node_modules/semver/internal/re.js +0 -223
  104. package/dist/node_modules/semver/package.json +0 -78
  105. package/dist/node_modules/semver/preload.js +0 -4
  106. package/dist/node_modules/semver/range.bnf +0 -16
  107. package/dist/node_modules/semver/ranges/gtr.js +0 -6
  108. package/dist/node_modules/semver/ranges/intersects.js +0 -9
  109. package/dist/node_modules/semver/ranges/ltr.js +0 -6
  110. package/dist/node_modules/semver/ranges/max-satisfying.js +0 -27
  111. package/dist/node_modules/semver/ranges/min-satisfying.js +0 -26
  112. package/dist/node_modules/semver/ranges/min-version.js +0 -63
  113. package/dist/node_modules/semver/ranges/outside.js +0 -82
  114. package/dist/node_modules/semver/ranges/simplify.js +0 -49
  115. package/dist/node_modules/semver/ranges/subset.js +0 -249
  116. package/dist/node_modules/semver/ranges/to-comparators.js +0 -10
  117. package/dist/node_modules/semver/ranges/valid.js +0 -13
  118. package/dist/node_modules/sharp/LICENSE +0 -191
  119. package/dist/node_modules/sharp/README.md +0 -118
  120. package/dist/node_modules/sharp/install/check.js +0 -36
  121. package/dist/node_modules/sharp/lib/channel.js +0 -174
  122. package/dist/node_modules/sharp/lib/colour.js +0 -182
  123. package/dist/node_modules/sharp/lib/composite.js +0 -210
  124. package/dist/node_modules/sharp/lib/constructor.js +0 -444
  125. package/dist/node_modules/sharp/lib/index.d.ts +0 -1717
  126. package/dist/node_modules/sharp/lib/index.js +0 -16
  127. package/dist/node_modules/sharp/lib/input.js +0 -657
  128. package/dist/node_modules/sharp/lib/is.js +0 -169
  129. package/dist/node_modules/sharp/lib/libvips.js +0 -171
  130. package/dist/node_modules/sharp/lib/operation.js +0 -919
  131. package/dist/node_modules/sharp/lib/output.js +0 -1561
  132. package/dist/node_modules/sharp/lib/resize.js +0 -582
  133. package/dist/node_modules/sharp/lib/sharp.js +0 -86
  134. package/dist/node_modules/sharp/lib/utility.js +0 -287
  135. package/dist/node_modules/sharp/package.json +0 -219
  136. package/dist/node_modules/sharp/src/binding.gyp +0 -277
  137. package/dist/node_modules/sharp/src/common.cc +0 -1090
  138. package/dist/node_modules/sharp/src/common.h +0 -393
  139. package/dist/node_modules/sharp/src/metadata.cc +0 -287
  140. package/dist/node_modules/sharp/src/metadata.h +0 -82
  141. package/dist/node_modules/sharp/src/operations.cc +0 -471
  142. package/dist/node_modules/sharp/src/operations.h +0 -125
  143. package/dist/node_modules/sharp/src/pipeline.cc +0 -1724
  144. package/dist/node_modules/sharp/src/pipeline.h +0 -385
  145. package/dist/node_modules/sharp/src/sharp.cc +0 -40
  146. package/dist/node_modules/sharp/src/stats.cc +0 -183
  147. package/dist/node_modules/sharp/src/stats.h +0 -59
  148. package/dist/node_modules/sharp/src/utilities.cc +0 -269
  149. package/dist/node_modules/sharp/src/utilities.h +0 -19
  150. package/dist/node_modules/simple-swizzle/LICENSE +0 -21
  151. package/dist/node_modules/simple-swizzle/README.md +0 -39
  152. package/dist/node_modules/simple-swizzle/index.js +0 -29
  153. package/dist/node_modules/simple-swizzle/package.json +0 -36
  154. package/dist/package-lock.json +0 -610
  155. package/dist/package.json +0 -40
  156. package/dist/static/expected_tile_2193_153_255_z7.png +0 -0
  157. package/dist/static/expected_tile_NZTM2000Quad_30_33_z6.png +0 -0
  158. package/dist/static/expected_tile_WebMercatorQuad_252_156_z8.png +0 -0
@@ -1,1724 +0,0 @@
1
- // Copyright 2013 Lovell Fuller and others.
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
- #include <algorithm>
5
- #include <cmath>
6
- #include <map>
7
- #include <memory>
8
- #include <numeric>
9
- #include <string>
10
- #include <tuple>
11
- #include <utility>
12
- #include <vector>
13
- #include <sys/types.h>
14
- #include <sys/stat.h>
15
-
16
- #include <vips/vips8>
17
- #include <napi.h>
18
-
19
- #include "common.h"
20
- #include "operations.h"
21
- #include "pipeline.h"
22
-
23
- #ifdef _WIN32
24
- #define STAT64_STRUCT __stat64
25
- #define STAT64_FUNCTION _stat64
26
- #elif defined(_LARGEFILE64_SOURCE)
27
- #define STAT64_STRUCT stat64
28
- #define STAT64_FUNCTION stat64
29
- #else
30
- #define STAT64_STRUCT stat
31
- #define STAT64_FUNCTION stat
32
- #endif
33
-
34
- class PipelineWorker : public Napi::AsyncWorker {
35
- public:
36
- PipelineWorker(Napi::Function callback, PipelineBaton *baton,
37
- Napi::Function debuglog, Napi::Function queueListener) :
38
- Napi::AsyncWorker(callback),
39
- baton(baton),
40
- debuglog(Napi::Persistent(debuglog)),
41
- queueListener(Napi::Persistent(queueListener)) {}
42
- ~PipelineWorker() {}
43
-
44
- // libuv worker
45
- void Execute() {
46
- // Decrement queued task counter
47
- sharp::counterQueue--;
48
- // Increment processing task counter
49
- sharp::counterProcess++;
50
-
51
- try {
52
- // Open input
53
- vips::VImage image;
54
- sharp::ImageType inputImageType;
55
- std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
56
- VipsAccess access = baton->input->access;
57
- image = sharp::EnsureColourspace(image, baton->colourspaceInput);
58
-
59
- int nPages = baton->input->pages;
60
- if (nPages == -1) {
61
- // Resolve the number of pages if we need to render until the end of the document
62
- nPages = image.get_typeof(VIPS_META_N_PAGES) != 0
63
- ? image.get_int(VIPS_META_N_PAGES) - baton->input->page
64
- : 1;
65
- }
66
-
67
- // Get pre-resize page height
68
- int pageHeight = sharp::GetPageHeight(image);
69
-
70
- // Calculate angle of rotation
71
- VipsAngle rotation = VIPS_ANGLE_D0;
72
- VipsAngle autoRotation = VIPS_ANGLE_D0;
73
- bool autoFlip = FALSE;
74
- bool autoFlop = FALSE;
75
-
76
- if (baton->useExifOrientation) {
77
- // Rotate and flip image according to Exif orientation
78
- std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
79
- image = sharp::RemoveExifOrientation(image);
80
- } else {
81
- rotation = CalculateAngleRotation(baton->angle);
82
- }
83
-
84
- // Rotate pre-extract
85
- bool const shouldRotateBefore = baton->rotateBeforePreExtract &&
86
- (rotation != VIPS_ANGLE_D0 || autoRotation != VIPS_ANGLE_D0 ||
87
- autoFlip || baton->flip || autoFlop || baton->flop ||
88
- baton->rotationAngle != 0.0);
89
-
90
- if (shouldRotateBefore) {
91
- image = sharp::StaySequential(image, access,
92
- rotation != VIPS_ANGLE_D0 ||
93
- autoRotation != VIPS_ANGLE_D0 ||
94
- autoFlip ||
95
- baton->flip ||
96
- baton->rotationAngle != 0.0);
97
-
98
- if (autoRotation != VIPS_ANGLE_D0) {
99
- image = image.rot(autoRotation);
100
- autoRotation = VIPS_ANGLE_D0;
101
- }
102
- if (autoFlip) {
103
- image = image.flip(VIPS_DIRECTION_VERTICAL);
104
- autoFlip = FALSE;
105
- } else if (baton->flip) {
106
- image = image.flip(VIPS_DIRECTION_VERTICAL);
107
- baton->flip = FALSE;
108
- }
109
- if (autoFlop) {
110
- image = image.flip(VIPS_DIRECTION_HORIZONTAL);
111
- autoFlop = FALSE;
112
- } else if (baton->flop) {
113
- image = image.flip(VIPS_DIRECTION_HORIZONTAL);
114
- baton->flop = FALSE;
115
- }
116
- if (rotation != VIPS_ANGLE_D0) {
117
- image = image.rot(rotation);
118
- rotation = VIPS_ANGLE_D0;
119
- }
120
- if (baton->rotationAngle != 0.0) {
121
- MultiPageUnsupported(nPages, "Rotate");
122
- std::vector<double> background;
123
- std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, FALSE);
124
- image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)).copy_memory();
125
- }
126
- }
127
-
128
- // Trim
129
- if (baton->trimThreshold >= 0.0) {
130
- MultiPageUnsupported(nPages, "Trim");
131
- image = sharp::StaySequential(image, access);
132
- image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold, baton->trimLineArt);
133
- baton->trimOffsetLeft = image.xoffset();
134
- baton->trimOffsetTop = image.yoffset();
135
- }
136
-
137
- // Pre extraction
138
- if (baton->topOffsetPre != -1) {
139
- image = nPages > 1
140
- ? sharp::CropMultiPage(image,
141
- baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre, nPages, &pageHeight)
142
- : image.extract_area(baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre);
143
- }
144
-
145
- // Get pre-resize image width and height
146
- int inputWidth = image.width();
147
- int inputHeight = image.height();
148
-
149
- // Is there just one page? Shrink to inputHeight instead
150
- if (nPages == 1) {
151
- pageHeight = inputHeight;
152
- }
153
-
154
- // Scaling calculations
155
- double hshrink;
156
- double vshrink;
157
- int targetResizeWidth = baton->width;
158
- int targetResizeHeight = baton->height;
159
-
160
- // When auto-rotating by 90 or 270 degrees, swap the target width and
161
- // height to ensure the behavior aligns with how it would have been if
162
- // the rotation had taken place *before* resizing.
163
- if (!baton->rotateBeforePreExtract &&
164
- (autoRotation == VIPS_ANGLE_D90 || autoRotation == VIPS_ANGLE_D270)) {
165
- std::swap(targetResizeWidth, targetResizeHeight);
166
- }
167
-
168
- // Shrink to pageHeight, so we work for multi-page images
169
- std::tie(hshrink, vshrink) = sharp::ResolveShrink(
170
- inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
171
- baton->canvas, baton->withoutEnlargement, baton->withoutReduction);
172
-
173
- // The jpeg preload shrink.
174
- int jpegShrinkOnLoad = 1;
175
-
176
- // WebP, PDF, SVG scale
177
- double scale = 1.0;
178
-
179
- // Try to reload input using shrink-on-load for JPEG, WebP, SVG and PDF, when:
180
- // - the width or height parameters are specified;
181
- // - gamma correction doesn't need to be applied;
182
- // - trimming or pre-resize extract isn't required;
183
- // - input colourspace is not specified;
184
- bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) &&
185
- baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold < 0.0 &&
186
- baton->colourspaceInput == VIPS_INTERPRETATION_LAST && !shouldRotateBefore;
187
-
188
- if (shouldPreShrink) {
189
- // The common part of the shrink: the bit by which both axes must be shrunk
190
- double shrink = std::min(hshrink, vshrink);
191
-
192
- if (inputImageType == sharp::ImageType::JPEG) {
193
- // Leave at least a factor of two for the final resize step, when fastShrinkOnLoad: false
194
- // for more consistent results and to avoid extra sharpness to the image
195
- int factor = baton->fastShrinkOnLoad ? 1 : 2;
196
- if (shrink >= 8 * factor) {
197
- jpegShrinkOnLoad = 8;
198
- } else if (shrink >= 4 * factor) {
199
- jpegShrinkOnLoad = 4;
200
- } else if (shrink >= 2 * factor) {
201
- jpegShrinkOnLoad = 2;
202
- }
203
- // Lower shrink-on-load for known libjpeg rounding errors
204
- if (jpegShrinkOnLoad > 1 && static_cast<int>(shrink) == jpegShrinkOnLoad) {
205
- jpegShrinkOnLoad /= 2;
206
- }
207
- } else if (inputImageType == sharp::ImageType::WEBP && baton->fastShrinkOnLoad && shrink > 1.0) {
208
- // Avoid upscaling via webp
209
- scale = 1.0 / shrink;
210
- } else if (inputImageType == sharp::ImageType::SVG ||
211
- inputImageType == sharp::ImageType::PDF) {
212
- scale = 1.0 / shrink;
213
- }
214
- }
215
-
216
- // Reload input using shrink-on-load, it'll be an integer shrink
217
- // factor for jpegload*, a double scale factor for webpload*,
218
- // pdfload* and svgload*
219
- if (jpegShrinkOnLoad > 1) {
220
- vips::VOption *option = VImage::option()
221
- ->set("access", access)
222
- ->set("shrink", jpegShrinkOnLoad)
223
- ->set("unlimited", baton->input->unlimited)
224
- ->set("fail_on", baton->input->failOn);
225
- if (baton->input->buffer != nullptr) {
226
- // Reload JPEG buffer
227
- VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
228
- image = VImage::jpegload_buffer(blob, option);
229
- vips_area_unref(reinterpret_cast<VipsArea*>(blob));
230
- } else {
231
- // Reload JPEG file
232
- image = VImage::jpegload(const_cast<char*>(baton->input->file.data()), option);
233
- }
234
- } else if (scale != 1.0) {
235
- vips::VOption *option = VImage::option()
236
- ->set("access", access)
237
- ->set("scale", scale)
238
- ->set("fail_on", baton->input->failOn);
239
- if (inputImageType == sharp::ImageType::WEBP) {
240
- option->set("n", baton->input->pages);
241
- option->set("page", baton->input->page);
242
-
243
- if (baton->input->buffer != nullptr) {
244
- // Reload WebP buffer
245
- VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
246
- image = VImage::webpload_buffer(blob, option);
247
- vips_area_unref(reinterpret_cast<VipsArea*>(blob));
248
- } else {
249
- // Reload WebP file
250
- image = VImage::webpload(const_cast<char*>(baton->input->file.data()), option);
251
- }
252
- } else if (inputImageType == sharp::ImageType::SVG) {
253
- option->set("unlimited", baton->input->unlimited);
254
- option->set("dpi", baton->input->density);
255
-
256
- if (baton->input->buffer != nullptr) {
257
- // Reload SVG buffer
258
- VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
259
- image = VImage::svgload_buffer(blob, option);
260
- vips_area_unref(reinterpret_cast<VipsArea*>(blob));
261
- } else {
262
- // Reload SVG file
263
- image = VImage::svgload(const_cast<char*>(baton->input->file.data()), option);
264
- }
265
- sharp::SetDensity(image, baton->input->density);
266
- if (image.width() > 32767 || image.height() > 32767) {
267
- throw vips::VError("Input SVG image will exceed 32767x32767 pixel limit when scaled");
268
- }
269
- } else if (inputImageType == sharp::ImageType::PDF) {
270
- option->set("n", baton->input->pages);
271
- option->set("page", baton->input->page);
272
- option->set("dpi", baton->input->density);
273
-
274
- if (baton->input->buffer != nullptr) {
275
- // Reload PDF buffer
276
- VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
277
- image = VImage::pdfload_buffer(blob, option);
278
- vips_area_unref(reinterpret_cast<VipsArea*>(blob));
279
- } else {
280
- // Reload PDF file
281
- image = VImage::pdfload(const_cast<char*>(baton->input->file.data()), option);
282
- }
283
-
284
- sharp::SetDensity(image, baton->input->density);
285
- }
286
- } else {
287
- if (inputImageType == sharp::ImageType::SVG && (image.width() > 32767 || image.height() > 32767)) {
288
- throw vips::VError("Input SVG image exceeds 32767x32767 pixel limit");
289
- }
290
- }
291
-
292
- // Any pre-shrinking may already have been done
293
- inputWidth = image.width();
294
- inputHeight = image.height();
295
-
296
- // After pre-shrink, but before the main shrink stage
297
- // Reuse the initial pageHeight if we didn't pre-shrink
298
- if (shouldPreShrink) {
299
- pageHeight = sharp::GetPageHeight(image);
300
- }
301
-
302
- // Shrink to pageHeight, so we work for multi-page images
303
- std::tie(hshrink, vshrink) = sharp::ResolveShrink(
304
- inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
305
- baton->canvas, baton->withoutEnlargement, baton->withoutReduction);
306
-
307
- int targetHeight = static_cast<int>(std::rint(static_cast<double>(pageHeight) / vshrink));
308
- int targetPageHeight = targetHeight;
309
-
310
- // In toilet-roll mode, we must adjust vshrink so that we exactly hit
311
- // pageHeight or we'll have pixels straddling pixel boundaries
312
- if (inputHeight > pageHeight) {
313
- targetHeight *= nPages;
314
- vshrink = static_cast<double>(inputHeight) / targetHeight;
315
- }
316
-
317
- // Ensure we're using a device-independent colour space
318
- std::pair<char*, size_t> inputProfile(nullptr, 0);
319
- if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) && baton->withIccProfile.empty()) {
320
- // Cache input profile for use with output
321
- inputProfile = sharp::GetProfile(image);
322
- }
323
- char const *processingProfile = image.interpretation() == VIPS_INTERPRETATION_RGB16 ? "p3" : "srgb";
324
- if (
325
- sharp::HasProfile(image) &&
326
- image.interpretation() != VIPS_INTERPRETATION_LABS &&
327
- image.interpretation() != VIPS_INTERPRETATION_GREY16 &&
328
- !baton->input->ignoreIcc
329
- ) {
330
- // Convert to sRGB/P3 using embedded profile
331
- try {
332
- image = image.icc_transform(processingProfile, VImage::option()
333
- ->set("embedded", TRUE)
334
- ->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
335
- ->set("intent", VIPS_INTENT_PERCEPTUAL));
336
- } catch(...) {
337
- sharp::VipsWarningCallback(nullptr, G_LOG_LEVEL_WARNING, "Invalid embedded profile", nullptr);
338
- }
339
- } else if (
340
- image.interpretation() == VIPS_INTERPRETATION_CMYK &&
341
- baton->colourspaceInput != VIPS_INTERPRETATION_CMYK
342
- ) {
343
- image = image.icc_transform(processingProfile, VImage::option()
344
- ->set("input_profile", "cmyk")
345
- ->set("intent", VIPS_INTENT_PERCEPTUAL));
346
- }
347
-
348
- // Flatten image to remove alpha channel
349
- if (baton->flatten && sharp::HasAlpha(image)) {
350
- image = sharp::Flatten(image, baton->flattenBackground);
351
- }
352
-
353
- // Negate the colours in the image
354
- if (baton->negate) {
355
- image = sharp::Negate(image, baton->negateAlpha);
356
- }
357
-
358
- // Gamma encoding (darken)
359
- if (baton->gamma >= 1 && baton->gamma <= 3) {
360
- image = sharp::Gamma(image, 1.0 / baton->gamma);
361
- }
362
-
363
- // Convert to greyscale (linear, therefore after gamma encoding, if any)
364
- if (baton->greyscale) {
365
- image = image.colourspace(VIPS_INTERPRETATION_B_W);
366
- }
367
-
368
- bool const shouldResize = hshrink != 1.0 || vshrink != 1.0;
369
- bool const shouldBlur = baton->blurSigma != 0.0;
370
- bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0;
371
- bool const shouldSharpen = baton->sharpenSigma != 0.0;
372
- bool const shouldComposite = !baton->composite.empty();
373
-
374
- if (shouldComposite && !sharp::HasAlpha(image)) {
375
- image = sharp::EnsureAlpha(image, 1);
376
- }
377
-
378
- VipsBandFormat premultiplyFormat = image.format();
379
- bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
380
- (shouldResize || shouldBlur || shouldConv || shouldSharpen);
381
-
382
- if (shouldPremultiplyAlpha) {
383
- image = image.premultiply().cast(premultiplyFormat);
384
- }
385
-
386
- // Resize
387
- if (shouldResize) {
388
- image = image.resize(1.0 / hshrink, VImage::option()
389
- ->set("vscale", 1.0 / vshrink)
390
- ->set("kernel", baton->kernel));
391
- }
392
-
393
- image = sharp::StaySequential(image, access,
394
- autoRotation != VIPS_ANGLE_D0 ||
395
- baton->flip ||
396
- autoFlip ||
397
- rotation != VIPS_ANGLE_D0);
398
- // Auto-rotate post-extract
399
- if (autoRotation != VIPS_ANGLE_D0) {
400
- image = image.rot(autoRotation);
401
- }
402
- // Mirror vertically (up-down) about the x-axis
403
- if (baton->flip || autoFlip) {
404
- image = image.flip(VIPS_DIRECTION_VERTICAL);
405
- }
406
- // Mirror horizontally (left-right) about the y-axis
407
- if (baton->flop || autoFlop) {
408
- image = image.flip(VIPS_DIRECTION_HORIZONTAL);
409
- }
410
- // Rotate post-extract 90-angle
411
- if (rotation != VIPS_ANGLE_D0) {
412
- image = image.rot(rotation);
413
- }
414
-
415
- // Join additional color channels to the image
416
- if (!baton->joinChannelIn.empty()) {
417
- VImage joinImage;
418
- sharp::ImageType joinImageType = sharp::ImageType::UNKNOWN;
419
-
420
- for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
421
- baton->joinChannelIn[i]->access = access;
422
- std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
423
- joinImage = sharp::EnsureColourspace(joinImage, baton->colourspaceInput);
424
- image = image.bandjoin(joinImage);
425
- }
426
- image = image.copy(VImage::option()->set("interpretation", baton->colourspace));
427
- image = sharp::RemoveGifPalette(image);
428
- }
429
-
430
- inputWidth = image.width();
431
- inputHeight = nPages > 1 ? targetPageHeight : image.height();
432
-
433
- // Resolve dimensions
434
- if (baton->width <= 0) {
435
- baton->width = inputWidth;
436
- }
437
- if (baton->height <= 0) {
438
- baton->height = inputHeight;
439
- }
440
-
441
- // Crop/embed
442
- if (inputWidth != baton->width || inputHeight != baton->height) {
443
- if (baton->canvas == sharp::Canvas::EMBED) {
444
- std::vector<double> background;
445
- std::tie(image, background) = sharp::ApplyAlpha(image, baton->resizeBackground, shouldPremultiplyAlpha);
446
-
447
- // Embed
448
- int left;
449
- int top;
450
- std::tie(left, top) = sharp::CalculateEmbedPosition(
451
- inputWidth, inputHeight, baton->width, baton->height, baton->position);
452
- int width = std::max(inputWidth, baton->width);
453
- int height = std::max(inputHeight, baton->height);
454
-
455
- image = nPages > 1
456
- ? sharp::EmbedMultiPage(image,
457
- left, top, width, height, VIPS_EXTEND_BACKGROUND, background, nPages, &targetPageHeight)
458
- : image.embed(left, top, width, height, VImage::option()
459
- ->set("extend", VIPS_EXTEND_BACKGROUND)
460
- ->set("background", background));
461
- } else if (baton->canvas == sharp::Canvas::CROP) {
462
- if (baton->width > inputWidth) {
463
- baton->width = inputWidth;
464
- }
465
- if (baton->height > inputHeight) {
466
- baton->height = inputHeight;
467
- }
468
-
469
- // Crop
470
- if (baton->position < 9) {
471
- // Gravity-based crop
472
- int left;
473
- int top;
474
-
475
- std::tie(left, top) = sharp::CalculateCrop(
476
- inputWidth, inputHeight, baton->width, baton->height, baton->position);
477
- int width = std::min(inputWidth, baton->width);
478
- int height = std::min(inputHeight, baton->height);
479
-
480
- image = nPages > 1
481
- ? sharp::CropMultiPage(image,
482
- left, top, width, height, nPages, &targetPageHeight)
483
- : image.extract_area(left, top, width, height);
484
- } else {
485
- int attention_x;
486
- int attention_y;
487
-
488
- // Attention-based or Entropy-based crop
489
- MultiPageUnsupported(nPages, "Resize strategy");
490
- image = sharp::StaySequential(image, access);
491
- image = image.smartcrop(baton->width, baton->height, VImage::option()
492
- ->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
493
- ->set("premultiplied", shouldPremultiplyAlpha)
494
- ->set("attention_x", &attention_x)
495
- ->set("attention_y", &attention_y));
496
- baton->hasCropOffset = true;
497
- baton->cropOffsetLeft = static_cast<int>(image.xoffset());
498
- baton->cropOffsetTop = static_cast<int>(image.yoffset());
499
- baton->hasAttentionCenter = true;
500
- baton->attentionX = static_cast<int>(attention_x * jpegShrinkOnLoad / scale);
501
- baton->attentionY = static_cast<int>(attention_y * jpegShrinkOnLoad / scale);
502
- }
503
- }
504
- }
505
-
506
- // Rotate post-extract non-90 angle
507
- if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) {
508
- MultiPageUnsupported(nPages, "Rotate");
509
- image = sharp::StaySequential(image, access);
510
- std::vector<double> background;
511
- std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha);
512
- image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
513
- }
514
-
515
- // Post extraction
516
- if (baton->topOffsetPost != -1) {
517
- if (nPages > 1) {
518
- image = sharp::CropMultiPage(image,
519
- baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost,
520
- nPages, &targetPageHeight);
521
-
522
- // heightPost is used in the info object, so update to reflect the number of pages
523
- baton->heightPost *= nPages;
524
- } else {
525
- image = image.extract_area(
526
- baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost);
527
- }
528
- }
529
-
530
- // Affine transform
531
- if (!baton->affineMatrix.empty()) {
532
- MultiPageUnsupported(nPages, "Affine");
533
- image = sharp::StaySequential(image, access);
534
- std::vector<double> background;
535
- std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
536
- vips::VInterpolate interp = vips::VInterpolate::new_from_name(
537
- const_cast<char*>(baton->affineInterpolator.data()));
538
- image = image.affine(baton->affineMatrix, VImage::option()->set("background", background)
539
- ->set("idx", baton->affineIdx)
540
- ->set("idy", baton->affineIdy)
541
- ->set("odx", baton->affineOdx)
542
- ->set("ody", baton->affineOdy)
543
- ->set("interpolate", interp));
544
- }
545
-
546
- // Extend edges
547
- if (baton->extendTop > 0 || baton->extendBottom > 0 || baton->extendLeft > 0 || baton->extendRight > 0) {
548
- // Embed
549
- baton->width = image.width() + baton->extendLeft + baton->extendRight;
550
- baton->height = (nPages > 1 ? targetPageHeight : image.height()) + baton->extendTop + baton->extendBottom;
551
-
552
- if (baton->extendWith == VIPS_EXTEND_BACKGROUND) {
553
- std::vector<double> background;
554
- std::tie(image, background) = sharp::ApplyAlpha(image, baton->extendBackground, shouldPremultiplyAlpha);
555
-
556
- image = nPages > 1
557
- ? sharp::EmbedMultiPage(image,
558
- baton->extendLeft, baton->extendTop, baton->width, baton->height,
559
- baton->extendWith, background, nPages, &targetPageHeight)
560
- : image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height,
561
- VImage::option()->set("extend", baton->extendWith)->set("background", background));
562
- } else {
563
- std::vector<double> ignoredBackground(1);
564
- image = nPages > 1
565
- ? sharp::EmbedMultiPage(image,
566
- baton->extendLeft, baton->extendTop, baton->width, baton->height,
567
- baton->extendWith, ignoredBackground, nPages, &targetPageHeight)
568
- : image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height,
569
- VImage::option()->set("extend", baton->extendWith));
570
- }
571
- }
572
- // Median - must happen before blurring, due to the utility of blurring after thresholding
573
- if (baton->medianSize > 0) {
574
- image = image.median(baton->medianSize);
575
- }
576
-
577
- // Threshold - must happen before blurring, due to the utility of blurring after thresholding
578
- // Threshold - must happen before unflatten to enable non-white unflattening
579
- if (baton->threshold != 0) {
580
- image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale);
581
- }
582
-
583
- // Blur
584
- if (shouldBlur) {
585
- image = sharp::Blur(image, baton->blurSigma);
586
- }
587
-
588
- // Unflatten the image
589
- if (baton->unflatten) {
590
- image = sharp::Unflatten(image);
591
- }
592
-
593
- // Convolve
594
- if (shouldConv) {
595
- image = sharp::Convolve(image,
596
- baton->convKernelWidth, baton->convKernelHeight,
597
- baton->convKernelScale, baton->convKernelOffset,
598
- baton->convKernel);
599
- }
600
-
601
- // Recomb
602
- if (baton->recombMatrix != NULL) {
603
- image = sharp::Recomb(image, baton->recombMatrix);
604
- }
605
-
606
- // Modulate
607
- if (baton->brightness != 1.0 || baton->saturation != 1.0 || baton->hue != 0.0 || baton->lightness != 0.0) {
608
- image = sharp::Modulate(image, baton->brightness, baton->saturation, baton->hue, baton->lightness);
609
- }
610
-
611
- // Sharpen
612
- if (shouldSharpen) {
613
- image = sharp::Sharpen(image, baton->sharpenSigma, baton->sharpenM1, baton->sharpenM2,
614
- baton->sharpenX1, baton->sharpenY2, baton->sharpenY3);
615
- }
616
-
617
- // Reverse premultiplication after all transformations
618
- if (shouldPremultiplyAlpha) {
619
- image = image.unpremultiply().cast(premultiplyFormat);
620
- }
621
- baton->premultiplied = shouldPremultiplyAlpha;
622
-
623
- // Composite
624
- if (shouldComposite) {
625
- std::vector<VImage> images = { image };
626
- std::vector<int> modes, xs, ys;
627
- for (Composite *composite : baton->composite) {
628
- VImage compositeImage;
629
- sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
630
- composite->input->access = access;
631
- std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
632
- compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspaceInput);
633
- // Verify within current dimensions
634
- if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
635
- throw vips::VError("Image to composite must have same dimensions or smaller");
636
- }
637
- // Check if overlay is tiled
638
- if (composite->tile) {
639
- int across = 0;
640
- int down = 0;
641
- // Use gravity in overlay
642
- if (compositeImage.width() <= image.width()) {
643
- across = static_cast<int>(ceil(static_cast<double>(image.width()) / compositeImage.width()));
644
- // Ensure odd number of tiles across when gravity is centre, north or south
645
- if (composite->gravity == 0 || composite->gravity == 1 || composite->gravity == 3) {
646
- across |= 1;
647
- }
648
- }
649
- if (compositeImage.height() <= image.height()) {
650
- down = static_cast<int>(ceil(static_cast<double>(image.height()) / compositeImage.height()));
651
- // Ensure odd number of tiles down when gravity is centre, east or west
652
- if (composite->gravity == 0 || composite->gravity == 2 || composite->gravity == 4) {
653
- down |= 1;
654
- }
655
- }
656
- if (across != 0 || down != 0) {
657
- int left;
658
- int top;
659
- compositeImage = sharp::StaySequential(compositeImage, access).replicate(across, down);
660
- if (composite->hasOffset) {
661
- std::tie(left, top) = sharp::CalculateCrop(
662
- compositeImage.width(), compositeImage.height(), image.width(), image.height(),
663
- composite->left, composite->top);
664
- } else {
665
- std::tie(left, top) = sharp::CalculateCrop(
666
- compositeImage.width(), compositeImage.height(), image.width(), image.height(), composite->gravity);
667
- }
668
- compositeImage = compositeImage.extract_area(left, top, image.width(), image.height());
669
- }
670
- // gravity was used for extract_area, set it back to its default value of 0
671
- composite->gravity = 0;
672
- }
673
- // Ensure image to composite is sRGB with unpremultiplied alpha
674
- compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
675
- if (!sharp::HasAlpha(compositeImage)) {
676
- compositeImage = sharp::EnsureAlpha(compositeImage, 1);
677
- }
678
- if (composite->premultiplied) compositeImage = compositeImage.unpremultiply();
679
- // Calculate position
680
- int left;
681
- int top;
682
- if (composite->hasOffset) {
683
- // Composite image at given offsets
684
- if (composite->tile) {
685
- std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
686
- compositeImage.width(), compositeImage.height(), composite->left, composite->top);
687
- } else {
688
- left = composite->left;
689
- top = composite->top;
690
- }
691
- } else {
692
- // Composite image with given gravity
693
- std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
694
- compositeImage.width(), compositeImage.height(), composite->gravity);
695
- }
696
- images.push_back(compositeImage);
697
- modes.push_back(composite->mode);
698
- xs.push_back(left);
699
- ys.push_back(top);
700
- }
701
- image = VImage::composite(images, modes, VImage::option()->set("x", xs)->set("y", ys));
702
- image = sharp::RemoveGifPalette(image);
703
- }
704
-
705
- // Gamma decoding (brighten)
706
- if (baton->gammaOut >= 1 && baton->gammaOut <= 3) {
707
- image = sharp::Gamma(image, baton->gammaOut);
708
- }
709
-
710
- // Linear adjustment (a * in + b)
711
- if (!baton->linearA.empty()) {
712
- image = sharp::Linear(image, baton->linearA, baton->linearB);
713
- }
714
-
715
- // Apply normalisation - stretch luminance to cover full dynamic range
716
- if (baton->normalise) {
717
- image = sharp::StaySequential(image, access);
718
- image = sharp::Normalise(image, baton->normaliseLower, baton->normaliseUpper);
719
- }
720
-
721
- // Apply contrast limiting adaptive histogram equalization (CLAHE)
722
- if (baton->claheWidth != 0 && baton->claheHeight != 0) {
723
- image = sharp::StaySequential(image, access);
724
- image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
725
- }
726
-
727
- // Apply bitwise boolean operation between images
728
- if (baton->boolean != nullptr) {
729
- VImage booleanImage;
730
- sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
731
- baton->boolean->access = access;
732
- std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
733
- booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspaceInput);
734
- image = sharp::Boolean(image, booleanImage, baton->booleanOp);
735
- image = sharp::RemoveGifPalette(image);
736
- }
737
-
738
- // Apply per-channel Bandbool bitwise operations after all other operations
739
- if (baton->bandBoolOp >= VIPS_OPERATION_BOOLEAN_AND && baton->bandBoolOp < VIPS_OPERATION_BOOLEAN_LAST) {
740
- image = sharp::Bandbool(image, baton->bandBoolOp);
741
- }
742
-
743
- // Tint the image
744
- if (baton->tint[0] >= 0.0) {
745
- image = sharp::Tint(image, baton->tint);
746
- }
747
-
748
- // Remove alpha channel, if any
749
- if (baton->removeAlpha) {
750
- image = sharp::RemoveAlpha(image);
751
- }
752
-
753
- // Ensure alpha channel, if missing
754
- if (baton->ensureAlpha != -1) {
755
- image = sharp::EnsureAlpha(image, baton->ensureAlpha);
756
- }
757
-
758
- // Convert image to sRGB, if not already
759
- if (sharp::Is16Bit(image.interpretation())) {
760
- image = image.cast(VIPS_FORMAT_USHORT);
761
- }
762
- if (image.interpretation() != baton->colourspace) {
763
- // Convert colourspace, pass the current known interpretation so libvips doesn't have to guess
764
- image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation()));
765
- // Transform colours from embedded profile to output profile
766
- if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) &&
767
- baton->withIccProfile.empty() && sharp::HasProfile(image)) {
768
- image = image.icc_transform("srgb", VImage::option()
769
- ->set("embedded", TRUE)
770
- ->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
771
- ->set("intent", VIPS_INTENT_PERCEPTUAL));
772
- }
773
- }
774
-
775
- // Extract channel
776
- if (baton->extractChannel > -1) {
777
- if (baton->extractChannel >= image.bands()) {
778
- if (baton->extractChannel == 3 && sharp::HasAlpha(image)) {
779
- baton->extractChannel = image.bands() - 1;
780
- } else {
781
- (baton->err)
782
- .append("Cannot extract channel ").append(std::to_string(baton->extractChannel))
783
- .append(" from image with channels 0-").append(std::to_string(image.bands() - 1));
784
- return Error();
785
- }
786
- }
787
- VipsInterpretation colourspace = sharp::Is16Bit(image.interpretation())
788
- ? VIPS_INTERPRETATION_GREY16
789
- : VIPS_INTERPRETATION_B_W;
790
- image = image
791
- .extract_band(baton->extractChannel)
792
- .copy(VImage::option()->set("interpretation", colourspace));
793
- }
794
-
795
- // Apply output ICC profile
796
- if (!baton->withIccProfile.empty()) {
797
- image = image.icc_transform(const_cast<char*>(baton->withIccProfile.data()), VImage::option()
798
- ->set("input_profile", processingProfile)
799
- ->set("embedded", TRUE)
800
- ->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
801
- ->set("intent", VIPS_INTENT_PERCEPTUAL));
802
- } else if (baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) {
803
- image = sharp::SetProfile(image, inputProfile);
804
- }
805
- // Override EXIF Orientation tag
806
- if (baton->withMetadataOrientation != -1) {
807
- image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
808
- }
809
- // Override pixel density
810
- if (baton->withMetadataDensity > 0) {
811
- image = sharp::SetDensity(image, baton->withMetadataDensity);
812
- }
813
- // EXIF key/value pairs
814
- if (baton->keepMetadata & VIPS_FOREIGN_KEEP_EXIF) {
815
- image = image.copy();
816
- if (!baton->withExifMerge) {
817
- image = sharp::RemoveExif(image);
818
- }
819
- for (const auto& s : baton->withExif) {
820
- image.set(s.first.data(), s.second.data());
821
- }
822
- }
823
-
824
- // Number of channels used in output image
825
- baton->channels = image.bands();
826
- baton->width = image.width();
827
- baton->height = image.height();
828
-
829
- image = sharp::SetAnimationProperties(
830
- image, nPages, targetPageHeight, baton->delay, baton->loop);
831
-
832
- // Output
833
- sharp::SetTimeout(image, baton->timeoutSeconds);
834
- if (baton->fileOut.empty()) {
835
- // Buffer output
836
- if (baton->formatOut == "jpeg" || (baton->formatOut == "input" && inputImageType == sharp::ImageType::JPEG)) {
837
- // Write JPEG to buffer
838
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
839
- VipsArea *area = reinterpret_cast<VipsArea*>(image.jpegsave_buffer(VImage::option()
840
- ->set("keep", baton->keepMetadata)
841
- ->set("Q", baton->jpegQuality)
842
- ->set("interlace", baton->jpegProgressive)
843
- ->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
844
- ? VIPS_FOREIGN_SUBSAMPLE_OFF
845
- : VIPS_FOREIGN_SUBSAMPLE_ON)
846
- ->set("trellis_quant", baton->jpegTrellisQuantisation)
847
- ->set("quant_table", baton->jpegQuantisationTable)
848
- ->set("overshoot_deringing", baton->jpegOvershootDeringing)
849
- ->set("optimize_scans", baton->jpegOptimiseScans)
850
- ->set("optimize_coding", baton->jpegOptimiseCoding)));
851
- baton->bufferOut = static_cast<char*>(area->data);
852
- baton->bufferOutLength = area->length;
853
- area->free_fn = nullptr;
854
- vips_area_unref(area);
855
- baton->formatOut = "jpeg";
856
- if (baton->colourspace == VIPS_INTERPRETATION_CMYK) {
857
- baton->channels = std::min(baton->channels, 4);
858
- } else {
859
- baton->channels = std::min(baton->channels, 3);
860
- }
861
- } else if (baton->formatOut == "jp2" || (baton->formatOut == "input"
862
- && inputImageType == sharp::ImageType::JP2)) {
863
- // Write JP2 to Buffer
864
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::JP2);
865
- VipsArea *area = reinterpret_cast<VipsArea*>(image.jp2ksave_buffer(VImage::option()
866
- ->set("Q", baton->jp2Quality)
867
- ->set("lossless", baton->jp2Lossless)
868
- ->set("subsample_mode", baton->jp2ChromaSubsampling == "4:4:4"
869
- ? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
870
- ->set("tile_height", baton->jp2TileHeight)
871
- ->set("tile_width", baton->jp2TileWidth)));
872
- baton->bufferOut = static_cast<char*>(area->data);
873
- baton->bufferOutLength = area->length;
874
- area->free_fn = nullptr;
875
- vips_area_unref(area);
876
- baton->formatOut = "jp2";
877
- } else if (baton->formatOut == "png" || (baton->formatOut == "input" &&
878
- (inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::SVG))) {
879
- // Write PNG to buffer
880
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
881
- VipsArea *area = reinterpret_cast<VipsArea*>(image.pngsave_buffer(VImage::option()
882
- ->set("keep", baton->keepMetadata)
883
- ->set("interlace", baton->pngProgressive)
884
- ->set("compression", baton->pngCompressionLevel)
885
- ->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
886
- ->set("palette", baton->pngPalette)
887
- ->set("Q", baton->pngQuality)
888
- ->set("effort", baton->pngEffort)
889
- ->set("bitdepth", sharp::Is16Bit(image.interpretation()) ? 16 : baton->pngBitdepth)
890
- ->set("dither", baton->pngDither)));
891
- baton->bufferOut = static_cast<char*>(area->data);
892
- baton->bufferOutLength = area->length;
893
- area->free_fn = nullptr;
894
- vips_area_unref(area);
895
- baton->formatOut = "png";
896
- } else if (baton->formatOut == "webp" ||
897
- (baton->formatOut == "input" && inputImageType == sharp::ImageType::WEBP)) {
898
- // Write WEBP to buffer
899
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::WEBP);
900
- VipsArea *area = reinterpret_cast<VipsArea*>(image.webpsave_buffer(VImage::option()
901
- ->set("keep", baton->keepMetadata)
902
- ->set("Q", baton->webpQuality)
903
- ->set("lossless", baton->webpLossless)
904
- ->set("near_lossless", baton->webpNearLossless)
905
- ->set("smart_subsample", baton->webpSmartSubsample)
906
- ->set("preset", baton->webpPreset)
907
- ->set("effort", baton->webpEffort)
908
- ->set("min_size", baton->webpMinSize)
909
- ->set("mixed", baton->webpMixed)
910
- ->set("alpha_q", baton->webpAlphaQuality)));
911
- baton->bufferOut = static_cast<char*>(area->data);
912
- baton->bufferOutLength = area->length;
913
- area->free_fn = nullptr;
914
- vips_area_unref(area);
915
- baton->formatOut = "webp";
916
- } else if (baton->formatOut == "gif" ||
917
- (baton->formatOut == "input" && inputImageType == sharp::ImageType::GIF)) {
918
- // Write GIF to buffer
919
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
920
- VipsArea *area = reinterpret_cast<VipsArea*>(image.gifsave_buffer(VImage::option()
921
- ->set("keep", baton->keepMetadata)
922
- ->set("bitdepth", baton->gifBitdepth)
923
- ->set("effort", baton->gifEffort)
924
- ->set("reuse", baton->gifReuse)
925
- ->set("interlace", baton->gifProgressive)
926
- ->set("interframe_maxerror", baton->gifInterFrameMaxError)
927
- ->set("interpalette_maxerror", baton->gifInterPaletteMaxError)
928
- ->set("dither", baton->gifDither)));
929
- baton->bufferOut = static_cast<char*>(area->data);
930
- baton->bufferOutLength = area->length;
931
- area->free_fn = nullptr;
932
- vips_area_unref(area);
933
- baton->formatOut = "gif";
934
- } else if (baton->formatOut == "tiff" ||
935
- (baton->formatOut == "input" && inputImageType == sharp::ImageType::TIFF)) {
936
- // Write TIFF to buffer
937
- if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
938
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
939
- baton->channels = std::min(baton->channels, 3);
940
- }
941
- // Cast pixel values to float, if required
942
- if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
943
- image = image.cast(VIPS_FORMAT_FLOAT);
944
- }
945
- VipsArea *area = reinterpret_cast<VipsArea*>(image.tiffsave_buffer(VImage::option()
946
- ->set("keep", baton->keepMetadata)
947
- ->set("Q", baton->tiffQuality)
948
- ->set("bitdepth", baton->tiffBitdepth)
949
- ->set("compression", baton->tiffCompression)
950
- ->set("miniswhite", baton->tiffMiniswhite)
951
- ->set("predictor", baton->tiffPredictor)
952
- ->set("pyramid", baton->tiffPyramid)
953
- ->set("tile", baton->tiffTile)
954
- ->set("tile_height", baton->tiffTileHeight)
955
- ->set("tile_width", baton->tiffTileWidth)
956
- ->set("xres", baton->tiffXres)
957
- ->set("yres", baton->tiffYres)
958
- ->set("resunit", baton->tiffResolutionUnit)));
959
- baton->bufferOut = static_cast<char*>(area->data);
960
- baton->bufferOutLength = area->length;
961
- area->free_fn = nullptr;
962
- vips_area_unref(area);
963
- baton->formatOut = "tiff";
964
- } else if (baton->formatOut == "heif" ||
965
- (baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
966
- // Write HEIF to buffer
967
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
968
- image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
969
- VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
970
- ->set("keep", baton->keepMetadata)
971
- ->set("Q", baton->heifQuality)
972
- ->set("compression", baton->heifCompression)
973
- ->set("effort", baton->heifEffort)
974
- ->set("bitdepth", 8)
975
- ->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
976
- ? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
977
- ->set("lossless", baton->heifLossless)));
978
- baton->bufferOut = static_cast<char*>(area->data);
979
- baton->bufferOutLength = area->length;
980
- area->free_fn = nullptr;
981
- vips_area_unref(area);
982
- baton->formatOut = "heif";
983
- } else if (baton->formatOut == "dz") {
984
- // Write DZ to buffer
985
- baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
986
- if (!sharp::HasAlpha(image)) {
987
- baton->tileBackground.pop_back();
988
- }
989
- image = sharp::StaySequential(image, access, baton->tileAngle != 0);
990
- vips::VOption *options = BuildOptionsDZ(baton);
991
- VipsArea *area = reinterpret_cast<VipsArea*>(image.dzsave_buffer(options));
992
- baton->bufferOut = static_cast<char*>(area->data);
993
- baton->bufferOutLength = area->length;
994
- area->free_fn = nullptr;
995
- vips_area_unref(area);
996
- baton->formatOut = "dz";
997
- } else if (baton->formatOut == "jxl" ||
998
- (baton->formatOut == "input" && inputImageType == sharp::ImageType::JXL)) {
999
- // Write JXL to buffer
1000
- image = sharp::RemoveAnimationProperties(image);
1001
- VipsArea *area = reinterpret_cast<VipsArea*>(image.jxlsave_buffer(VImage::option()
1002
- ->set("keep", baton->keepMetadata)
1003
- ->set("distance", baton->jxlDistance)
1004
- ->set("tier", baton->jxlDecodingTier)
1005
- ->set("effort", baton->jxlEffort)
1006
- ->set("lossless", baton->jxlLossless)));
1007
- baton->bufferOut = static_cast<char*>(area->data);
1008
- baton->bufferOutLength = area->length;
1009
- area->free_fn = nullptr;
1010
- vips_area_unref(area);
1011
- baton->formatOut = "jxl";
1012
- } else if (baton->formatOut == "raw" ||
1013
- (baton->formatOut == "input" && inputImageType == sharp::ImageType::RAW)) {
1014
- // Write raw, uncompressed image data to buffer
1015
- if (baton->greyscale || image.interpretation() == VIPS_INTERPRETATION_B_W) {
1016
- // Extract first band for greyscale image
1017
- image = image[0];
1018
- baton->channels = 1;
1019
- }
1020
- if (image.format() != baton->rawDepth) {
1021
- // Cast pixels to requested format
1022
- image = image.cast(baton->rawDepth);
1023
- }
1024
- // Get raw image data
1025
- baton->bufferOut = static_cast<char*>(image.write_to_memory(&baton->bufferOutLength));
1026
- if (baton->bufferOut == nullptr) {
1027
- (baton->err).append("Could not allocate enough memory for raw output");
1028
- return Error();
1029
- }
1030
- baton->formatOut = "raw";
1031
- } else {
1032
- // Unsupported output format
1033
- (baton->err).append("Unsupported output format ");
1034
- if (baton->formatOut == "input") {
1035
- (baton->err).append(ImageTypeId(inputImageType));
1036
- } else {
1037
- (baton->err).append(baton->formatOut);
1038
- }
1039
- return Error();
1040
- }
1041
- } else {
1042
- // File output
1043
- bool const isJpeg = sharp::IsJpeg(baton->fileOut);
1044
- bool const isPng = sharp::IsPng(baton->fileOut);
1045
- bool const isWebp = sharp::IsWebp(baton->fileOut);
1046
- bool const isGif = sharp::IsGif(baton->fileOut);
1047
- bool const isTiff = sharp::IsTiff(baton->fileOut);
1048
- bool const isJp2 = sharp::IsJp2(baton->fileOut);
1049
- bool const isHeif = sharp::IsHeif(baton->fileOut);
1050
- bool const isJxl = sharp::IsJxl(baton->fileOut);
1051
- bool const isDz = sharp::IsDz(baton->fileOut);
1052
- bool const isDzZip = sharp::IsDzZip(baton->fileOut);
1053
- bool const isV = sharp::IsV(baton->fileOut);
1054
- bool const mightMatchInput = baton->formatOut == "input";
1055
- bool const willMatchInput = mightMatchInput &&
1056
- !(isJpeg || isPng || isWebp || isGif || isTiff || isJp2 || isHeif || isDz || isDzZip || isV);
1057
-
1058
- if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) ||
1059
- (willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
1060
- // Write JPEG to file
1061
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
1062
- image.jpegsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
1063
- ->set("keep", baton->keepMetadata)
1064
- ->set("Q", baton->jpegQuality)
1065
- ->set("interlace", baton->jpegProgressive)
1066
- ->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
1067
- ? VIPS_FOREIGN_SUBSAMPLE_OFF
1068
- : VIPS_FOREIGN_SUBSAMPLE_ON)
1069
- ->set("trellis_quant", baton->jpegTrellisQuantisation)
1070
- ->set("quant_table", baton->jpegQuantisationTable)
1071
- ->set("overshoot_deringing", baton->jpegOvershootDeringing)
1072
- ->set("optimize_scans", baton->jpegOptimiseScans)
1073
- ->set("optimize_coding", baton->jpegOptimiseCoding));
1074
- baton->formatOut = "jpeg";
1075
- baton->channels = std::min(baton->channels, 3);
1076
- } else if (baton->formatOut == "jp2" || (mightMatchInput && isJp2) ||
1077
- (willMatchInput && (inputImageType == sharp::ImageType::JP2))) {
1078
- // Write JP2 to file
1079
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::JP2);
1080
- image.jp2ksave(const_cast<char*>(baton->fileOut.data()), VImage::option()
1081
- ->set("Q", baton->jp2Quality)
1082
- ->set("lossless", baton->jp2Lossless)
1083
- ->set("subsample_mode", baton->jp2ChromaSubsampling == "4:4:4"
1084
- ? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
1085
- ->set("tile_height", baton->jp2TileHeight)
1086
- ->set("tile_width", baton->jp2TileWidth));
1087
- baton->formatOut = "jp2";
1088
- } else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput &&
1089
- (inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::SVG))) {
1090
- // Write PNG to file
1091
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
1092
- image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
1093
- ->set("keep", baton->keepMetadata)
1094
- ->set("interlace", baton->pngProgressive)
1095
- ->set("compression", baton->pngCompressionLevel)
1096
- ->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
1097
- ->set("palette", baton->pngPalette)
1098
- ->set("Q", baton->pngQuality)
1099
- ->set("bitdepth", sharp::Is16Bit(image.interpretation()) ? 16 : baton->pngBitdepth)
1100
- ->set("effort", baton->pngEffort)
1101
- ->set("dither", baton->pngDither));
1102
- baton->formatOut = "png";
1103
- } else if (baton->formatOut == "webp" || (mightMatchInput && isWebp) ||
1104
- (willMatchInput && inputImageType == sharp::ImageType::WEBP)) {
1105
- // Write WEBP to file
1106
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::WEBP);
1107
- image.webpsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
1108
- ->set("keep", baton->keepMetadata)
1109
- ->set("Q", baton->webpQuality)
1110
- ->set("lossless", baton->webpLossless)
1111
- ->set("near_lossless", baton->webpNearLossless)
1112
- ->set("smart_subsample", baton->webpSmartSubsample)
1113
- ->set("preset", baton->webpPreset)
1114
- ->set("effort", baton->webpEffort)
1115
- ->set("min_size", baton->webpMinSize)
1116
- ->set("mixed", baton->webpMixed)
1117
- ->set("alpha_q", baton->webpAlphaQuality));
1118
- baton->formatOut = "webp";
1119
- } else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
1120
- (willMatchInput && inputImageType == sharp::ImageType::GIF)) {
1121
- // Write GIF to file
1122
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
1123
- image.gifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
1124
- ->set("keep", baton->keepMetadata)
1125
- ->set("bitdepth", baton->gifBitdepth)
1126
- ->set("effort", baton->gifEffort)
1127
- ->set("reuse", baton->gifReuse)
1128
- ->set("interlace", baton->gifProgressive)
1129
- ->set("dither", baton->gifDither));
1130
- baton->formatOut = "gif";
1131
- } else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
1132
- (willMatchInput && inputImageType == sharp::ImageType::TIFF)) {
1133
- // Write TIFF to file
1134
- if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
1135
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
1136
- baton->channels = std::min(baton->channels, 3);
1137
- }
1138
- // Cast pixel values to float, if required
1139
- if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
1140
- image = image.cast(VIPS_FORMAT_FLOAT);
1141
- }
1142
- image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
1143
- ->set("keep", baton->keepMetadata)
1144
- ->set("Q", baton->tiffQuality)
1145
- ->set("bitdepth", baton->tiffBitdepth)
1146
- ->set("compression", baton->tiffCompression)
1147
- ->set("miniswhite", baton->tiffMiniswhite)
1148
- ->set("predictor", baton->tiffPredictor)
1149
- ->set("pyramid", baton->tiffPyramid)
1150
- ->set("tile", baton->tiffTile)
1151
- ->set("tile_height", baton->tiffTileHeight)
1152
- ->set("tile_width", baton->tiffTileWidth)
1153
- ->set("xres", baton->tiffXres)
1154
- ->set("yres", baton->tiffYres)
1155
- ->set("resunit", baton->tiffResolutionUnit));
1156
- baton->formatOut = "tiff";
1157
- } else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
1158
- (willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
1159
- // Write HEIF to file
1160
- sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
1161
- image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
1162
- image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
1163
- ->set("keep", baton->keepMetadata)
1164
- ->set("Q", baton->heifQuality)
1165
- ->set("compression", baton->heifCompression)
1166
- ->set("effort", baton->heifEffort)
1167
- ->set("bitdepth", 8)
1168
- ->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
1169
- ? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
1170
- ->set("lossless", baton->heifLossless));
1171
- baton->formatOut = "heif";
1172
- } else if (baton->formatOut == "jxl" || (mightMatchInput && isJxl) ||
1173
- (willMatchInput && inputImageType == sharp::ImageType::JXL)) {
1174
- // Write JXL to file
1175
- image = sharp::RemoveAnimationProperties(image);
1176
- image.jxlsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
1177
- ->set("keep", baton->keepMetadata)
1178
- ->set("distance", baton->jxlDistance)
1179
- ->set("tier", baton->jxlDecodingTier)
1180
- ->set("effort", baton->jxlEffort)
1181
- ->set("lossless", baton->jxlLossless));
1182
- baton->formatOut = "jxl";
1183
- } else if (baton->formatOut == "dz" || isDz || isDzZip) {
1184
- // Write DZ to file
1185
- if (isDzZip) {
1186
- baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
1187
- }
1188
- if (!sharp::HasAlpha(image)) {
1189
- baton->tileBackground.pop_back();
1190
- }
1191
- image = sharp::StaySequential(image, access, baton->tileAngle != 0);
1192
- vips::VOption *options = BuildOptionsDZ(baton);
1193
- image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
1194
- baton->formatOut = "dz";
1195
- } else if (baton->formatOut == "v" || (mightMatchInput && isV) ||
1196
- (willMatchInput && inputImageType == sharp::ImageType::VIPS)) {
1197
- // Write V to file
1198
- image.vipssave(const_cast<char*>(baton->fileOut.data()), VImage::option()
1199
- ->set("keep", baton->keepMetadata));
1200
- baton->formatOut = "v";
1201
- } else {
1202
- // Unsupported output format
1203
- (baton->err).append("Unsupported output format " + baton->fileOut);
1204
- return Error();
1205
- }
1206
- }
1207
- } catch (vips::VError const &err) {
1208
- char const *what = err.what();
1209
- if (what && what[0]) {
1210
- (baton->err).append(what);
1211
- } else {
1212
- (baton->err).append("Unknown error");
1213
- }
1214
- }
1215
- // Clean up libvips' per-request data and threads
1216
- vips_error_clear();
1217
- vips_thread_shutdown();
1218
- }
1219
-
1220
- void OnOK() {
1221
- Napi::Env env = Env();
1222
- Napi::HandleScope scope(env);
1223
-
1224
- // Handle warnings
1225
- std::string warning = sharp::VipsWarningPop();
1226
- while (!warning.empty()) {
1227
- debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) });
1228
- warning = sharp::VipsWarningPop();
1229
- }
1230
-
1231
- if (baton->err.empty()) {
1232
- int width = baton->width;
1233
- int height = baton->height;
1234
- if (baton->topOffsetPre != -1 && (baton->width == -1 || baton->height == -1)) {
1235
- width = baton->widthPre;
1236
- height = baton->heightPre;
1237
- }
1238
- if (baton->topOffsetPost != -1) {
1239
- width = baton->widthPost;
1240
- height = baton->heightPost;
1241
- }
1242
- // Info Object
1243
- Napi::Object info = Napi::Object::New(env);
1244
- info.Set("format", baton->formatOut);
1245
- info.Set("width", static_cast<uint32_t>(width));
1246
- info.Set("height", static_cast<uint32_t>(height));
1247
- info.Set("channels", static_cast<uint32_t>(baton->channels));
1248
- if (baton->formatOut == "raw") {
1249
- info.Set("depth", vips_enum_nick(VIPS_TYPE_BAND_FORMAT, baton->rawDepth));
1250
- }
1251
- info.Set("premultiplied", baton->premultiplied);
1252
- if (baton->hasCropOffset) {
1253
- info.Set("cropOffsetLeft", static_cast<int32_t>(baton->cropOffsetLeft));
1254
- info.Set("cropOffsetTop", static_cast<int32_t>(baton->cropOffsetTop));
1255
- }
1256
- if (baton->hasAttentionCenter) {
1257
- info.Set("attentionX", static_cast<int32_t>(baton->attentionX));
1258
- info.Set("attentionY", static_cast<int32_t>(baton->attentionY));
1259
- }
1260
- if (baton->trimThreshold >= 0.0) {
1261
- info.Set("trimOffsetLeft", static_cast<int32_t>(baton->trimOffsetLeft));
1262
- info.Set("trimOffsetTop", static_cast<int32_t>(baton->trimOffsetTop));
1263
- }
1264
- if (baton->input->textAutofitDpi) {
1265
- info.Set("textAutofitDpi", static_cast<uint32_t>(baton->input->textAutofitDpi));
1266
- }
1267
-
1268
- if (baton->bufferOutLength > 0) {
1269
- // Add buffer size to info
1270
- info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
1271
- // Pass ownership of output data to Buffer instance
1272
- Napi::Buffer<char> data = Napi::Buffer<char>::NewOrCopy(env, static_cast<char*>(baton->bufferOut),
1273
- baton->bufferOutLength, sharp::FreeCallback);
1274
- Callback().Call(Receiver().Value(), { env.Null(), data, info });
1275
- } else {
1276
- // Add file size to info
1277
- struct STAT64_STRUCT st;
1278
- if (STAT64_FUNCTION(baton->fileOut.data(), &st) == 0) {
1279
- info.Set("size", static_cast<uint32_t>(st.st_size));
1280
- }
1281
- Callback().Call(Receiver().Value(), { env.Null(), info });
1282
- }
1283
- } else {
1284
- Callback().Call(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
1285
- }
1286
-
1287
- // Delete baton
1288
- delete baton->input;
1289
- delete baton->boolean;
1290
- for (Composite *composite : baton->composite) {
1291
- delete composite->input;
1292
- delete composite;
1293
- }
1294
- for (sharp::InputDescriptor *input : baton->joinChannelIn) {
1295
- delete input;
1296
- }
1297
- delete baton;
1298
-
1299
- // Decrement processing task counter
1300
- sharp::counterProcess--;
1301
- Napi::Number queueLength = Napi::Number::New(env, static_cast<int>(sharp::counterQueue));
1302
- queueListener.Call(Receiver().Value(), { queueLength });
1303
- }
1304
-
1305
- private:
1306
- PipelineBaton *baton;
1307
- Napi::FunctionReference debuglog;
1308
- Napi::FunctionReference queueListener;
1309
-
1310
- void MultiPageUnsupported(int const pages, std::string op) {
1311
- if (pages > 1) {
1312
- throw vips::VError(op + " is not supported for multi-page images");
1313
- }
1314
- }
1315
-
1316
- /*
1317
- Calculate the angle of rotation and need-to-flip for the given Exif orientation
1318
- By default, returns zero, i.e. no rotation.
1319
- */
1320
- std::tuple<VipsAngle, bool, bool>
1321
- CalculateExifRotationAndFlip(int const exifOrientation) {
1322
- VipsAngle rotate = VIPS_ANGLE_D0;
1323
- bool flip = FALSE;
1324
- bool flop = FALSE;
1325
- switch (exifOrientation) {
1326
- case 6: rotate = VIPS_ANGLE_D90; break;
1327
- case 3: rotate = VIPS_ANGLE_D180; break;
1328
- case 8: rotate = VIPS_ANGLE_D270; break;
1329
- case 2: flop = TRUE; break; // flop 1
1330
- case 7: flip = TRUE; rotate = VIPS_ANGLE_D90; break; // flip 6
1331
- case 4: flop = TRUE; rotate = VIPS_ANGLE_D180; break; // flop 3
1332
- case 5: flip = TRUE; rotate = VIPS_ANGLE_D270; break; // flip 8
1333
- }
1334
- return std::make_tuple(rotate, flip, flop);
1335
- }
1336
-
1337
- /*
1338
- Calculate the rotation for the given angle.
1339
- Supports any positive or negative angle that is a multiple of 90.
1340
- */
1341
- VipsAngle
1342
- CalculateAngleRotation(int angle) {
1343
- angle = angle % 360;
1344
- if (angle < 0)
1345
- angle = 360 + angle;
1346
- switch (angle) {
1347
- case 90: return VIPS_ANGLE_D90;
1348
- case 180: return VIPS_ANGLE_D180;
1349
- case 270: return VIPS_ANGLE_D270;
1350
- }
1351
- return VIPS_ANGLE_D0;
1352
- }
1353
-
1354
- /*
1355
- Assemble the suffix argument to dzsave, which is the format (by extname)
1356
- alongside comma-separated arguments to the corresponding `formatsave` vips
1357
- action.
1358
- */
1359
- std::string
1360
- AssembleSuffixString(std::string extname, std::vector<std::pair<std::string, std::string>> options) {
1361
- std::string argument;
1362
- for (auto const &option : options) {
1363
- if (!argument.empty()) {
1364
- argument += ",";
1365
- }
1366
- argument += option.first + "=" + option.second;
1367
- }
1368
- return extname + "[" + argument + "]";
1369
- }
1370
-
1371
- /*
1372
- Build VOption for dzsave
1373
- */
1374
- vips::VOption*
1375
- BuildOptionsDZ(PipelineBaton *baton) {
1376
- // Forward format options through suffix
1377
- std::string suffix;
1378
- if (baton->tileFormat == "png") {
1379
- std::vector<std::pair<std::string, std::string>> options {
1380
- {"interlace", baton->pngProgressive ? "TRUE" : "FALSE"},
1381
- {"compression", std::to_string(baton->pngCompressionLevel)},
1382
- {"filter", baton->pngAdaptiveFiltering ? "all" : "none"}
1383
- };
1384
- suffix = AssembleSuffixString(".png", options);
1385
- } else if (baton->tileFormat == "webp") {
1386
- std::vector<std::pair<std::string, std::string>> options {
1387
- {"Q", std::to_string(baton->webpQuality)},
1388
- {"alpha_q", std::to_string(baton->webpAlphaQuality)},
1389
- {"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
1390
- {"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
1391
- {"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
1392
- {"preset", vips_enum_nick(VIPS_TYPE_FOREIGN_WEBP_PRESET, baton->webpPreset)},
1393
- {"min_size", baton->webpMinSize ? "TRUE" : "FALSE"},
1394
- {"mixed", baton->webpMixed ? "TRUE" : "FALSE"},
1395
- {"effort", std::to_string(baton->webpEffort)}
1396
- };
1397
- suffix = AssembleSuffixString(".webp", options);
1398
- } else {
1399
- std::vector<std::pair<std::string, std::string>> options {
1400
- {"Q", std::to_string(baton->jpegQuality)},
1401
- {"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
1402
- {"subsample_mode", baton->jpegChromaSubsampling == "4:4:4" ? "off" : "on"},
1403
- {"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"},
1404
- {"quant_table", std::to_string(baton->jpegQuantisationTable)},
1405
- {"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"},
1406
- {"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"},
1407
- {"optimize_coding", baton->jpegOptimiseCoding ? "TRUE": "FALSE"}
1408
- };
1409
- std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_DZ ? ".jpeg" : ".jpg";
1410
- suffix = AssembleSuffixString(extname, options);
1411
- }
1412
- vips::VOption *options = VImage::option()
1413
- ->set("keep", baton->keepMetadata)
1414
- ->set("tile_size", baton->tileSize)
1415
- ->set("overlap", baton->tileOverlap)
1416
- ->set("container", baton->tileContainer)
1417
- ->set("layout", baton->tileLayout)
1418
- ->set("suffix", const_cast<char*>(suffix.data()))
1419
- ->set("angle", CalculateAngleRotation(baton->tileAngle))
1420
- ->set("background", baton->tileBackground)
1421
- ->set("centre", baton->tileCentre)
1422
- ->set("id", const_cast<char*>(baton->tileId.data()))
1423
- ->set("skip_blanks", baton->tileSkipBlanks);
1424
- if (baton->tileDepth < VIPS_FOREIGN_DZ_DEPTH_LAST) {
1425
- options->set("depth", baton->tileDepth);
1426
- }
1427
- if (!baton->tileBasename.empty()) {
1428
- options->set("basename", const_cast<char*>(baton->tileBasename.data()));
1429
- }
1430
- return options;
1431
- }
1432
-
1433
- /*
1434
- Clear all thread-local data.
1435
- */
1436
- void Error() {
1437
- // Clean up libvips' per-request data and threads
1438
- vips_error_clear();
1439
- vips_thread_shutdown();
1440
- }
1441
- };
1442
-
1443
- /*
1444
- pipeline(options, output, callback)
1445
- */
1446
- Napi::Value pipeline(const Napi::CallbackInfo& info) {
1447
- // V8 objects are converted to non-V8 types held in the baton struct
1448
- PipelineBaton *baton = new PipelineBaton;
1449
- Napi::Object options = info[size_t(0)].As<Napi::Object>();
1450
-
1451
- // Input
1452
- baton->input = sharp::CreateInputDescriptor(options.Get("input").As<Napi::Object>());
1453
- // Extract image options
1454
- baton->topOffsetPre = sharp::AttrAsInt32(options, "topOffsetPre");
1455
- baton->leftOffsetPre = sharp::AttrAsInt32(options, "leftOffsetPre");
1456
- baton->widthPre = sharp::AttrAsInt32(options, "widthPre");
1457
- baton->heightPre = sharp::AttrAsInt32(options, "heightPre");
1458
- baton->topOffsetPost = sharp::AttrAsInt32(options, "topOffsetPost");
1459
- baton->leftOffsetPost = sharp::AttrAsInt32(options, "leftOffsetPost");
1460
- baton->widthPost = sharp::AttrAsInt32(options, "widthPost");
1461
- baton->heightPost = sharp::AttrAsInt32(options, "heightPost");
1462
- // Output image dimensions
1463
- baton->width = sharp::AttrAsInt32(options, "width");
1464
- baton->height = sharp::AttrAsInt32(options, "height");
1465
- // Canvas option
1466
- std::string canvas = sharp::AttrAsStr(options, "canvas");
1467
- if (canvas == "crop") {
1468
- baton->canvas = sharp::Canvas::CROP;
1469
- } else if (canvas == "embed") {
1470
- baton->canvas = sharp::Canvas::EMBED;
1471
- } else if (canvas == "max") {
1472
- baton->canvas = sharp::Canvas::MAX;
1473
- } else if (canvas == "min") {
1474
- baton->canvas = sharp::Canvas::MIN;
1475
- } else if (canvas == "ignore_aspect") {
1476
- baton->canvas = sharp::Canvas::IGNORE_ASPECT;
1477
- }
1478
- // Composite
1479
- Napi::Array compositeArray = options.Get("composite").As<Napi::Array>();
1480
- for (unsigned int i = 0; i < compositeArray.Length(); i++) {
1481
- Napi::Object compositeObject = compositeArray.Get(i).As<Napi::Object>();
1482
- Composite *composite = new Composite;
1483
- composite->input = sharp::CreateInputDescriptor(compositeObject.Get("input").As<Napi::Object>());
1484
- composite->mode = sharp::AttrAsEnum<VipsBlendMode>(compositeObject, "blend", VIPS_TYPE_BLEND_MODE);
1485
- composite->gravity = sharp::AttrAsUint32(compositeObject, "gravity");
1486
- composite->left = sharp::AttrAsInt32(compositeObject, "left");
1487
- composite->top = sharp::AttrAsInt32(compositeObject, "top");
1488
- composite->hasOffset = sharp::AttrAsBool(compositeObject, "hasOffset");
1489
- composite->tile = sharp::AttrAsBool(compositeObject, "tile");
1490
- composite->premultiplied = sharp::AttrAsBool(compositeObject, "premultiplied");
1491
- baton->composite.push_back(composite);
1492
- }
1493
- // Resize options
1494
- baton->withoutEnlargement = sharp::AttrAsBool(options, "withoutEnlargement");
1495
- baton->withoutReduction = sharp::AttrAsBool(options, "withoutReduction");
1496
- baton->position = sharp::AttrAsInt32(options, "position");
1497
- baton->resizeBackground = sharp::AttrAsVectorOfDouble(options, "resizeBackground");
1498
- baton->kernel = sharp::AttrAsEnum<VipsKernel>(options, "kernel", VIPS_TYPE_KERNEL);
1499
- baton->fastShrinkOnLoad = sharp::AttrAsBool(options, "fastShrinkOnLoad");
1500
- // Join Channel Options
1501
- if (options.Has("joinChannelIn")) {
1502
- Napi::Array joinChannelArray = options.Get("joinChannelIn").As<Napi::Array>();
1503
- for (unsigned int i = 0; i < joinChannelArray.Length(); i++) {
1504
- baton->joinChannelIn.push_back(
1505
- sharp::CreateInputDescriptor(joinChannelArray.Get(i).As<Napi::Object>()));
1506
- }
1507
- }
1508
- // Operators
1509
- baton->flatten = sharp::AttrAsBool(options, "flatten");
1510
- baton->flattenBackground = sharp::AttrAsVectorOfDouble(options, "flattenBackground");
1511
- baton->unflatten = sharp::AttrAsBool(options, "unflatten");
1512
- baton->negate = sharp::AttrAsBool(options, "negate");
1513
- baton->negateAlpha = sharp::AttrAsBool(options, "negateAlpha");
1514
- baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
1515
- baton->brightness = sharp::AttrAsDouble(options, "brightness");
1516
- baton->saturation = sharp::AttrAsDouble(options, "saturation");
1517
- baton->hue = sharp::AttrAsInt32(options, "hue");
1518
- baton->lightness = sharp::AttrAsDouble(options, "lightness");
1519
- baton->medianSize = sharp::AttrAsUint32(options, "medianSize");
1520
- baton->sharpenSigma = sharp::AttrAsDouble(options, "sharpenSigma");
1521
- baton->sharpenM1 = sharp::AttrAsDouble(options, "sharpenM1");
1522
- baton->sharpenM2 = sharp::AttrAsDouble(options, "sharpenM2");
1523
- baton->sharpenX1 = sharp::AttrAsDouble(options, "sharpenX1");
1524
- baton->sharpenY2 = sharp::AttrAsDouble(options, "sharpenY2");
1525
- baton->sharpenY3 = sharp::AttrAsDouble(options, "sharpenY3");
1526
- baton->threshold = sharp::AttrAsInt32(options, "threshold");
1527
- baton->thresholdGrayscale = sharp::AttrAsBool(options, "thresholdGrayscale");
1528
- baton->trimBackground = sharp::AttrAsVectorOfDouble(options, "trimBackground");
1529
- baton->trimThreshold = sharp::AttrAsDouble(options, "trimThreshold");
1530
- baton->trimLineArt = sharp::AttrAsBool(options, "trimLineArt");
1531
- baton->gamma = sharp::AttrAsDouble(options, "gamma");
1532
- baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
1533
- baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA");
1534
- baton->linearB = sharp::AttrAsVectorOfDouble(options, "linearB");
1535
- baton->greyscale = sharp::AttrAsBool(options, "greyscale");
1536
- baton->normalise = sharp::AttrAsBool(options, "normalise");
1537
- baton->normaliseLower = sharp::AttrAsUint32(options, "normaliseLower");
1538
- baton->normaliseUpper = sharp::AttrAsUint32(options, "normaliseUpper");
1539
- baton->tint = sharp::AttrAsVectorOfDouble(options, "tint");
1540
- baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
1541
- baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight");
1542
- baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope");
1543
- baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
1544
- baton->angle = sharp::AttrAsInt32(options, "angle");
1545
- baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
1546
- baton->rotationBackground = sharp::AttrAsVectorOfDouble(options, "rotationBackground");
1547
- baton->rotateBeforePreExtract = sharp::AttrAsBool(options, "rotateBeforePreExtract");
1548
- baton->flip = sharp::AttrAsBool(options, "flip");
1549
- baton->flop = sharp::AttrAsBool(options, "flop");
1550
- baton->extendTop = sharp::AttrAsInt32(options, "extendTop");
1551
- baton->extendBottom = sharp::AttrAsInt32(options, "extendBottom");
1552
- baton->extendLeft = sharp::AttrAsInt32(options, "extendLeft");
1553
- baton->extendRight = sharp::AttrAsInt32(options, "extendRight");
1554
- baton->extendBackground = sharp::AttrAsVectorOfDouble(options, "extendBackground");
1555
- baton->extendWith = sharp::AttrAsEnum<VipsExtend>(options, "extendWith", VIPS_TYPE_EXTEND);
1556
- baton->extractChannel = sharp::AttrAsInt32(options, "extractChannel");
1557
- baton->affineMatrix = sharp::AttrAsVectorOfDouble(options, "affineMatrix");
1558
- baton->affineBackground = sharp::AttrAsVectorOfDouble(options, "affineBackground");
1559
- baton->affineIdx = sharp::AttrAsDouble(options, "affineIdx");
1560
- baton->affineIdy = sharp::AttrAsDouble(options, "affineIdy");
1561
- baton->affineOdx = sharp::AttrAsDouble(options, "affineOdx");
1562
- baton->affineOdy = sharp::AttrAsDouble(options, "affineOdy");
1563
- baton->affineInterpolator = sharp::AttrAsStr(options, "affineInterpolator");
1564
- baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha");
1565
- baton->ensureAlpha = sharp::AttrAsDouble(options, "ensureAlpha");
1566
- if (options.Has("boolean")) {
1567
- baton->boolean = sharp::CreateInputDescriptor(options.Get("boolean").As<Napi::Object>());
1568
- baton->booleanOp = sharp::AttrAsEnum<VipsOperationBoolean>(options, "booleanOp", VIPS_TYPE_OPERATION_BOOLEAN);
1569
- }
1570
- if (options.Has("bandBoolOp")) {
1571
- baton->bandBoolOp = sharp::AttrAsEnum<VipsOperationBoolean>(options, "bandBoolOp", VIPS_TYPE_OPERATION_BOOLEAN);
1572
- }
1573
- if (options.Has("convKernel")) {
1574
- Napi::Object kernel = options.Get("convKernel").As<Napi::Object>();
1575
- baton->convKernelWidth = sharp::AttrAsUint32(kernel, "width");
1576
- baton->convKernelHeight = sharp::AttrAsUint32(kernel, "height");
1577
- baton->convKernelScale = sharp::AttrAsDouble(kernel, "scale");
1578
- baton->convKernelOffset = sharp::AttrAsDouble(kernel, "offset");
1579
- size_t const kernelSize = static_cast<size_t>(baton->convKernelWidth * baton->convKernelHeight);
1580
- baton->convKernel = std::unique_ptr<double[]>(new double[kernelSize]);
1581
- Napi::Array kdata = kernel.Get("kernel").As<Napi::Array>();
1582
- for (unsigned int i = 0; i < kernelSize; i++) {
1583
- baton->convKernel[i] = sharp::AttrAsDouble(kdata, i);
1584
- }
1585
- }
1586
- if (options.Has("recombMatrix")) {
1587
- baton->recombMatrix = std::unique_ptr<double[]>(new double[9]);
1588
- Napi::Array recombMatrix = options.Get("recombMatrix").As<Napi::Array>();
1589
- for (unsigned int i = 0; i < 9; i++) {
1590
- baton->recombMatrix[i] = sharp::AttrAsDouble(recombMatrix, i);
1591
- }
1592
- }
1593
- baton->colourspaceInput = sharp::AttrAsEnum<VipsInterpretation>(
1594
- options, "colourspaceInput", VIPS_TYPE_INTERPRETATION);
1595
- if (baton->colourspaceInput == VIPS_INTERPRETATION_ERROR) {
1596
- baton->colourspaceInput = VIPS_INTERPRETATION_LAST;
1597
- }
1598
- baton->colourspace = sharp::AttrAsEnum<VipsInterpretation>(options, "colourspace", VIPS_TYPE_INTERPRETATION);
1599
- if (baton->colourspace == VIPS_INTERPRETATION_ERROR) {
1600
- baton->colourspace = VIPS_INTERPRETATION_sRGB;
1601
- }
1602
- // Output
1603
- baton->formatOut = sharp::AttrAsStr(options, "formatOut");
1604
- baton->fileOut = sharp::AttrAsStr(options, "fileOut");
1605
- baton->keepMetadata = sharp::AttrAsUint32(options, "keepMetadata");
1606
- baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
1607
- baton->withMetadataDensity = sharp::AttrAsDouble(options, "withMetadataDensity");
1608
- baton->withIccProfile = sharp::AttrAsStr(options, "withIccProfile");
1609
- Napi::Object withExif = options.Get("withExif").As<Napi::Object>();
1610
- Napi::Array withExifKeys = withExif.GetPropertyNames();
1611
- for (unsigned int i = 0; i < withExifKeys.Length(); i++) {
1612
- std::string k = sharp::AttrAsStr(withExifKeys, i);
1613
- if (withExif.HasOwnProperty(k)) {
1614
- baton->withExif.insert(std::make_pair(k, sharp::AttrAsStr(withExif, k)));
1615
- }
1616
- }
1617
- baton->withExifMerge = sharp::AttrAsBool(options, "withExifMerge");
1618
- baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
1619
- // Format-specific
1620
- baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
1621
- baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
1622
- baton->jpegChromaSubsampling = sharp::AttrAsStr(options, "jpegChromaSubsampling");
1623
- baton->jpegTrellisQuantisation = sharp::AttrAsBool(options, "jpegTrellisQuantisation");
1624
- baton->jpegQuantisationTable = sharp::AttrAsUint32(options, "jpegQuantisationTable");
1625
- baton->jpegOvershootDeringing = sharp::AttrAsBool(options, "jpegOvershootDeringing");
1626
- baton->jpegOptimiseScans = sharp::AttrAsBool(options, "jpegOptimiseScans");
1627
- baton->jpegOptimiseCoding = sharp::AttrAsBool(options, "jpegOptimiseCoding");
1628
- baton->pngProgressive = sharp::AttrAsBool(options, "pngProgressive");
1629
- baton->pngCompressionLevel = sharp::AttrAsUint32(options, "pngCompressionLevel");
1630
- baton->pngAdaptiveFiltering = sharp::AttrAsBool(options, "pngAdaptiveFiltering");
1631
- baton->pngPalette = sharp::AttrAsBool(options, "pngPalette");
1632
- baton->pngQuality = sharp::AttrAsUint32(options, "pngQuality");
1633
- baton->pngEffort = sharp::AttrAsUint32(options, "pngEffort");
1634
- baton->pngBitdepth = sharp::AttrAsUint32(options, "pngBitdepth");
1635
- baton->pngDither = sharp::AttrAsDouble(options, "pngDither");
1636
- baton->jp2Quality = sharp::AttrAsUint32(options, "jp2Quality");
1637
- baton->jp2Lossless = sharp::AttrAsBool(options, "jp2Lossless");
1638
- baton->jp2TileHeight = sharp::AttrAsUint32(options, "jp2TileHeight");
1639
- baton->jp2TileWidth = sharp::AttrAsUint32(options, "jp2TileWidth");
1640
- baton->jp2ChromaSubsampling = sharp::AttrAsStr(options, "jp2ChromaSubsampling");
1641
- baton->webpQuality = sharp::AttrAsUint32(options, "webpQuality");
1642
- baton->webpAlphaQuality = sharp::AttrAsUint32(options, "webpAlphaQuality");
1643
- baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
1644
- baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
1645
- baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
1646
- baton->webpPreset = sharp::AttrAsEnum<VipsForeignWebpPreset>(options, "webpPreset", VIPS_TYPE_FOREIGN_WEBP_PRESET);
1647
- baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
1648
- baton->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
1649
- baton->webpMixed = sharp::AttrAsBool(options, "webpMixed");
1650
- baton->gifBitdepth = sharp::AttrAsUint32(options, "gifBitdepth");
1651
- baton->gifEffort = sharp::AttrAsUint32(options, "gifEffort");
1652
- baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
1653
- baton->gifInterFrameMaxError = sharp::AttrAsDouble(options, "gifInterFrameMaxError");
1654
- baton->gifInterPaletteMaxError = sharp::AttrAsDouble(options, "gifInterPaletteMaxError");
1655
- baton->gifReuse = sharp::AttrAsBool(options, "gifReuse");
1656
- baton->gifProgressive = sharp::AttrAsBool(options, "gifProgressive");
1657
- baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
1658
- baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
1659
- baton->tiffMiniswhite = sharp::AttrAsBool(options, "tiffMiniswhite");
1660
- baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
1661
- baton->tiffTile = sharp::AttrAsBool(options, "tiffTile");
1662
- baton->tiffTileWidth = sharp::AttrAsUint32(options, "tiffTileWidth");
1663
- baton->tiffTileHeight = sharp::AttrAsUint32(options, "tiffTileHeight");
1664
- baton->tiffXres = sharp::AttrAsDouble(options, "tiffXres");
1665
- baton->tiffYres = sharp::AttrAsDouble(options, "tiffYres");
1666
- if (baton->tiffXres == 1.0 && baton->tiffYres == 1.0 && baton->withMetadataDensity > 0) {
1667
- baton->tiffXres = baton->tiffYres = baton->withMetadataDensity / 25.4;
1668
- }
1669
- baton->tiffCompression = sharp::AttrAsEnum<VipsForeignTiffCompression>(
1670
- options, "tiffCompression", VIPS_TYPE_FOREIGN_TIFF_COMPRESSION);
1671
- baton->tiffPredictor = sharp::AttrAsEnum<VipsForeignTiffPredictor>(
1672
- options, "tiffPredictor", VIPS_TYPE_FOREIGN_TIFF_PREDICTOR);
1673
- baton->tiffResolutionUnit = sharp::AttrAsEnum<VipsForeignTiffResunit>(
1674
- options, "tiffResolutionUnit", VIPS_TYPE_FOREIGN_TIFF_RESUNIT);
1675
- baton->heifQuality = sharp::AttrAsUint32(options, "heifQuality");
1676
- baton->heifLossless = sharp::AttrAsBool(options, "heifLossless");
1677
- baton->heifCompression = sharp::AttrAsEnum<VipsForeignHeifCompression>(
1678
- options, "heifCompression", VIPS_TYPE_FOREIGN_HEIF_COMPRESSION);
1679
- baton->heifEffort = sharp::AttrAsUint32(options, "heifEffort");
1680
- baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
1681
- baton->jxlDistance = sharp::AttrAsDouble(options, "jxlDistance");
1682
- baton->jxlDecodingTier = sharp::AttrAsUint32(options, "jxlDecodingTier");
1683
- baton->jxlEffort = sharp::AttrAsUint32(options, "jxlEffort");
1684
- baton->jxlLossless = sharp::AttrAsBool(options, "jxlLossless");
1685
- baton->rawDepth = sharp::AttrAsEnum<VipsBandFormat>(options, "rawDepth", VIPS_TYPE_BAND_FORMAT);
1686
- // Animated output properties
1687
- if (sharp::HasAttr(options, "loop")) {
1688
- baton->loop = sharp::AttrAsUint32(options, "loop");
1689
- }
1690
- if (sharp::HasAttr(options, "delay")) {
1691
- baton->delay = sharp::AttrAsInt32Vector(options, "delay");
1692
- }
1693
- baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
1694
- baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
1695
- baton->tileAngle = sharp::AttrAsInt32(options, "tileAngle");
1696
- baton->tileBackground = sharp::AttrAsVectorOfDouble(options, "tileBackground");
1697
- baton->tileSkipBlanks = sharp::AttrAsInt32(options, "tileSkipBlanks");
1698
- baton->tileContainer = sharp::AttrAsEnum<VipsForeignDzContainer>(
1699
- options, "tileContainer", VIPS_TYPE_FOREIGN_DZ_CONTAINER);
1700
- baton->tileLayout = sharp::AttrAsEnum<VipsForeignDzLayout>(options, "tileLayout", VIPS_TYPE_FOREIGN_DZ_LAYOUT);
1701
- baton->tileFormat = sharp::AttrAsStr(options, "tileFormat");
1702
- baton->tileDepth = sharp::AttrAsEnum<VipsForeignDzDepth>(options, "tileDepth", VIPS_TYPE_FOREIGN_DZ_DEPTH);
1703
- baton->tileCentre = sharp::AttrAsBool(options, "tileCentre");
1704
- baton->tileId = sharp::AttrAsStr(options, "tileId");
1705
- baton->tileBasename = sharp::AttrAsStr(options, "tileBasename");
1706
-
1707
- // Function to notify of libvips warnings
1708
- Napi::Function debuglog = options.Get("debuglog").As<Napi::Function>();
1709
-
1710
- // Function to notify of queue length changes
1711
- Napi::Function queueListener = options.Get("queueListener").As<Napi::Function>();
1712
-
1713
- // Join queue for worker thread
1714
- Napi::Function callback = info[size_t(1)].As<Napi::Function>();
1715
- PipelineWorker *worker = new PipelineWorker(callback, baton, debuglog, queueListener);
1716
- worker->Receiver().Set("options", options);
1717
- worker->Queue();
1718
-
1719
- // Increment queued task counter
1720
- Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<int>(++sharp::counterQueue));
1721
- queueListener.Call(info.This(), { queueLength });
1722
-
1723
- return info.Env().Undefined();
1724
- }