sharp 0.29.1 → 0.30.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.
- package/README.md +5 -5
- package/binding.gyp +2 -0
- package/install/libvips.js +39 -8
- package/lib/constructor.js +13 -4
- package/lib/input.js +21 -4
- package/lib/libvips.js +16 -2
- package/lib/operation.js +14 -1
- package/lib/output.js +158 -69
- package/lib/platform.js +1 -1
- package/lib/resize.js +19 -0
- package/lib/sharp.js +11 -3
- package/lib/utility.js +24 -5
- package/package.json +28 -13
- package/src/common.cc +125 -19
- package/src/common.h +39 -5
- package/src/libvips/cplusplus/VImage.cpp +34 -16
- package/src/libvips/cplusplus/vips-operators.cpp +29 -1
- package/src/metadata.cc +6 -0
- package/src/metadata.h +1 -0
- package/src/operations.cc +94 -0
- package/src/operations.h +12 -0
- package/src/pipeline.cc +291 -255
- package/src/pipeline.h +19 -19
- package/src/sharp.cc +16 -0
package/src/pipeline.cc
CHANGED
|
@@ -69,15 +69,24 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
69
69
|
std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
|
|
70
70
|
image = sharp::EnsureColourspace(image, baton->colourspaceInput);
|
|
71
71
|
|
|
72
|
+
int nPages = baton->input->pages;
|
|
73
|
+
if (nPages == -1) {
|
|
74
|
+
// Resolve the number of pages if we need to render until the end of the document
|
|
75
|
+
nPages = image.get_typeof(VIPS_META_N_PAGES) != 0
|
|
76
|
+
? image.get_int(VIPS_META_N_PAGES) - baton->input->page
|
|
77
|
+
: 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Get pre-resize page height
|
|
81
|
+
int pageHeight = sharp::GetPageHeight(image);
|
|
82
|
+
|
|
72
83
|
// Calculate angle of rotation
|
|
73
84
|
VipsAngle rotation;
|
|
85
|
+
bool flip = FALSE;
|
|
86
|
+
bool flop = FALSE;
|
|
74
87
|
if (baton->useExifOrientation) {
|
|
75
88
|
// Rotate and flip image according to Exif orientation
|
|
76
|
-
bool flip;
|
|
77
|
-
bool flop;
|
|
78
89
|
std::tie(rotation, flip, flop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
|
|
79
|
-
baton->flip = baton->flip || flip;
|
|
80
|
-
baton->flop = baton->flop || flop;
|
|
81
90
|
} else {
|
|
82
91
|
rotation = CalculateAngleRotation(baton->angle);
|
|
83
92
|
}
|
|
@@ -86,9 +95,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
86
95
|
if (baton->rotateBeforePreExtract) {
|
|
87
96
|
if (rotation != VIPS_ANGLE_D0) {
|
|
88
97
|
image = image.rot(rotation);
|
|
98
|
+
if (flip) {
|
|
99
|
+
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
100
|
+
flip = FALSE;
|
|
101
|
+
}
|
|
102
|
+
if (flop) {
|
|
103
|
+
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
104
|
+
flop = FALSE;
|
|
105
|
+
}
|
|
89
106
|
image = sharp::RemoveExifOrientation(image);
|
|
90
107
|
}
|
|
91
108
|
if (baton->rotationAngle != 0.0) {
|
|
109
|
+
MultiPageUnsupported(nPages, "Rotate");
|
|
92
110
|
std::vector<double> background;
|
|
93
111
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, FALSE);
|
|
94
112
|
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
|
@@ -97,6 +115,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
97
115
|
|
|
98
116
|
// Trim
|
|
99
117
|
if (baton->trimThreshold > 0.0) {
|
|
118
|
+
MultiPageUnsupported(nPages, "Trim");
|
|
100
119
|
image = sharp::Trim(image, baton->trimThreshold);
|
|
101
120
|
baton->trimOffsetLeft = image.xoffset();
|
|
102
121
|
baton->trimOffsetTop = image.yoffset();
|
|
@@ -104,190 +123,168 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
104
123
|
|
|
105
124
|
// Pre extraction
|
|
106
125
|
if (baton->topOffsetPre != -1) {
|
|
107
|
-
image =
|
|
126
|
+
image = nPages > 1
|
|
127
|
+
? sharp::CropMultiPage(image,
|
|
128
|
+
baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre, nPages, &pageHeight)
|
|
129
|
+
: image.extract_area(baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre);
|
|
108
130
|
}
|
|
109
131
|
|
|
110
132
|
// Get pre-resize image width and height
|
|
111
133
|
int inputWidth = image.width();
|
|
112
134
|
int inputHeight = image.height();
|
|
113
|
-
if (!baton->rotateBeforePreExtract &&
|
|
114
|
-
(rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270)) {
|
|
115
|
-
// Swap input output width and height when rotating by 90 or 270 degrees
|
|
116
|
-
std::swap(inputWidth, inputHeight);
|
|
117
|
-
}
|
|
118
135
|
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (baton->width > inputWidth) {
|
|
123
|
-
baton->width = inputWidth;
|
|
124
|
-
}
|
|
125
|
-
if (baton->height > inputHeight) {
|
|
126
|
-
baton->height = inputHeight;
|
|
127
|
-
}
|
|
136
|
+
// Is there just one page? Shrink to inputHeight instead
|
|
137
|
+
if (nPages == 1) {
|
|
138
|
+
pageHeight = inputHeight;
|
|
128
139
|
}
|
|
129
140
|
|
|
130
141
|
// Scaling calculations
|
|
131
|
-
double
|
|
132
|
-
double
|
|
142
|
+
double hshrink;
|
|
143
|
+
double vshrink;
|
|
133
144
|
int targetResizeWidth = baton->width;
|
|
134
145
|
int targetResizeHeight = baton->height;
|
|
135
|
-
if (baton->width > 0 && baton->height > 0) {
|
|
136
|
-
// Fixed width and height
|
|
137
|
-
xfactor = static_cast<double>(inputWidth) / static_cast<double>(baton->width);
|
|
138
|
-
yfactor = static_cast<double>(inputHeight) / static_cast<double>(baton->height);
|
|
139
|
-
switch (baton->canvas) {
|
|
140
|
-
case Canvas::CROP:
|
|
141
|
-
if (xfactor < yfactor) {
|
|
142
|
-
targetResizeHeight = static_cast<int>(round(static_cast<double>(inputHeight) / xfactor));
|
|
143
|
-
yfactor = xfactor;
|
|
144
|
-
} else {
|
|
145
|
-
targetResizeWidth = static_cast<int>(round(static_cast<double>(inputWidth) / yfactor));
|
|
146
|
-
xfactor = yfactor;
|
|
147
|
-
}
|
|
148
|
-
break;
|
|
149
|
-
case Canvas::EMBED:
|
|
150
|
-
if (xfactor > yfactor) {
|
|
151
|
-
targetResizeHeight = static_cast<int>(round(static_cast<double>(inputHeight) / xfactor));
|
|
152
|
-
yfactor = xfactor;
|
|
153
|
-
} else {
|
|
154
|
-
targetResizeWidth = static_cast<int>(round(static_cast<double>(inputWidth) / yfactor));
|
|
155
|
-
xfactor = yfactor;
|
|
156
|
-
}
|
|
157
|
-
break;
|
|
158
|
-
case Canvas::MAX:
|
|
159
|
-
if (xfactor > yfactor) {
|
|
160
|
-
targetResizeHeight = baton->height = static_cast<int>(round(static_cast<double>(inputHeight) / xfactor));
|
|
161
|
-
yfactor = xfactor;
|
|
162
|
-
} else {
|
|
163
|
-
targetResizeWidth = baton->width = static_cast<int>(round(static_cast<double>(inputWidth) / yfactor));
|
|
164
|
-
xfactor = yfactor;
|
|
165
|
-
}
|
|
166
|
-
break;
|
|
167
|
-
case Canvas::MIN:
|
|
168
|
-
if (xfactor < yfactor) {
|
|
169
|
-
targetResizeHeight = baton->height = static_cast<int>(round(static_cast<double>(inputHeight) / xfactor));
|
|
170
|
-
yfactor = xfactor;
|
|
171
|
-
} else {
|
|
172
|
-
targetResizeWidth = baton->width = static_cast<int>(round(static_cast<double>(inputWidth) / yfactor));
|
|
173
|
-
xfactor = yfactor;
|
|
174
|
-
}
|
|
175
|
-
break;
|
|
176
|
-
case Canvas::IGNORE_ASPECT:
|
|
177
|
-
if (!baton->rotateBeforePreExtract &&
|
|
178
|
-
(rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270)) {
|
|
179
|
-
std::swap(xfactor, yfactor);
|
|
180
|
-
}
|
|
181
|
-
break;
|
|
182
|
-
}
|
|
183
|
-
} else if (baton->width > 0) {
|
|
184
|
-
// Fixed width
|
|
185
|
-
xfactor = static_cast<double>(inputWidth) / static_cast<double>(baton->width);
|
|
186
|
-
if (baton->canvas == Canvas::IGNORE_ASPECT) {
|
|
187
|
-
targetResizeHeight = baton->height = inputHeight;
|
|
188
|
-
} else {
|
|
189
|
-
// Auto height
|
|
190
|
-
yfactor = xfactor;
|
|
191
|
-
targetResizeHeight = baton->height = static_cast<int>(round(static_cast<double>(inputHeight) / yfactor));
|
|
192
|
-
}
|
|
193
|
-
} else if (baton->height > 0) {
|
|
194
|
-
// Fixed height
|
|
195
|
-
yfactor = static_cast<double>(inputHeight) / static_cast<double>(baton->height);
|
|
196
|
-
if (baton->canvas == Canvas::IGNORE_ASPECT) {
|
|
197
|
-
targetResizeWidth = baton->width = inputWidth;
|
|
198
|
-
} else {
|
|
199
|
-
// Auto width
|
|
200
|
-
xfactor = yfactor;
|
|
201
|
-
targetResizeWidth = baton->width = static_cast<int>(round(static_cast<double>(inputWidth) / xfactor));
|
|
202
|
-
}
|
|
203
|
-
} else {
|
|
204
|
-
// Identity transform
|
|
205
|
-
baton->width = inputWidth;
|
|
206
|
-
baton->height = inputHeight;
|
|
207
|
-
}
|
|
208
146
|
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
int yshrink = std::max(1, static_cast<int>(floor(yfactor)));
|
|
147
|
+
// Swap input output width and height when rotating by 90 or 270 degrees
|
|
148
|
+
bool swap = !baton->rotateBeforePreExtract && (rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270);
|
|
212
149
|
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
150
|
+
// Shrink to pageHeight, so we work for multi-page images
|
|
151
|
+
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
|
152
|
+
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
|
|
153
|
+
baton->canvas, swap, baton->withoutEnlargement, baton->withoutReduction);
|
|
216
154
|
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
int shrink_on_load = 1;
|
|
155
|
+
// The jpeg preload shrink.
|
|
156
|
+
int jpegShrinkOnLoad = 1;
|
|
220
157
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
158
|
+
// WebP, PDF, SVG scale
|
|
159
|
+
double scale = 1.0;
|
|
160
|
+
|
|
161
|
+
// Try to reload input using shrink-on-load for JPEG, WebP, SVG and PDF, when:
|
|
162
|
+
// - the width or height parameters are specified;
|
|
163
|
+
// - gamma correction doesn't need to be applied;
|
|
164
|
+
// - trimming or pre-resize extract isn't required;
|
|
165
|
+
// - input colourspace is not specified;
|
|
166
|
+
bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) &&
|
|
230
167
|
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0 &&
|
|
231
|
-
baton->colourspaceInput == VIPS_INTERPRETATION_LAST
|
|
232
|
-
|
|
233
|
-
) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
168
|
+
baton->colourspaceInput == VIPS_INTERPRETATION_LAST;
|
|
169
|
+
|
|
170
|
+
if (shouldPreShrink) {
|
|
171
|
+
// The common part of the shrink: the bit by which both axes must be shrunk
|
|
172
|
+
double shrink = std::min(hshrink, vshrink);
|
|
173
|
+
|
|
174
|
+
if (inputImageType == sharp::ImageType::JPEG) {
|
|
175
|
+
// Leave at least a factor of two for the final resize step, when fastShrinkOnLoad: false
|
|
176
|
+
// for more consistent results and to avoid extra sharpness to the image
|
|
177
|
+
int factor = baton->fastShrinkOnLoad ? 1 : 2;
|
|
178
|
+
if (shrink >= 8 * factor) {
|
|
179
|
+
jpegShrinkOnLoad = 8;
|
|
180
|
+
} else if (shrink >= 4 * factor) {
|
|
181
|
+
jpegShrinkOnLoad = 4;
|
|
182
|
+
} else if (shrink >= 2 * factor) {
|
|
183
|
+
jpegShrinkOnLoad = 2;
|
|
184
|
+
}
|
|
185
|
+
// Lower shrink-on-load for known libjpeg rounding errors
|
|
186
|
+
if (jpegShrinkOnLoad > 1 && static_cast<int>(shrink) == jpegShrinkOnLoad) {
|
|
187
|
+
jpegShrinkOnLoad /= 2;
|
|
188
|
+
}
|
|
189
|
+
} else if (inputImageType == sharp::ImageType::WEBP ||
|
|
190
|
+
inputImageType == sharp::ImageType::SVG ||
|
|
191
|
+
inputImageType == sharp::ImageType::PDF) {
|
|
192
|
+
scale = 1.0 / shrink;
|
|
246
193
|
}
|
|
247
194
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
if (shrink_on_load > 1) {
|
|
255
|
-
// Reload input using shrink-on-load
|
|
195
|
+
|
|
196
|
+
// Reload input using shrink-on-load, it'll be an integer shrink
|
|
197
|
+
// factor for jpegload*, a double scale factor for webpload*,
|
|
198
|
+
// pdfload* and svgload*
|
|
199
|
+
if (jpegShrinkOnLoad > 1) {
|
|
256
200
|
vips::VOption *option = VImage::option()
|
|
257
201
|
->set("access", baton->input->access)
|
|
258
|
-
->set("shrink",
|
|
202
|
+
->set("shrink", jpegShrinkOnLoad)
|
|
259
203
|
->set("fail", baton->input->failOnError);
|
|
260
204
|
if (baton->input->buffer != nullptr) {
|
|
205
|
+
// Reload JPEG buffer
|
|
261
206
|
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
|
262
|
-
|
|
263
|
-
// Reload JPEG buffer
|
|
264
|
-
image = VImage::jpegload_buffer(blob, option);
|
|
265
|
-
} else {
|
|
266
|
-
// Reload WebP buffer
|
|
267
|
-
image = VImage::webpload_buffer(blob, option);
|
|
268
|
-
}
|
|
207
|
+
image = VImage::jpegload_buffer(blob, option);
|
|
269
208
|
vips_area_unref(reinterpret_cast<VipsArea*>(blob));
|
|
270
209
|
} else {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
210
|
+
// Reload JPEG file
|
|
211
|
+
image = VImage::jpegload(const_cast<char*>(baton->input->file.data()), option);
|
|
212
|
+
}
|
|
213
|
+
} else if (scale != 1.0) {
|
|
214
|
+
vips::VOption *option = VImage::option()
|
|
215
|
+
->set("access", baton->input->access)
|
|
216
|
+
->set("scale", scale)
|
|
217
|
+
->set("fail", baton->input->failOnError);
|
|
218
|
+
if (inputImageType == sharp::ImageType::WEBP) {
|
|
219
|
+
option->set("n", baton->input->pages);
|
|
220
|
+
option->set("page", baton->input->page);
|
|
221
|
+
|
|
222
|
+
if (baton->input->buffer != nullptr) {
|
|
223
|
+
// Reload WebP buffer
|
|
224
|
+
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
|
225
|
+
image = VImage::webpload_buffer(blob, option);
|
|
226
|
+
vips_area_unref(reinterpret_cast<VipsArea*>(blob));
|
|
274
227
|
} else {
|
|
275
228
|
// Reload WebP file
|
|
276
229
|
image = VImage::webpload(const_cast<char*>(baton->input->file.data()), option);
|
|
277
230
|
}
|
|
231
|
+
} else if (inputImageType == sharp::ImageType::SVG) {
|
|
232
|
+
option->set("unlimited", baton->input->unlimited);
|
|
233
|
+
option->set("dpi", baton->input->density);
|
|
234
|
+
|
|
235
|
+
if (baton->input->buffer != nullptr) {
|
|
236
|
+
// Reload SVG buffer
|
|
237
|
+
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
|
238
|
+
image = VImage::svgload_buffer(blob, option);
|
|
239
|
+
vips_area_unref(reinterpret_cast<VipsArea*>(blob));
|
|
240
|
+
} else {
|
|
241
|
+
// Reload SVG file
|
|
242
|
+
image = VImage::svgload(const_cast<char*>(baton->input->file.data()), option);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
sharp::SetDensity(image, baton->input->density);
|
|
246
|
+
} else if (inputImageType == sharp::ImageType::PDF) {
|
|
247
|
+
option->set("n", baton->input->pages);
|
|
248
|
+
option->set("page", baton->input->page);
|
|
249
|
+
option->set("dpi", baton->input->density);
|
|
250
|
+
|
|
251
|
+
if (baton->input->buffer != nullptr) {
|
|
252
|
+
// Reload PDF buffer
|
|
253
|
+
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
|
254
|
+
image = VImage::pdfload_buffer(blob, option);
|
|
255
|
+
vips_area_unref(reinterpret_cast<VipsArea*>(blob));
|
|
256
|
+
} else {
|
|
257
|
+
// Reload PDF file
|
|
258
|
+
image = VImage::pdfload(const_cast<char*>(baton->input->file.data()), option);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
sharp::SetDensity(image, baton->input->density);
|
|
278
262
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Any pre-shrinking may already have been done
|
|
266
|
+
inputWidth = image.width();
|
|
267
|
+
inputHeight = image.height();
|
|
268
|
+
|
|
269
|
+
// After pre-shrink, but before the main shrink stage
|
|
270
|
+
// Reuse the initial pageHeight if we didn't pre-shrink
|
|
271
|
+
if (shouldPreShrink) {
|
|
272
|
+
pageHeight = sharp::GetPageHeight(image);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Shrink to pageHeight, so we work for multi-page images
|
|
276
|
+
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
|
277
|
+
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
|
|
278
|
+
baton->canvas, swap, baton->withoutEnlargement, baton->withoutReduction);
|
|
279
|
+
|
|
280
|
+
int targetHeight = static_cast<int>(std::rint(static_cast<double>(pageHeight) / vshrink));
|
|
281
|
+
int targetPageHeight = targetHeight;
|
|
282
|
+
|
|
283
|
+
// In toilet-roll mode, we must adjust vshrink so that we exactly hit
|
|
284
|
+
// pageHeight or we'll have pixels straddling pixel boundaries
|
|
285
|
+
if (inputHeight > pageHeight) {
|
|
286
|
+
targetHeight *= nPages;
|
|
287
|
+
vshrink = static_cast<double>(inputHeight) / targetHeight;
|
|
291
288
|
}
|
|
292
289
|
|
|
293
290
|
// Ensure we're using a device-independent colour space
|
|
@@ -341,7 +338,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
341
338
|
image = image.colourspace(VIPS_INTERPRETATION_B_W);
|
|
342
339
|
}
|
|
343
340
|
|
|
344
|
-
bool const shouldResize =
|
|
341
|
+
bool const shouldResize = hshrink != 1.0 || vshrink != 1.0;
|
|
345
342
|
bool const shouldBlur = baton->blurSigma != 0.0;
|
|
346
343
|
bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0;
|
|
347
344
|
bool const shouldSharpen = baton->sharpenSigma != 0.0;
|
|
@@ -375,35 +372,33 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
375
372
|
) {
|
|
376
373
|
throw vips::VError("Unknown kernel");
|
|
377
374
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
xfactor = 2 * image.width();
|
|
381
|
-
baton->width = 1;
|
|
382
|
-
}
|
|
383
|
-
if (image.height() / yfactor < 0.5) {
|
|
384
|
-
yfactor = 2 * image.height();
|
|
385
|
-
baton->height = 1;
|
|
386
|
-
}
|
|
387
|
-
image = image.resize(1.0 / xfactor, VImage::option()
|
|
388
|
-
->set("vscale", 1.0 / yfactor)
|
|
375
|
+
image = image.resize(1.0 / hshrink, VImage::option()
|
|
376
|
+
->set("vscale", 1.0 / vshrink)
|
|
389
377
|
->set("kernel", kernel));
|
|
390
378
|
}
|
|
391
379
|
|
|
392
380
|
// Rotate post-extract 90-angle
|
|
393
|
-
if (!baton->rotateBeforePreExtract &&
|
|
394
|
-
|
|
395
|
-
|
|
381
|
+
if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) {
|
|
382
|
+
image = image.rot(rotation);
|
|
383
|
+
if (flip) {
|
|
384
|
+
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
385
|
+
flip = FALSE;
|
|
386
|
+
}
|
|
387
|
+
if (flop) {
|
|
388
|
+
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
389
|
+
flop = FALSE;
|
|
390
|
+
}
|
|
391
|
+
image = sharp::RemoveExifOrientation(image);
|
|
396
392
|
}
|
|
397
393
|
|
|
398
|
-
|
|
399
394
|
// Flip (mirror about Y axis)
|
|
400
|
-
if (baton->flip) {
|
|
395
|
+
if (baton->flip || flip) {
|
|
401
396
|
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
402
397
|
image = sharp::RemoveExifOrientation(image);
|
|
403
398
|
}
|
|
404
399
|
|
|
405
400
|
// Flop (mirror about X axis)
|
|
406
|
-
if (baton->flop) {
|
|
401
|
+
if (baton->flop || flop) {
|
|
407
402
|
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
408
403
|
image = sharp::RemoveExifOrientation(image);
|
|
409
404
|
}
|
|
@@ -421,52 +416,68 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
421
416
|
image = image.copy(VImage::option()->set("interpretation", baton->colourspace));
|
|
422
417
|
}
|
|
423
418
|
|
|
419
|
+
inputWidth = image.width();
|
|
420
|
+
inputHeight = nPages > 1 ? targetPageHeight : image.height();
|
|
421
|
+
|
|
422
|
+
// Resolve dimensions
|
|
423
|
+
if (baton->width <= 0) {
|
|
424
|
+
baton->width = inputWidth;
|
|
425
|
+
}
|
|
426
|
+
if (baton->height <= 0) {
|
|
427
|
+
baton->height = inputHeight;
|
|
428
|
+
}
|
|
429
|
+
|
|
424
430
|
// Crop/embed
|
|
425
|
-
if (
|
|
426
|
-
if (baton->canvas == Canvas::EMBED) {
|
|
431
|
+
if (inputWidth != baton->width || inputHeight != baton->height) {
|
|
432
|
+
if (baton->canvas == sharp::Canvas::EMBED) {
|
|
427
433
|
std::vector<double> background;
|
|
428
434
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->resizeBackground, shouldPremultiplyAlpha);
|
|
429
435
|
|
|
430
436
|
// Embed
|
|
431
437
|
|
|
432
|
-
// Calculate where to position the
|
|
438
|
+
// Calculate where to position the embedded image if gravity specified, else center.
|
|
433
439
|
int left;
|
|
434
440
|
int top;
|
|
435
441
|
|
|
436
|
-
left = static_cast<int>(round((baton->width -
|
|
437
|
-
top = static_cast<int>(round((baton->height -
|
|
442
|
+
left = static_cast<int>(round((baton->width - inputWidth) / 2));
|
|
443
|
+
top = static_cast<int>(round((baton->height - inputHeight) / 2));
|
|
438
444
|
|
|
439
|
-
int width = std::max(
|
|
440
|
-
int height = std::max(
|
|
445
|
+
int width = std::max(inputWidth, baton->width);
|
|
446
|
+
int height = std::max(inputHeight, baton->height);
|
|
441
447
|
std::tie(left, top) = sharp::CalculateEmbedPosition(
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
image =
|
|
445
|
-
|
|
446
|
-
|
|
448
|
+
inputWidth, inputHeight, baton->width, baton->height, baton->position);
|
|
449
|
+
|
|
450
|
+
image = nPages > 1
|
|
451
|
+
? sharp::EmbedMultiPage(image,
|
|
452
|
+
left, top, width, height, background, nPages, &targetPageHeight)
|
|
453
|
+
: image.embed(left, top, width, height, VImage::option()
|
|
454
|
+
->set("extend", VIPS_EXTEND_BACKGROUND)
|
|
455
|
+
->set("background", background));
|
|
456
|
+
} else if (baton->canvas == sharp::Canvas::CROP) {
|
|
457
|
+
if (baton->width > inputWidth) {
|
|
458
|
+
baton->width = inputWidth;
|
|
459
|
+
}
|
|
460
|
+
if (baton->height > inputHeight) {
|
|
461
|
+
baton->height = inputHeight;
|
|
462
|
+
}
|
|
447
463
|
|
|
448
|
-
|
|
449
|
-
baton->canvas != Canvas::IGNORE_ASPECT &&
|
|
450
|
-
(image.width() > baton->width || image.height() > baton->height)
|
|
451
|
-
) {
|
|
452
|
-
// Crop/max/min
|
|
464
|
+
// Crop
|
|
453
465
|
if (baton->position < 9) {
|
|
454
466
|
// Gravity-based crop
|
|
455
467
|
int left;
|
|
456
468
|
int top;
|
|
457
469
|
std::tie(left, top) = sharp::CalculateCrop(
|
|
458
|
-
|
|
459
|
-
int width = std::min(
|
|
460
|
-
int height = std::min(
|
|
461
|
-
|
|
470
|
+
inputWidth, inputHeight, baton->width, baton->height, baton->position);
|
|
471
|
+
int width = std::min(inputWidth, baton->width);
|
|
472
|
+
int height = std::min(inputHeight, baton->height);
|
|
473
|
+
|
|
474
|
+
image = nPages > 1
|
|
475
|
+
? sharp::CropMultiPage(image,
|
|
476
|
+
left, top, width, height, nPages, &targetPageHeight)
|
|
477
|
+
: image.extract_area(left, top, width, height);
|
|
462
478
|
} else {
|
|
463
479
|
// Attention-based or Entropy-based crop
|
|
464
|
-
|
|
465
|
-
baton->width = image.width();
|
|
466
|
-
}
|
|
467
|
-
if (baton->height > image.height()) {
|
|
468
|
-
baton->height = image.height();
|
|
469
|
-
}
|
|
480
|
+
MultiPageUnsupported(nPages, "Resize strategy");
|
|
470
481
|
image = image.tilecache(VImage::option()
|
|
471
482
|
->set("access", VIPS_ACCESS_RANDOM)
|
|
472
483
|
->set("threaded", TRUE));
|
|
@@ -481,6 +492,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
481
492
|
|
|
482
493
|
// Rotate post-extract non-90 angle
|
|
483
494
|
if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) {
|
|
495
|
+
MultiPageUnsupported(nPages, "Rotate");
|
|
484
496
|
std::vector<double> background;
|
|
485
497
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha);
|
|
486
498
|
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
|
@@ -488,20 +500,32 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
488
500
|
|
|
489
501
|
// Post extraction
|
|
490
502
|
if (baton->topOffsetPost != -1) {
|
|
491
|
-
|
|
492
|
-
|
|
503
|
+
if (nPages > 1) {
|
|
504
|
+
image = sharp::CropMultiPage(image,
|
|
505
|
+
baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost,
|
|
506
|
+
nPages, &targetPageHeight);
|
|
507
|
+
|
|
508
|
+
// heightPost is used in the info object, so update to reflect the number of pages
|
|
509
|
+
baton->heightPost *= nPages;
|
|
510
|
+
} else {
|
|
511
|
+
image = image.extract_area(
|
|
512
|
+
baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost);
|
|
513
|
+
}
|
|
493
514
|
}
|
|
494
515
|
|
|
495
516
|
// Affine transform
|
|
496
517
|
if (baton->affineMatrix.size() > 0) {
|
|
518
|
+
MultiPageUnsupported(nPages, "Affine");
|
|
497
519
|
std::vector<double> background;
|
|
498
520
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
|
|
521
|
+
vips::VInterpolate interp = vips::VInterpolate::new_from_name(
|
|
522
|
+
const_cast<char*>(baton->affineInterpolator.data()));
|
|
499
523
|
image = image.affine(baton->affineMatrix, VImage::option()->set("background", background)
|
|
500
524
|
->set("idx", baton->affineIdx)
|
|
501
525
|
->set("idy", baton->affineIdy)
|
|
502
526
|
->set("odx", baton->affineOdx)
|
|
503
527
|
->set("ody", baton->affineOdy)
|
|
504
|
-
->set("interpolate",
|
|
528
|
+
->set("interpolate", interp));
|
|
505
529
|
}
|
|
506
530
|
|
|
507
531
|
// Extend edges
|
|
@@ -511,10 +535,13 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
511
535
|
|
|
512
536
|
// Embed
|
|
513
537
|
baton->width = image.width() + baton->extendLeft + baton->extendRight;
|
|
514
|
-
baton->height = image.height() + baton->extendTop + baton->extendBottom;
|
|
538
|
+
baton->height = (nPages > 1 ? targetPageHeight : image.height()) + baton->extendTop + baton->extendBottom;
|
|
515
539
|
|
|
516
|
-
image =
|
|
517
|
-
|
|
540
|
+
image = nPages > 1
|
|
541
|
+
? sharp::EmbedMultiPage(image,
|
|
542
|
+
baton->extendLeft, baton->extendTop, baton->width, baton->height, background, nPages, &targetPageHeight)
|
|
543
|
+
: image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height,
|
|
544
|
+
VImage::option()->set("extend", VIPS_EXTEND_BACKGROUND)->set("background", background));
|
|
518
545
|
}
|
|
519
546
|
// Median - must happen before blurring, due to the utility of blurring after thresholding
|
|
520
547
|
if (shouldApplyMedian) {
|
|
@@ -754,16 +781,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
754
781
|
baton->width = image.width();
|
|
755
782
|
baton->height = image.height();
|
|
756
783
|
|
|
757
|
-
bool const supportsGifOutput = vips_type_find("VipsOperation", "magicksave") != 0 &&
|
|
758
|
-
vips_type_find("VipsOperation", "magicksave_buffer") != 0;
|
|
759
|
-
|
|
760
784
|
image = sharp::SetAnimationProperties(
|
|
761
|
-
image,
|
|
762
|
-
baton->pageHeight,
|
|
763
|
-
baton->delay,
|
|
764
|
-
baton->loop);
|
|
785
|
+
image, nPages, targetPageHeight, baton->delay, baton->loop);
|
|
765
786
|
|
|
766
787
|
// Output
|
|
788
|
+
sharp::SetTimeout(image, baton->timeoutSeconds);
|
|
767
789
|
if (baton->fileOut.empty()) {
|
|
768
790
|
// Buffer output
|
|
769
791
|
if (baton->formatOut == "jpeg" || (baton->formatOut == "input" && inputImageType == sharp::ImageType::JPEG)) {
|
|
@@ -808,8 +830,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
808
830
|
vips_area_unref(area);
|
|
809
831
|
baton->formatOut = "jp2";
|
|
810
832
|
} else if (baton->formatOut == "png" || (baton->formatOut == "input" &&
|
|
811
|
-
(inputImageType == sharp::ImageType::PNG ||
|
|
812
|
-
inputImageType == sharp::ImageType::SVG))) {
|
|
833
|
+
(inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::SVG))) {
|
|
813
834
|
// Write PNG to buffer
|
|
814
835
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
|
815
836
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.pngsave_buffer(VImage::option()
|
|
@@ -819,7 +840,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
819
840
|
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
|
|
820
841
|
->set("palette", baton->pngPalette)
|
|
821
842
|
->set("Q", baton->pngQuality)
|
|
822
|
-
->set("
|
|
843
|
+
->set("effort", baton->pngEffort)
|
|
844
|
+
->set("bitdepth", sharp::Is16Bit(image.interpretation()) ? 16 : baton->pngBitdepth)
|
|
823
845
|
->set("dither", baton->pngDither)));
|
|
824
846
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
825
847
|
baton->bufferOutLength = area->length;
|
|
@@ -836,7 +858,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
836
858
|
->set("lossless", baton->webpLossless)
|
|
837
859
|
->set("near_lossless", baton->webpNearLossless)
|
|
838
860
|
->set("smart_subsample", baton->webpSmartSubsample)
|
|
839
|
-
->set("
|
|
861
|
+
->set("effort", baton->webpEffort)
|
|
840
862
|
->set("alpha_q", baton->webpAlphaQuality)));
|
|
841
863
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
842
864
|
baton->bufferOutLength = area->length;
|
|
@@ -844,14 +866,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
844
866
|
vips_area_unref(area);
|
|
845
867
|
baton->formatOut = "webp";
|
|
846
868
|
} else if (baton->formatOut == "gif" ||
|
|
847
|
-
(baton->formatOut == "input" && inputImageType == sharp::ImageType::GIF
|
|
869
|
+
(baton->formatOut == "input" && inputImageType == sharp::ImageType::GIF)) {
|
|
848
870
|
// Write GIF to buffer
|
|
849
871
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
|
850
|
-
VipsArea *area = reinterpret_cast<VipsArea*>(image.
|
|
872
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.gifsave_buffer(VImage::option()
|
|
851
873
|
->set("strip", !baton->withMetadata)
|
|
852
|
-
->set("
|
|
853
|
-
->set("
|
|
854
|
-
->set("
|
|
874
|
+
->set("bitdepth", baton->gifBitdepth)
|
|
875
|
+
->set("effort", baton->gifEffort)
|
|
876
|
+
->set("dither", baton->gifDither)));
|
|
855
877
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
856
878
|
baton->bufferOutLength = area->length;
|
|
857
879
|
area->free_fn = nullptr;
|
|
@@ -879,7 +901,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
879
901
|
->set("tile_height", baton->tiffTileHeight)
|
|
880
902
|
->set("tile_width", baton->tiffTileWidth)
|
|
881
903
|
->set("xres", baton->tiffXres)
|
|
882
|
-
->set("yres", baton->tiffYres)
|
|
904
|
+
->set("yres", baton->tiffYres)
|
|
905
|
+
->set("resunit", baton->tiffResolutionUnit)));
|
|
883
906
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
884
907
|
baton->bufferOutLength = area->length;
|
|
885
908
|
area->free_fn = nullptr;
|
|
@@ -893,7 +916,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
893
916
|
->set("strip", !baton->withMetadata)
|
|
894
917
|
->set("Q", baton->heifQuality)
|
|
895
918
|
->set("compression", baton->heifCompression)
|
|
896
|
-
->set("
|
|
919
|
+
->set("effort", baton->heifEffort)
|
|
897
920
|
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
|
898
921
|
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
899
922
|
->set("lossless", baton->heifLossless)));
|
|
@@ -978,8 +1001,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
978
1001
|
->set("tile_width", baton->jp2TileWidth));
|
|
979
1002
|
baton->formatOut = "jp2";
|
|
980
1003
|
} else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput &&
|
|
981
|
-
(inputImageType == sharp::ImageType::PNG ||
|
|
982
|
-
inputImageType == sharp::ImageType::SVG))) {
|
|
1004
|
+
(inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::SVG))) {
|
|
983
1005
|
// Write PNG to file
|
|
984
1006
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
|
985
1007
|
image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
@@ -989,7 +1011,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
989
1011
|
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
|
|
990
1012
|
->set("palette", baton->pngPalette)
|
|
991
1013
|
->set("Q", baton->pngQuality)
|
|
992
|
-
->set("bitdepth", baton->pngBitdepth)
|
|
1014
|
+
->set("bitdepth", sharp::Is16Bit(image.interpretation()) ? 16 : baton->pngBitdepth)
|
|
1015
|
+
->set("effort", baton->pngEffort)
|
|
993
1016
|
->set("dither", baton->pngDither));
|
|
994
1017
|
baton->formatOut = "png";
|
|
995
1018
|
} else if (baton->formatOut == "webp" || (mightMatchInput && isWebp) ||
|
|
@@ -1002,18 +1025,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1002
1025
|
->set("lossless", baton->webpLossless)
|
|
1003
1026
|
->set("near_lossless", baton->webpNearLossless)
|
|
1004
1027
|
->set("smart_subsample", baton->webpSmartSubsample)
|
|
1005
|
-
->set("
|
|
1028
|
+
->set("effort", baton->webpEffort)
|
|
1006
1029
|
->set("alpha_q", baton->webpAlphaQuality));
|
|
1007
1030
|
baton->formatOut = "webp";
|
|
1008
1031
|
} else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
|
|
1009
|
-
(willMatchInput && inputImageType == sharp::ImageType::GIF
|
|
1032
|
+
(willMatchInput && inputImageType == sharp::ImageType::GIF)) {
|
|
1010
1033
|
// Write GIF to file
|
|
1011
1034
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
|
1012
|
-
image.
|
|
1035
|
+
image.gifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1013
1036
|
->set("strip", !baton->withMetadata)
|
|
1014
|
-
->set("
|
|
1015
|
-
->set("
|
|
1016
|
-
->set("
|
|
1037
|
+
->set("bitdepth", baton->gifBitdepth)
|
|
1038
|
+
->set("effort", baton->gifEffort)
|
|
1039
|
+
->set("dither", baton->gifDither));
|
|
1017
1040
|
baton->formatOut = "gif";
|
|
1018
1041
|
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
|
1019
1042
|
(willMatchInput && inputImageType == sharp::ImageType::TIFF)) {
|
|
@@ -1037,7 +1060,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1037
1060
|
->set("tile_height", baton->tiffTileHeight)
|
|
1038
1061
|
->set("tile_width", baton->tiffTileWidth)
|
|
1039
1062
|
->set("xres", baton->tiffXres)
|
|
1040
|
-
->set("yres", baton->tiffYres)
|
|
1063
|
+
->set("yres", baton->tiffYres)
|
|
1064
|
+
->set("resunit", baton->tiffResolutionUnit));
|
|
1041
1065
|
baton->formatOut = "tiff";
|
|
1042
1066
|
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
|
1043
1067
|
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
|
|
@@ -1047,7 +1071,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1047
1071
|
->set("strip", !baton->withMetadata)
|
|
1048
1072
|
->set("Q", baton->heifQuality)
|
|
1049
1073
|
->set("compression", baton->heifCompression)
|
|
1050
|
-
->set("
|
|
1074
|
+
->set("effort", baton->heifEffort)
|
|
1051
1075
|
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
|
1052
1076
|
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
1053
1077
|
->set("lossless", baton->heifLossless));
|
|
@@ -1072,7 +1096,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1072
1096
|
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
|
|
1073
1097
|
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
|
|
1074
1098
|
{"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
|
|
1075
|
-
{"
|
|
1099
|
+
{"effort", std::to_string(baton->webpEffort)}
|
|
1076
1100
|
};
|
|
1077
1101
|
suffix = AssembleSuffixString(".webp", options);
|
|
1078
1102
|
} else {
|
|
@@ -1221,6 +1245,12 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1221
1245
|
Napi::FunctionReference debuglog;
|
|
1222
1246
|
Napi::FunctionReference queueListener;
|
|
1223
1247
|
|
|
1248
|
+
void MultiPageUnsupported(int const pages, std::string op) {
|
|
1249
|
+
if (pages > 1) {
|
|
1250
|
+
throw vips::VError(op + " is not supported for multi-page images");
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1224
1254
|
/*
|
|
1225
1255
|
Calculate the angle of rotation and need-to-flip for the given Exif orientation
|
|
1226
1256
|
By default, returns zero, i.e. no rotation.
|
|
@@ -1311,15 +1341,15 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1311
1341
|
// Canvas option
|
|
1312
1342
|
std::string canvas = sharp::AttrAsStr(options, "canvas");
|
|
1313
1343
|
if (canvas == "crop") {
|
|
1314
|
-
baton->canvas = Canvas::CROP;
|
|
1344
|
+
baton->canvas = sharp::Canvas::CROP;
|
|
1315
1345
|
} else if (canvas == "embed") {
|
|
1316
|
-
baton->canvas = Canvas::EMBED;
|
|
1346
|
+
baton->canvas = sharp::Canvas::EMBED;
|
|
1317
1347
|
} else if (canvas == "max") {
|
|
1318
|
-
baton->canvas = Canvas::MAX;
|
|
1348
|
+
baton->canvas = sharp::Canvas::MAX;
|
|
1319
1349
|
} else if (canvas == "min") {
|
|
1320
|
-
baton->canvas = Canvas::MIN;
|
|
1350
|
+
baton->canvas = sharp::Canvas::MIN;
|
|
1321
1351
|
} else if (canvas == "ignore_aspect") {
|
|
1322
|
-
baton->canvas = Canvas::IGNORE_ASPECT;
|
|
1352
|
+
baton->canvas = sharp::Canvas::IGNORE_ASPECT;
|
|
1323
1353
|
}
|
|
1324
1354
|
// Tint chroma
|
|
1325
1355
|
baton->tintA = sharp::AttrAsDouble(options, "tintA");
|
|
@@ -1342,6 +1372,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1342
1372
|
}
|
|
1343
1373
|
// Resize options
|
|
1344
1374
|
baton->withoutEnlargement = sharp::AttrAsBool(options, "withoutEnlargement");
|
|
1375
|
+
baton->withoutReduction = sharp::AttrAsBool(options, "withoutReduction");
|
|
1345
1376
|
baton->position = sharp::AttrAsInt32(options, "position");
|
|
1346
1377
|
baton->resizeBackground = sharp::AttrAsVectorOfDouble(options, "resizeBackground");
|
|
1347
1378
|
baton->kernel = sharp::AttrAsStr(options, "kernel");
|
|
@@ -1399,8 +1430,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1399
1430
|
baton->affineIdy = sharp::AttrAsDouble(options, "affineIdy");
|
|
1400
1431
|
baton->affineOdx = sharp::AttrAsDouble(options, "affineOdx");
|
|
1401
1432
|
baton->affineOdy = sharp::AttrAsDouble(options, "affineOdy");
|
|
1402
|
-
baton->affineInterpolator =
|
|
1403
|
-
|
|
1433
|
+
baton->affineInterpolator = sharp::AttrAsStr(options, "affineInterpolator");
|
|
1404
1434
|
baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha");
|
|
1405
1435
|
baton->ensureAlpha = sharp::AttrAsDouble(options, "ensureAlpha");
|
|
1406
1436
|
if (options.Has("boolean")) {
|
|
@@ -1451,6 +1481,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1451
1481
|
std::string k = sharp::AttrAsStr(mdStrKeys, i);
|
|
1452
1482
|
baton->withMetadataStrs.insert(std::make_pair(k, sharp::AttrAsStr(mdStrs, k)));
|
|
1453
1483
|
}
|
|
1484
|
+
baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
|
|
1454
1485
|
// Format-specific
|
|
1455
1486
|
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
|
|
1456
1487
|
baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
|
|
@@ -1465,6 +1496,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1465
1496
|
baton->pngAdaptiveFiltering = sharp::AttrAsBool(options, "pngAdaptiveFiltering");
|
|
1466
1497
|
baton->pngPalette = sharp::AttrAsBool(options, "pngPalette");
|
|
1467
1498
|
baton->pngQuality = sharp::AttrAsUint32(options, "pngQuality");
|
|
1499
|
+
baton->pngEffort = sharp::AttrAsUint32(options, "pngEffort");
|
|
1468
1500
|
baton->pngBitdepth = sharp::AttrAsUint32(options, "pngBitdepth");
|
|
1469
1501
|
baton->pngDither = sharp::AttrAsDouble(options, "pngDither");
|
|
1470
1502
|
baton->jp2Quality = sharp::AttrAsUint32(options, "jp2Quality");
|
|
@@ -1477,7 +1509,10 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1477
1509
|
baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
|
|
1478
1510
|
baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
|
|
1479
1511
|
baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
|
|
1480
|
-
baton->
|
|
1512
|
+
baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
|
|
1513
|
+
baton->gifBitdepth = sharp::AttrAsUint32(options, "gifBitdepth");
|
|
1514
|
+
baton->gifEffort = sharp::AttrAsUint32(options, "gifEffort");
|
|
1515
|
+
baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
|
|
1481
1516
|
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
|
|
1482
1517
|
baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
|
|
1483
1518
|
baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
|
|
@@ -1486,6 +1521,9 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1486
1521
|
baton->tiffTileHeight = sharp::AttrAsUint32(options, "tiffTileHeight");
|
|
1487
1522
|
baton->tiffXres = sharp::AttrAsDouble(options, "tiffXres");
|
|
1488
1523
|
baton->tiffYres = sharp::AttrAsDouble(options, "tiffYres");
|
|
1524
|
+
if (baton->tiffXres == 1.0 && baton->tiffYres == 1.0 && baton->withMetadataDensity > 0) {
|
|
1525
|
+
baton->tiffXres = baton->tiffYres = baton->withMetadataDensity / 25.4;
|
|
1526
|
+
}
|
|
1489
1527
|
// tiff compression options
|
|
1490
1528
|
baton->tiffCompression = static_cast<VipsForeignTiffCompression>(
|
|
1491
1529
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_COMPRESSION,
|
|
@@ -1493,30 +1531,28 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1493
1531
|
baton->tiffPredictor = static_cast<VipsForeignTiffPredictor>(
|
|
1494
1532
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_PREDICTOR,
|
|
1495
1533
|
sharp::AttrAsStr(options, "tiffPredictor").data()));
|
|
1534
|
+
baton->tiffResolutionUnit = static_cast<VipsForeignTiffResunit>(
|
|
1535
|
+
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_RESUNIT,
|
|
1536
|
+
sharp::AttrAsStr(options, "tiffResolutionUnit").data()));
|
|
1537
|
+
|
|
1496
1538
|
baton->heifQuality = sharp::AttrAsUint32(options, "heifQuality");
|
|
1497
1539
|
baton->heifLossless = sharp::AttrAsBool(options, "heifLossless");
|
|
1498
1540
|
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
|
|
1499
1541
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
|
1500
1542
|
sharp::AttrAsStr(options, "heifCompression").data()));
|
|
1501
|
-
baton->
|
|
1543
|
+
baton->heifEffort = sharp::AttrAsUint32(options, "heifEffort");
|
|
1502
1544
|
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
|
|
1503
|
-
|
|
1504
1545
|
// Raw output
|
|
1505
1546
|
baton->rawDepth = static_cast<VipsBandFormat>(
|
|
1506
1547
|
vips_enum_from_nick(nullptr, VIPS_TYPE_BAND_FORMAT,
|
|
1507
1548
|
sharp::AttrAsStr(options, "rawDepth").data()));
|
|
1508
|
-
|
|
1509
|
-
// Animated output
|
|
1510
|
-
if (sharp::HasAttr(options, "pageHeight")) {
|
|
1511
|
-
baton->pageHeight = sharp::AttrAsUint32(options, "pageHeight");
|
|
1512
|
-
}
|
|
1549
|
+
// Animated output properties
|
|
1513
1550
|
if (sharp::HasAttr(options, "loop")) {
|
|
1514
1551
|
baton->loop = sharp::AttrAsUint32(options, "loop");
|
|
1515
1552
|
}
|
|
1516
1553
|
if (sharp::HasAttr(options, "delay")) {
|
|
1517
1554
|
baton->delay = sharp::AttrAsInt32Vector(options, "delay");
|
|
1518
1555
|
}
|
|
1519
|
-
|
|
1520
1556
|
// Tile output
|
|
1521
1557
|
baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
|
|
1522
1558
|
baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
|