sharp 0.29.3 → 0.30.0
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 +2 -2
- package/binding.gyp +2 -0
- package/install/libvips.js +39 -8
- package/lib/constructor.js +10 -3
- package/lib/input.js +21 -4
- package/lib/libvips.js +16 -2
- package/lib/operation.js +14 -1
- package/lib/output.js +128 -69
- package/lib/platform.js +1 -1
- package/lib/resize.js +19 -0
- package/lib/sharp.js +2 -1
- package/lib/utility.js +20 -2
- package/package.json +27 -12
- package/src/common.cc +93 -19
- package/src/common.h +29 -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 +294 -259
- package/src/pipeline.h +15 -17
- 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,194 +123,182 @@ 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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
135
|
+
|
|
136
|
+
// Is there just one page? Shrink to inputHeight instead
|
|
137
|
+
if (nPages == 1) {
|
|
138
|
+
pageHeight = inputHeight;
|
|
117
139
|
}
|
|
118
140
|
|
|
119
|
-
// If
|
|
120
|
-
// Override target width and height if
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
141
|
+
// If withoutReduction is specified,
|
|
142
|
+
// Override target width and height if less than respective value from input file
|
|
143
|
+
if (baton->withoutReduction) {
|
|
144
|
+
if (baton->width < inputWidth) {
|
|
145
|
+
baton->width = inputWidth;
|
|
146
|
+
}
|
|
147
|
+
if (baton->height < inputHeight) {
|
|
148
|
+
baton->height = inputHeight;
|
|
149
|
+
}
|
|
128
150
|
}
|
|
129
151
|
|
|
130
152
|
// Scaling calculations
|
|
131
|
-
double
|
|
132
|
-
double
|
|
153
|
+
double hshrink;
|
|
154
|
+
double vshrink;
|
|
133
155
|
int targetResizeWidth = baton->width;
|
|
134
156
|
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
157
|
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
int yshrink = std::max(1, static_cast<int>(floor(yfactor)));
|
|
158
|
+
// Swap input output width and height when rotating by 90 or 270 degrees
|
|
159
|
+
bool swap = !baton->rotateBeforePreExtract && (rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270);
|
|
212
160
|
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
161
|
+
// Shrink to pageHeight, so we work for multi-page images
|
|
162
|
+
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
|
163
|
+
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
|
|
164
|
+
baton->canvas, swap, baton->withoutEnlargement);
|
|
216
165
|
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
int shrink_on_load = 1;
|
|
166
|
+
// The jpeg preload shrink.
|
|
167
|
+
int jpegShrinkOnLoad = 1;
|
|
220
168
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
169
|
+
// WebP, PDF, SVG scale
|
|
170
|
+
double scale = 1.0;
|
|
171
|
+
|
|
172
|
+
// Try to reload input using shrink-on-load for JPEG, WebP, SVG and PDF, when:
|
|
173
|
+
// - the width or height parameters are specified;
|
|
174
|
+
// - gamma correction doesn't need to be applied;
|
|
175
|
+
// - trimming or pre-resize extract isn't required;
|
|
176
|
+
// - input colourspace is not specified;
|
|
177
|
+
bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) &&
|
|
230
178
|
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
|
-
|
|
179
|
+
baton->colourspaceInput == VIPS_INTERPRETATION_LAST;
|
|
180
|
+
|
|
181
|
+
if (shouldPreShrink) {
|
|
182
|
+
// The common part of the shrink: the bit by which both axes must be shrunk
|
|
183
|
+
double shrink = std::min(hshrink, vshrink);
|
|
184
|
+
|
|
185
|
+
if (inputImageType == sharp::ImageType::JPEG) {
|
|
186
|
+
// Leave at least a factor of two for the final resize step, when fastShrinkOnLoad: false
|
|
187
|
+
// for more consistent results and avoid occasional small image shifting
|
|
188
|
+
int factor = baton->fastShrinkOnLoad ? 1 : 2;
|
|
189
|
+
if (shrink >= 8 * factor) {
|
|
190
|
+
jpegShrinkOnLoad = 8;
|
|
191
|
+
} else if (shrink >= 4 * factor) {
|
|
192
|
+
jpegShrinkOnLoad = 4;
|
|
193
|
+
} else if (shrink >= 2 * factor) {
|
|
194
|
+
jpegShrinkOnLoad = 2;
|
|
195
|
+
}
|
|
196
|
+
} else if (inputImageType == sharp::ImageType::WEBP ||
|
|
197
|
+
inputImageType == sharp::ImageType::SVG ||
|
|
198
|
+
inputImageType == sharp::ImageType::PDF) {
|
|
199
|
+
scale = 1.0 / shrink;
|
|
246
200
|
}
|
|
247
201
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
if (shrink_on_load > 1) {
|
|
255
|
-
// Reload input using shrink-on-load
|
|
202
|
+
|
|
203
|
+
// Reload input using shrink-on-load, it'll be an integer shrink
|
|
204
|
+
// factor for jpegload*, a double scale factor for webpload*,
|
|
205
|
+
// pdfload* and svgload*
|
|
206
|
+
if (jpegShrinkOnLoad > 1) {
|
|
256
207
|
vips::VOption *option = VImage::option()
|
|
257
208
|
->set("access", baton->input->access)
|
|
258
|
-
->set("shrink",
|
|
209
|
+
->set("shrink", jpegShrinkOnLoad)
|
|
259
210
|
->set("fail", baton->input->failOnError);
|
|
260
211
|
if (baton->input->buffer != nullptr) {
|
|
212
|
+
// Reload JPEG buffer
|
|
261
213
|
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
|
-
}
|
|
214
|
+
image = VImage::jpegload_buffer(blob, option);
|
|
269
215
|
vips_area_unref(reinterpret_cast<VipsArea*>(blob));
|
|
270
216
|
} else {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
217
|
+
// Reload JPEG file
|
|
218
|
+
image = VImage::jpegload(const_cast<char*>(baton->input->file.data()), option);
|
|
219
|
+
}
|
|
220
|
+
} else if (scale != 1.0) {
|
|
221
|
+
vips::VOption *option = VImage::option()
|
|
222
|
+
->set("access", baton->input->access)
|
|
223
|
+
->set("scale", scale)
|
|
224
|
+
->set("fail", baton->input->failOnError);
|
|
225
|
+
if (inputImageType == sharp::ImageType::WEBP) {
|
|
226
|
+
option->set("n", baton->input->pages);
|
|
227
|
+
option->set("page", baton->input->page);
|
|
228
|
+
|
|
229
|
+
if (baton->input->buffer != nullptr) {
|
|
230
|
+
// Reload WebP buffer
|
|
231
|
+
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
|
232
|
+
image = VImage::webpload_buffer(blob, option);
|
|
233
|
+
vips_area_unref(reinterpret_cast<VipsArea*>(blob));
|
|
274
234
|
} else {
|
|
275
235
|
// Reload WebP file
|
|
276
236
|
image = VImage::webpload(const_cast<char*>(baton->input->file.data()), option);
|
|
277
237
|
}
|
|
238
|
+
} else if (inputImageType == sharp::ImageType::SVG) {
|
|
239
|
+
option->set("unlimited", baton->input->unlimited);
|
|
240
|
+
option->set("dpi", baton->input->density);
|
|
241
|
+
|
|
242
|
+
if (baton->input->buffer != nullptr) {
|
|
243
|
+
// Reload SVG buffer
|
|
244
|
+
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
|
245
|
+
image = VImage::svgload_buffer(blob, option);
|
|
246
|
+
vips_area_unref(reinterpret_cast<VipsArea*>(blob));
|
|
247
|
+
} else {
|
|
248
|
+
// Reload SVG file
|
|
249
|
+
image = VImage::svgload(const_cast<char*>(baton->input->file.data()), option);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
sharp::SetDensity(image, baton->input->density);
|
|
253
|
+
} else if (inputImageType == sharp::ImageType::PDF) {
|
|
254
|
+
option->set("n", baton->input->pages);
|
|
255
|
+
option->set("page", baton->input->page);
|
|
256
|
+
option->set("dpi", baton->input->density);
|
|
257
|
+
|
|
258
|
+
if (baton->input->buffer != nullptr) {
|
|
259
|
+
// Reload PDF buffer
|
|
260
|
+
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
|
261
|
+
image = VImage::pdfload_buffer(blob, option);
|
|
262
|
+
vips_area_unref(reinterpret_cast<VipsArea*>(blob));
|
|
263
|
+
} else {
|
|
264
|
+
// Reload PDF file
|
|
265
|
+
image = VImage::pdfload(const_cast<char*>(baton->input->file.data()), option);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
sharp::SetDensity(image, baton->input->density);
|
|
278
269
|
}
|
|
279
|
-
// Recalculate integral shrink and double residual
|
|
280
|
-
int const shrunkOnLoadWidth = image.width();
|
|
281
|
-
int const shrunkOnLoadHeight = image.height();
|
|
282
|
-
if (!baton->rotateBeforePreExtract &&
|
|
283
|
-
(rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270)) {
|
|
284
|
-
// Swap when rotating by 90 or 270 degrees
|
|
285
|
-
xfactor = static_cast<double>(shrunkOnLoadWidth) / static_cast<double>(targetResizeHeight);
|
|
286
|
-
yfactor = static_cast<double>(shrunkOnLoadHeight) / static_cast<double>(targetResizeWidth);
|
|
287
|
-
} else {
|
|
288
|
-
xfactor = static_cast<double>(shrunkOnLoadWidth) / static_cast<double>(targetResizeWidth);
|
|
289
|
-
yfactor = static_cast<double>(shrunkOnLoadHeight) / static_cast<double>(targetResizeHeight);
|
|
290
|
-
}
|
|
291
270
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
271
|
+
|
|
272
|
+
// Any pre-shrinking may already have been done
|
|
273
|
+
int thumbWidth = image.width();
|
|
274
|
+
int thumbHeight = image.height();
|
|
275
|
+
|
|
276
|
+
// After pre-shrink, but before the main shrink stage
|
|
277
|
+
// Reuse the initial pageHeight if we didn't pre-shrink
|
|
278
|
+
int preshrunkPageHeight = shouldPreShrink ? sharp::GetPageHeight(image) : pageHeight;
|
|
279
|
+
|
|
280
|
+
if (baton->fastShrinkOnLoad && jpegShrinkOnLoad > 1) {
|
|
281
|
+
// JPEG shrink-on-load rounds the output dimensions down, which
|
|
282
|
+
// may cause incorrect dimensions when fastShrinkOnLoad is enabled
|
|
283
|
+
// Just recalculate vshrink / hshrink on the main image instead of
|
|
284
|
+
// the pre-shrunk image when this is the case
|
|
285
|
+
hshrink = static_cast<double>(thumbWidth) / (static_cast<double>(inputWidth) / hshrink);
|
|
286
|
+
vshrink = static_cast<double>(preshrunkPageHeight) / (static_cast<double>(pageHeight) / vshrink);
|
|
287
|
+
} else {
|
|
288
|
+
// Shrink to preshrunkPageHeight, so we work for multi-page images
|
|
289
|
+
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
|
290
|
+
thumbWidth, preshrunkPageHeight, targetResizeWidth, targetResizeHeight,
|
|
291
|
+
baton->canvas, swap, baton->withoutEnlargement);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
int targetHeight = static_cast<int>(std::rint(static_cast<double>(preshrunkPageHeight) / vshrink));
|
|
295
|
+
int targetPageHeight = targetHeight;
|
|
296
|
+
|
|
297
|
+
// In toilet-roll mode, we must adjust vshrink so that we exactly hit
|
|
298
|
+
// preshrunkPageHeight or we'll have pixels straddling pixel boundaries
|
|
299
|
+
if (thumbHeight > preshrunkPageHeight) {
|
|
300
|
+
targetHeight *= nPages;
|
|
301
|
+
vshrink = static_cast<double>(thumbHeight) / targetHeight;
|
|
295
302
|
}
|
|
296
303
|
|
|
297
304
|
// Ensure we're using a device-independent colour space
|
|
@@ -345,7 +352,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
345
352
|
image = image.colourspace(VIPS_INTERPRETATION_B_W);
|
|
346
353
|
}
|
|
347
354
|
|
|
348
|
-
bool const shouldResize =
|
|
355
|
+
bool const shouldResize = hshrink != 1.0 || vshrink != 1.0;
|
|
349
356
|
bool const shouldBlur = baton->blurSigma != 0.0;
|
|
350
357
|
bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0;
|
|
351
358
|
bool const shouldSharpen = baton->sharpenSigma != 0.0;
|
|
@@ -379,39 +386,33 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
379
386
|
) {
|
|
380
387
|
throw vips::VError("Unknown kernel");
|
|
381
388
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
xfactor = 2 * image.width();
|
|
385
|
-
if (baton->canvas != Canvas::EMBED) {
|
|
386
|
-
baton->width = 1;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
if (image.height() / yfactor < 0.5) {
|
|
390
|
-
yfactor = 2 * image.height();
|
|
391
|
-
if (baton->canvas != Canvas::EMBED) {
|
|
392
|
-
baton->height = 1;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
image = image.resize(1.0 / xfactor, VImage::option()
|
|
396
|
-
->set("vscale", 1.0 / yfactor)
|
|
389
|
+
image = image.resize(1.0 / hshrink, VImage::option()
|
|
390
|
+
->set("vscale", 1.0 / vshrink)
|
|
397
391
|
->set("kernel", kernel));
|
|
398
392
|
}
|
|
399
393
|
|
|
400
394
|
// Rotate post-extract 90-angle
|
|
401
|
-
if (!baton->rotateBeforePreExtract &&
|
|
402
|
-
|
|
403
|
-
|
|
395
|
+
if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) {
|
|
396
|
+
image = image.rot(rotation);
|
|
397
|
+
if (flip) {
|
|
398
|
+
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
399
|
+
flip = FALSE;
|
|
400
|
+
}
|
|
401
|
+
if (flop) {
|
|
402
|
+
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
403
|
+
flop = FALSE;
|
|
404
|
+
}
|
|
405
|
+
image = sharp::RemoveExifOrientation(image);
|
|
404
406
|
}
|
|
405
407
|
|
|
406
|
-
|
|
407
408
|
// Flip (mirror about Y axis)
|
|
408
|
-
if (baton->flip) {
|
|
409
|
+
if (baton->flip || flip) {
|
|
409
410
|
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
410
411
|
image = sharp::RemoveExifOrientation(image);
|
|
411
412
|
}
|
|
412
413
|
|
|
413
414
|
// Flop (mirror about X axis)
|
|
414
|
-
if (baton->flop) {
|
|
415
|
+
if (baton->flop || flop) {
|
|
415
416
|
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
416
417
|
image = sharp::RemoveExifOrientation(image);
|
|
417
418
|
}
|
|
@@ -429,52 +430,68 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
429
430
|
image = image.copy(VImage::option()->set("interpretation", baton->colourspace));
|
|
430
431
|
}
|
|
431
432
|
|
|
433
|
+
inputWidth = image.width();
|
|
434
|
+
inputHeight = nPages > 1 ? targetPageHeight : image.height();
|
|
435
|
+
|
|
436
|
+
// Resolve dimensions
|
|
437
|
+
if (baton->width <= 0) {
|
|
438
|
+
baton->width = inputWidth;
|
|
439
|
+
}
|
|
440
|
+
if (baton->height <= 0) {
|
|
441
|
+
baton->height = inputHeight;
|
|
442
|
+
}
|
|
443
|
+
|
|
432
444
|
// Crop/embed
|
|
433
|
-
if (
|
|
434
|
-
if (baton->canvas == Canvas::EMBED) {
|
|
445
|
+
if (inputWidth != baton->width || inputHeight != baton->height) {
|
|
446
|
+
if (baton->canvas == sharp::Canvas::EMBED) {
|
|
435
447
|
std::vector<double> background;
|
|
436
448
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->resizeBackground, shouldPremultiplyAlpha);
|
|
437
449
|
|
|
438
450
|
// Embed
|
|
439
451
|
|
|
440
|
-
// Calculate where to position the
|
|
452
|
+
// Calculate where to position the embedded image if gravity specified, else center.
|
|
441
453
|
int left;
|
|
442
454
|
int top;
|
|
443
455
|
|
|
444
|
-
left = static_cast<int>(round((baton->width -
|
|
445
|
-
top = static_cast<int>(round((baton->height -
|
|
456
|
+
left = static_cast<int>(round((baton->width - inputWidth) / 2));
|
|
457
|
+
top = static_cast<int>(round((baton->height - inputHeight) / 2));
|
|
446
458
|
|
|
447
|
-
int width = std::max(
|
|
448
|
-
int height = std::max(
|
|
459
|
+
int width = std::max(inputWidth, baton->width);
|
|
460
|
+
int height = std::max(inputHeight, baton->height);
|
|
449
461
|
std::tie(left, top) = sharp::CalculateEmbedPosition(
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
image =
|
|
453
|
-
|
|
454
|
-
|
|
462
|
+
inputWidth, inputHeight, baton->width, baton->height, baton->position);
|
|
463
|
+
|
|
464
|
+
image = nPages > 1
|
|
465
|
+
? sharp::EmbedMultiPage(image,
|
|
466
|
+
left, top, width, height, background, nPages, &targetPageHeight)
|
|
467
|
+
: image.embed(left, top, width, height, VImage::option()
|
|
468
|
+
->set("extend", VIPS_EXTEND_BACKGROUND)
|
|
469
|
+
->set("background", background));
|
|
470
|
+
} else if (baton->canvas == sharp::Canvas::CROP) {
|
|
471
|
+
if (baton->width > inputWidth) {
|
|
472
|
+
baton->width = inputWidth;
|
|
473
|
+
}
|
|
474
|
+
if (baton->height > inputHeight) {
|
|
475
|
+
baton->height = inputHeight;
|
|
476
|
+
}
|
|
455
477
|
|
|
456
|
-
|
|
457
|
-
baton->canvas != Canvas::IGNORE_ASPECT &&
|
|
458
|
-
(image.width() > baton->width || image.height() > baton->height)
|
|
459
|
-
) {
|
|
460
|
-
// Crop/max/min
|
|
478
|
+
// Crop
|
|
461
479
|
if (baton->position < 9) {
|
|
462
480
|
// Gravity-based crop
|
|
463
481
|
int left;
|
|
464
482
|
int top;
|
|
465
483
|
std::tie(left, top) = sharp::CalculateCrop(
|
|
466
|
-
|
|
467
|
-
int width = std::min(
|
|
468
|
-
int height = std::min(
|
|
469
|
-
|
|
484
|
+
inputWidth, inputHeight, baton->width, baton->height, baton->position);
|
|
485
|
+
int width = std::min(inputWidth, baton->width);
|
|
486
|
+
int height = std::min(inputHeight, baton->height);
|
|
487
|
+
|
|
488
|
+
image = nPages > 1
|
|
489
|
+
? sharp::CropMultiPage(image,
|
|
490
|
+
left, top, width, height, nPages, &targetPageHeight)
|
|
491
|
+
: image.extract_area(left, top, width, height);
|
|
470
492
|
} else {
|
|
471
493
|
// Attention-based or Entropy-based crop
|
|
472
|
-
|
|
473
|
-
baton->width = image.width();
|
|
474
|
-
}
|
|
475
|
-
if (baton->height > image.height()) {
|
|
476
|
-
baton->height = image.height();
|
|
477
|
-
}
|
|
494
|
+
MultiPageUnsupported(nPages, "Resize strategy");
|
|
478
495
|
image = image.tilecache(VImage::option()
|
|
479
496
|
->set("access", VIPS_ACCESS_RANDOM)
|
|
480
497
|
->set("threaded", TRUE));
|
|
@@ -489,6 +506,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
489
506
|
|
|
490
507
|
// Rotate post-extract non-90 angle
|
|
491
508
|
if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) {
|
|
509
|
+
MultiPageUnsupported(nPages, "Rotate");
|
|
492
510
|
std::vector<double> background;
|
|
493
511
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha);
|
|
494
512
|
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
|
@@ -496,12 +514,22 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
496
514
|
|
|
497
515
|
// Post extraction
|
|
498
516
|
if (baton->topOffsetPost != -1) {
|
|
499
|
-
|
|
500
|
-
|
|
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
|
+
}
|
|
501
528
|
}
|
|
502
529
|
|
|
503
530
|
// Affine transform
|
|
504
531
|
if (baton->affineMatrix.size() > 0) {
|
|
532
|
+
MultiPageUnsupported(nPages, "Affine");
|
|
505
533
|
std::vector<double> background;
|
|
506
534
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
|
|
507
535
|
image = image.affine(baton->affineMatrix, VImage::option()->set("background", background)
|
|
@@ -519,10 +547,13 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
519
547
|
|
|
520
548
|
// Embed
|
|
521
549
|
baton->width = image.width() + baton->extendLeft + baton->extendRight;
|
|
522
|
-
baton->height = image.height() + baton->extendTop + baton->extendBottom;
|
|
550
|
+
baton->height = (nPages > 1 ? targetPageHeight : image.height()) + baton->extendTop + baton->extendBottom;
|
|
523
551
|
|
|
524
|
-
image =
|
|
525
|
-
|
|
552
|
+
image = nPages > 1
|
|
553
|
+
? sharp::EmbedMultiPage(image,
|
|
554
|
+
baton->extendLeft, baton->extendTop, baton->width, baton->height, background, nPages, &targetPageHeight)
|
|
555
|
+
: image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height,
|
|
556
|
+
VImage::option()->set("extend", VIPS_EXTEND_BACKGROUND)->set("background", background));
|
|
526
557
|
}
|
|
527
558
|
// Median - must happen before blurring, due to the utility of blurring after thresholding
|
|
528
559
|
if (shouldApplyMedian) {
|
|
@@ -762,14 +793,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
762
793
|
baton->width = image.width();
|
|
763
794
|
baton->height = image.height();
|
|
764
795
|
|
|
765
|
-
bool const supportsGifOutput = vips_type_find("VipsOperation", "magicksave") != 0 &&
|
|
766
|
-
vips_type_find("VipsOperation", "magicksave_buffer") != 0;
|
|
767
|
-
|
|
768
796
|
image = sharp::SetAnimationProperties(
|
|
769
|
-
image,
|
|
770
|
-
baton->pageHeight,
|
|
771
|
-
baton->delay,
|
|
772
|
-
baton->loop);
|
|
797
|
+
image, nPages, targetPageHeight, baton->delay, baton->loop);
|
|
773
798
|
|
|
774
799
|
// Output
|
|
775
800
|
sharp::SetTimeout(image, baton->timeoutSeconds);
|
|
@@ -817,8 +842,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
817
842
|
vips_area_unref(area);
|
|
818
843
|
baton->formatOut = "jp2";
|
|
819
844
|
} else if (baton->formatOut == "png" || (baton->formatOut == "input" &&
|
|
820
|
-
(inputImageType == sharp::ImageType::PNG ||
|
|
821
|
-
inputImageType == sharp::ImageType::SVG))) {
|
|
845
|
+
(inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::SVG))) {
|
|
822
846
|
// Write PNG to buffer
|
|
823
847
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
|
824
848
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.pngsave_buffer(VImage::option()
|
|
@@ -828,7 +852,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
828
852
|
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
|
|
829
853
|
->set("palette", baton->pngPalette)
|
|
830
854
|
->set("Q", baton->pngQuality)
|
|
831
|
-
->set("
|
|
855
|
+
->set("effort", baton->pngEffort)
|
|
856
|
+
->set("bitdepth", sharp::Is16Bit(image.interpretation()) ? 16 : baton->pngBitdepth)
|
|
832
857
|
->set("dither", baton->pngDither)));
|
|
833
858
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
834
859
|
baton->bufferOutLength = area->length;
|
|
@@ -845,7 +870,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
845
870
|
->set("lossless", baton->webpLossless)
|
|
846
871
|
->set("near_lossless", baton->webpNearLossless)
|
|
847
872
|
->set("smart_subsample", baton->webpSmartSubsample)
|
|
848
|
-
->set("
|
|
873
|
+
->set("effort", baton->webpEffort)
|
|
849
874
|
->set("alpha_q", baton->webpAlphaQuality)));
|
|
850
875
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
851
876
|
baton->bufferOutLength = area->length;
|
|
@@ -853,14 +878,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
853
878
|
vips_area_unref(area);
|
|
854
879
|
baton->formatOut = "webp";
|
|
855
880
|
} else if (baton->formatOut == "gif" ||
|
|
856
|
-
(baton->formatOut == "input" && inputImageType == sharp::ImageType::GIF
|
|
881
|
+
(baton->formatOut == "input" && inputImageType == sharp::ImageType::GIF)) {
|
|
857
882
|
// Write GIF to buffer
|
|
858
883
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
|
859
|
-
VipsArea *area = reinterpret_cast<VipsArea*>(image.
|
|
884
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.gifsave_buffer(VImage::option()
|
|
860
885
|
->set("strip", !baton->withMetadata)
|
|
861
|
-
->set("
|
|
862
|
-
->set("
|
|
863
|
-
->set("
|
|
886
|
+
->set("bitdepth", baton->gifBitdepth)
|
|
887
|
+
->set("effort", baton->gifEffort)
|
|
888
|
+
->set("dither", baton->gifDither)));
|
|
864
889
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
865
890
|
baton->bufferOutLength = area->length;
|
|
866
891
|
area->free_fn = nullptr;
|
|
@@ -888,7 +913,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
888
913
|
->set("tile_height", baton->tiffTileHeight)
|
|
889
914
|
->set("tile_width", baton->tiffTileWidth)
|
|
890
915
|
->set("xres", baton->tiffXres)
|
|
891
|
-
->set("yres", baton->tiffYres)
|
|
916
|
+
->set("yres", baton->tiffYres)
|
|
917
|
+
->set("resunit", baton->tiffResolutionUnit)));
|
|
892
918
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
893
919
|
baton->bufferOutLength = area->length;
|
|
894
920
|
area->free_fn = nullptr;
|
|
@@ -902,7 +928,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
902
928
|
->set("strip", !baton->withMetadata)
|
|
903
929
|
->set("Q", baton->heifQuality)
|
|
904
930
|
->set("compression", baton->heifCompression)
|
|
905
|
-
->set("
|
|
931
|
+
->set("effort", baton->heifEffort)
|
|
906
932
|
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
|
907
933
|
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
908
934
|
->set("lossless", baton->heifLossless)));
|
|
@@ -987,8 +1013,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
987
1013
|
->set("tile_width", baton->jp2TileWidth));
|
|
988
1014
|
baton->formatOut = "jp2";
|
|
989
1015
|
} else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput &&
|
|
990
|
-
(inputImageType == sharp::ImageType::PNG ||
|
|
991
|
-
inputImageType == sharp::ImageType::SVG))) {
|
|
1016
|
+
(inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::SVG))) {
|
|
992
1017
|
// Write PNG to file
|
|
993
1018
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
|
994
1019
|
image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
@@ -998,7 +1023,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
998
1023
|
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
|
|
999
1024
|
->set("palette", baton->pngPalette)
|
|
1000
1025
|
->set("Q", baton->pngQuality)
|
|
1001
|
-
->set("bitdepth", baton->pngBitdepth)
|
|
1026
|
+
->set("bitdepth", sharp::Is16Bit(image.interpretation()) ? 16 : baton->pngBitdepth)
|
|
1027
|
+
->set("effort", baton->pngEffort)
|
|
1002
1028
|
->set("dither", baton->pngDither));
|
|
1003
1029
|
baton->formatOut = "png";
|
|
1004
1030
|
} else if (baton->formatOut == "webp" || (mightMatchInput && isWebp) ||
|
|
@@ -1011,18 +1037,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1011
1037
|
->set("lossless", baton->webpLossless)
|
|
1012
1038
|
->set("near_lossless", baton->webpNearLossless)
|
|
1013
1039
|
->set("smart_subsample", baton->webpSmartSubsample)
|
|
1014
|
-
->set("
|
|
1040
|
+
->set("effort", baton->webpEffort)
|
|
1015
1041
|
->set("alpha_q", baton->webpAlphaQuality));
|
|
1016
1042
|
baton->formatOut = "webp";
|
|
1017
1043
|
} else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
|
|
1018
|
-
(willMatchInput && inputImageType == sharp::ImageType::GIF
|
|
1044
|
+
(willMatchInput && inputImageType == sharp::ImageType::GIF)) {
|
|
1019
1045
|
// Write GIF to file
|
|
1020
1046
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
|
1021
|
-
image.
|
|
1047
|
+
image.gifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1022
1048
|
->set("strip", !baton->withMetadata)
|
|
1023
|
-
->set("
|
|
1024
|
-
->set("
|
|
1025
|
-
->set("
|
|
1049
|
+
->set("bitdepth", baton->gifBitdepth)
|
|
1050
|
+
->set("effort", baton->gifEffort)
|
|
1051
|
+
->set("dither", baton->gifDither));
|
|
1026
1052
|
baton->formatOut = "gif";
|
|
1027
1053
|
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
|
1028
1054
|
(willMatchInput && inputImageType == sharp::ImageType::TIFF)) {
|
|
@@ -1046,7 +1072,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1046
1072
|
->set("tile_height", baton->tiffTileHeight)
|
|
1047
1073
|
->set("tile_width", baton->tiffTileWidth)
|
|
1048
1074
|
->set("xres", baton->tiffXres)
|
|
1049
|
-
->set("yres", baton->tiffYres)
|
|
1075
|
+
->set("yres", baton->tiffYres)
|
|
1076
|
+
->set("resunit", baton->tiffResolutionUnit));
|
|
1050
1077
|
baton->formatOut = "tiff";
|
|
1051
1078
|
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
|
1052
1079
|
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
|
|
@@ -1056,7 +1083,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1056
1083
|
->set("strip", !baton->withMetadata)
|
|
1057
1084
|
->set("Q", baton->heifQuality)
|
|
1058
1085
|
->set("compression", baton->heifCompression)
|
|
1059
|
-
->set("
|
|
1086
|
+
->set("effort", baton->heifEffort)
|
|
1060
1087
|
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
|
1061
1088
|
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
1062
1089
|
->set("lossless", baton->heifLossless));
|
|
@@ -1081,7 +1108,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1081
1108
|
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
|
|
1082
1109
|
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
|
|
1083
1110
|
{"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
|
|
1084
|
-
{"
|
|
1111
|
+
{"effort", std::to_string(baton->webpEffort)}
|
|
1085
1112
|
};
|
|
1086
1113
|
suffix = AssembleSuffixString(".webp", options);
|
|
1087
1114
|
} else {
|
|
@@ -1230,6 +1257,12 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1230
1257
|
Napi::FunctionReference debuglog;
|
|
1231
1258
|
Napi::FunctionReference queueListener;
|
|
1232
1259
|
|
|
1260
|
+
void MultiPageUnsupported(int const pages, std::string op) {
|
|
1261
|
+
if (pages > 1) {
|
|
1262
|
+
throw vips::VError(op + " is not supported for multi-page images");
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1233
1266
|
/*
|
|
1234
1267
|
Calculate the angle of rotation and need-to-flip for the given Exif orientation
|
|
1235
1268
|
By default, returns zero, i.e. no rotation.
|
|
@@ -1320,15 +1353,15 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1320
1353
|
// Canvas option
|
|
1321
1354
|
std::string canvas = sharp::AttrAsStr(options, "canvas");
|
|
1322
1355
|
if (canvas == "crop") {
|
|
1323
|
-
baton->canvas = Canvas::CROP;
|
|
1356
|
+
baton->canvas = sharp::Canvas::CROP;
|
|
1324
1357
|
} else if (canvas == "embed") {
|
|
1325
|
-
baton->canvas = Canvas::EMBED;
|
|
1358
|
+
baton->canvas = sharp::Canvas::EMBED;
|
|
1326
1359
|
} else if (canvas == "max") {
|
|
1327
|
-
baton->canvas = Canvas::MAX;
|
|
1360
|
+
baton->canvas = sharp::Canvas::MAX;
|
|
1328
1361
|
} else if (canvas == "min") {
|
|
1329
|
-
baton->canvas = Canvas::MIN;
|
|
1362
|
+
baton->canvas = sharp::Canvas::MIN;
|
|
1330
1363
|
} else if (canvas == "ignore_aspect") {
|
|
1331
|
-
baton->canvas = Canvas::IGNORE_ASPECT;
|
|
1364
|
+
baton->canvas = sharp::Canvas::IGNORE_ASPECT;
|
|
1332
1365
|
}
|
|
1333
1366
|
// Tint chroma
|
|
1334
1367
|
baton->tintA = sharp::AttrAsDouble(options, "tintA");
|
|
@@ -1351,6 +1384,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1351
1384
|
}
|
|
1352
1385
|
// Resize options
|
|
1353
1386
|
baton->withoutEnlargement = sharp::AttrAsBool(options, "withoutEnlargement");
|
|
1387
|
+
baton->withoutReduction = sharp::AttrAsBool(options, "withoutReduction");
|
|
1354
1388
|
baton->position = sharp::AttrAsInt32(options, "position");
|
|
1355
1389
|
baton->resizeBackground = sharp::AttrAsVectorOfDouble(options, "resizeBackground");
|
|
1356
1390
|
baton->kernel = sharp::AttrAsStr(options, "kernel");
|
|
@@ -1409,7 +1443,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1409
1443
|
baton->affineOdx = sharp::AttrAsDouble(options, "affineOdx");
|
|
1410
1444
|
baton->affineOdy = sharp::AttrAsDouble(options, "affineOdy");
|
|
1411
1445
|
baton->affineInterpolator = vips::VInterpolate::new_from_name(sharp::AttrAsStr(options, "affineInterpolator").data());
|
|
1412
|
-
|
|
1413
1446
|
baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha");
|
|
1414
1447
|
baton->ensureAlpha = sharp::AttrAsDouble(options, "ensureAlpha");
|
|
1415
1448
|
if (options.Has("boolean")) {
|
|
@@ -1475,6 +1508,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1475
1508
|
baton->pngAdaptiveFiltering = sharp::AttrAsBool(options, "pngAdaptiveFiltering");
|
|
1476
1509
|
baton->pngPalette = sharp::AttrAsBool(options, "pngPalette");
|
|
1477
1510
|
baton->pngQuality = sharp::AttrAsUint32(options, "pngQuality");
|
|
1511
|
+
baton->pngEffort = sharp::AttrAsUint32(options, "pngEffort");
|
|
1478
1512
|
baton->pngBitdepth = sharp::AttrAsUint32(options, "pngBitdepth");
|
|
1479
1513
|
baton->pngDither = sharp::AttrAsDouble(options, "pngDither");
|
|
1480
1514
|
baton->jp2Quality = sharp::AttrAsUint32(options, "jp2Quality");
|
|
@@ -1487,7 +1521,10 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1487
1521
|
baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
|
|
1488
1522
|
baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
|
|
1489
1523
|
baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
|
|
1490
|
-
baton->
|
|
1524
|
+
baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
|
|
1525
|
+
baton->gifBitdepth = sharp::AttrAsUint32(options, "gifBitdepth");
|
|
1526
|
+
baton->gifEffort = sharp::AttrAsUint32(options, "gifEffort");
|
|
1527
|
+
baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
|
|
1491
1528
|
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
|
|
1492
1529
|
baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
|
|
1493
1530
|
baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
|
|
@@ -1506,30 +1543,28 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1506
1543
|
baton->tiffPredictor = static_cast<VipsForeignTiffPredictor>(
|
|
1507
1544
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_PREDICTOR,
|
|
1508
1545
|
sharp::AttrAsStr(options, "tiffPredictor").data()));
|
|
1546
|
+
baton->tiffResolutionUnit = static_cast<VipsForeignTiffResunit>(
|
|
1547
|
+
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_RESUNIT,
|
|
1548
|
+
sharp::AttrAsStr(options, "tiffResolutionUnit").data()));
|
|
1549
|
+
|
|
1509
1550
|
baton->heifQuality = sharp::AttrAsUint32(options, "heifQuality");
|
|
1510
1551
|
baton->heifLossless = sharp::AttrAsBool(options, "heifLossless");
|
|
1511
1552
|
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
|
|
1512
1553
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
|
1513
1554
|
sharp::AttrAsStr(options, "heifCompression").data()));
|
|
1514
|
-
baton->
|
|
1555
|
+
baton->heifEffort = sharp::AttrAsUint32(options, "heifEffort");
|
|
1515
1556
|
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
|
|
1516
|
-
|
|
1517
1557
|
// Raw output
|
|
1518
1558
|
baton->rawDepth = static_cast<VipsBandFormat>(
|
|
1519
1559
|
vips_enum_from_nick(nullptr, VIPS_TYPE_BAND_FORMAT,
|
|
1520
1560
|
sharp::AttrAsStr(options, "rawDepth").data()));
|
|
1521
|
-
|
|
1522
|
-
// Animated output
|
|
1523
|
-
if (sharp::HasAttr(options, "pageHeight")) {
|
|
1524
|
-
baton->pageHeight = sharp::AttrAsUint32(options, "pageHeight");
|
|
1525
|
-
}
|
|
1561
|
+
// Animated output properties
|
|
1526
1562
|
if (sharp::HasAttr(options, "loop")) {
|
|
1527
1563
|
baton->loop = sharp::AttrAsUint32(options, "loop");
|
|
1528
1564
|
}
|
|
1529
1565
|
if (sharp::HasAttr(options, "delay")) {
|
|
1530
1566
|
baton->delay = sharp::AttrAsInt32Vector(options, "delay");
|
|
1531
1567
|
}
|
|
1532
|
-
|
|
1533
1568
|
// Tile output
|
|
1534
1569
|
baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
|
|
1535
1570
|
baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
|