@revizly/sharp 0.34.1-revizly9 → 0.34.4-revizly1

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/src/pipeline.cc CHANGED
@@ -92,31 +92,22 @@ class PipelineWorker : public Napi::AsyncWorker {
92
92
  // Calculate angle of rotation
93
93
  VipsAngle rotation = VIPS_ANGLE_D0;
94
94
  VipsAngle autoRotation = VIPS_ANGLE_D0;
95
- bool autoFlip = false;
96
95
  bool autoFlop = false;
97
96
 
98
97
  if (baton->input->autoOrient) {
99
98
  // Rotate and flip image according to Exif orientation
100
- std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
101
- image = sharp::RemoveExifOrientation(image);
99
+ std::tie(autoRotation, autoFlop) = CalculateExifRotationAndFlop(sharp::ExifOrientation(image));
102
100
  }
103
101
 
104
102
  rotation = CalculateAngleRotation(baton->angle);
105
103
 
106
- // Rotate pre-extract
107
- bool const shouldRotateBefore = baton->rotateBeforePreExtract &&
108
- (rotation != VIPS_ANGLE_D0 || autoRotation != VIPS_ANGLE_D0 ||
109
- autoFlip || baton->flip || autoFlop || baton->flop ||
110
- baton->rotationAngle != 0.0);
111
-
112
- if (shouldRotateBefore) {
113
- image = sharp::StaySequential(image,
114
- rotation != VIPS_ANGLE_D0 ||
115
- autoRotation != VIPS_ANGLE_D0 ||
116
- autoFlip ||
117
- baton->flip ||
118
- baton->rotationAngle != 0.0);
104
+ bool const shouldRotateBefore = baton->rotateBefore &&
105
+ (rotation != VIPS_ANGLE_D0 || baton->flip || baton->flop || baton->rotationAngle != 0.0);
106
+ bool const shouldOrientBefore = (shouldRotateBefore || baton->orientBefore) &&
107
+ (autoRotation != VIPS_ANGLE_D0 || autoFlop);
119
108
 
109
+ if (shouldOrientBefore) {
110
+ image = sharp::StaySequential(image, autoRotation != VIPS_ANGLE_D0);
120
111
  if (autoRotation != VIPS_ANGLE_D0) {
121
112
  if (autoRotation != VIPS_ANGLE_D180) {
122
113
  MultiPageUnsupported(nPages, "Rotate");
@@ -124,14 +115,20 @@ class PipelineWorker : public Napi::AsyncWorker {
124
115
  image = image.rot(autoRotation);
125
116
  autoRotation = VIPS_ANGLE_D0;
126
117
  }
127
- if (autoFlip != baton->flip) {
118
+ if (autoFlop) {
119
+ image = image.flip(VIPS_DIRECTION_HORIZONTAL);
120
+ autoFlop = false;
121
+ }
122
+ }
123
+
124
+ if (shouldRotateBefore) {
125
+ image = sharp::StaySequential(image, rotation != VIPS_ANGLE_D0 || baton->flip || baton->rotationAngle != 0.0);
126
+ if (baton->flip) {
128
127
  image = image.flip(VIPS_DIRECTION_VERTICAL);
129
- autoFlip = false;
130
128
  baton->flip = false;
131
129
  }
132
- if (autoFlop != baton->flop) {
130
+ if (baton->flop) {
133
131
  image = image.flip(VIPS_DIRECTION_HORIZONTAL);
134
- autoFlop = false;
135
132
  baton->flop = false;
136
133
  }
137
134
  if (rotation != VIPS_ANGLE_D0) {
@@ -146,6 +143,7 @@ class PipelineWorker : public Napi::AsyncWorker {
146
143
  std::vector<double> background;
147
144
  std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, false);
148
145
  image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)).copy_memory();
146
+ baton->rotationAngle = 0.0;
149
147
  }
150
148
  }
151
149
 
@@ -184,8 +182,7 @@ class PipelineWorker : public Napi::AsyncWorker {
184
182
  // When auto-rotating by 90 or 270 degrees, swap the target width and
185
183
  // height to ensure the behavior aligns with how it would have been if
186
184
  // the rotation had taken place *before* resizing.
187
- if (!baton->rotateBeforePreExtract &&
188
- (autoRotation == VIPS_ANGLE_D90 || autoRotation == VIPS_ANGLE_D270)) {
185
+ if (autoRotation == VIPS_ANGLE_D90 || autoRotation == VIPS_ANGLE_D270) {
189
186
  std::swap(targetResizeWidth, targetResizeHeight);
190
187
  }
191
188
 
@@ -207,7 +204,7 @@ class PipelineWorker : public Napi::AsyncWorker {
207
204
  // - input colourspace is not specified;
208
205
  bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) &&
209
206
  baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold < 0.0 &&
210
- baton->colourspacePipeline == VIPS_INTERPRETATION_LAST && !shouldRotateBefore;
207
+ baton->colourspacePipeline == VIPS_INTERPRETATION_LAST && !(shouldOrientBefore || shouldRotateBefore);
211
208
 
212
209
  if (shouldPreShrink) {
213
210
  // The common part of the shrink: the bit by which both axes must be shrunk
@@ -241,11 +238,7 @@ class PipelineWorker : public Napi::AsyncWorker {
241
238
  // factor for jpegload*, a double scale factor for webpload*,
242
239
  // pdfload* and svgload*
243
240
  if (jpegShrinkOnLoad > 1) {
244
- vips::VOption *option = VImage::option()
245
- ->set("access", access)
246
- ->set("shrink", jpegShrinkOnLoad)
247
- ->set("unlimited", baton->input->unlimited)
248
- ->set("fail_on", baton->input->failOn);
241
+ vips::VOption *option = GetOptionsForImageType(inputImageType, baton->input)->set("shrink", jpegShrinkOnLoad);
249
242
  if (baton->input->buffer != nullptr) {
250
243
  // Reload JPEG buffer
251
244
  VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
@@ -256,14 +249,8 @@ class PipelineWorker : public Napi::AsyncWorker {
256
249
  image = VImage::jpegload(const_cast<char*>(baton->input->file.data()), option);
257
250
  }
258
251
  } else if (scale != 1.0) {
259
- vips::VOption *option = VImage::option()
260
- ->set("access", access)
261
- ->set("scale", scale)
262
- ->set("fail_on", baton->input->failOn);
252
+ vips::VOption *option = GetOptionsForImageType(inputImageType, baton->input)->set("scale", scale);
263
253
  if (inputImageType == sharp::ImageType::WEBP) {
264
- option->set("n", baton->input->pages);
265
- option->set("page", baton->input->page);
266
-
267
254
  if (baton->input->buffer != nullptr) {
268
255
  // Reload WebP buffer
269
256
  VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
@@ -274,9 +261,6 @@ class PipelineWorker : public Napi::AsyncWorker {
274
261
  image = VImage::webpload(const_cast<char*>(baton->input->file.data()), option);
275
262
  }
276
263
  } else if (inputImageType == sharp::ImageType::SVG) {
277
- option->set("unlimited", baton->input->unlimited);
278
- option->set("dpi", baton->input->density);
279
-
280
264
  if (baton->input->buffer != nullptr) {
281
265
  // Reload SVG buffer
282
266
  VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
@@ -291,10 +275,6 @@ class PipelineWorker : public Napi::AsyncWorker {
291
275
  throw vips::VError("Input SVG image will exceed 32767x32767 pixel limit when scaled");
292
276
  }
293
277
  } else if (inputImageType == sharp::ImageType::PDF) {
294
- option->set("n", baton->input->pages);
295
- option->set("page", baton->input->page);
296
- option->set("dpi", baton->input->density);
297
-
298
278
  if (baton->input->buffer != nullptr) {
299
279
  // Reload PDF buffer
300
280
  VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
@@ -304,7 +284,6 @@ class PipelineWorker : public Napi::AsyncWorker {
304
284
  // Reload PDF file
305
285
  image = VImage::pdfload(const_cast<char*>(baton->input->file.data()), option);
306
286
  }
307
-
308
287
  sharp::SetDensity(image, baton->input->density);
309
288
  }
310
289
  } else {
@@ -312,6 +291,9 @@ class PipelineWorker : public Napi::AsyncWorker {
312
291
  throw vips::VError("Input SVG image exceeds 32767x32767 pixel limit");
313
292
  }
314
293
  }
294
+ if (baton->input->autoOrient) {
295
+ image = sharp::RemoveExifOrientation(image);
296
+ }
315
297
 
316
298
  // Any pre-shrinking may already have been done
317
299
  inputWidth = image.width();
@@ -414,7 +396,6 @@ class PipelineWorker : public Napi::AsyncWorker {
414
396
  image = sharp::StaySequential(image,
415
397
  autoRotation != VIPS_ANGLE_D0 ||
416
398
  baton->flip ||
417
- autoFlip ||
418
399
  rotation != VIPS_ANGLE_D0);
419
400
  // Auto-rotate post-extract
420
401
  if (autoRotation != VIPS_ANGLE_D0) {
@@ -424,7 +405,7 @@ class PipelineWorker : public Napi::AsyncWorker {
424
405
  image = image.rot(autoRotation);
425
406
  }
426
407
  // Mirror vertically (up-down) about the x-axis
427
- if (baton->flip != autoFlip) {
408
+ if (baton->flip) {
428
409
  image = image.flip(VIPS_DIRECTION_VERTICAL);
429
410
  }
430
411
  // Mirror horizontally (left-right) about the y-axis
@@ -531,7 +512,7 @@ class PipelineWorker : public Napi::AsyncWorker {
531
512
  }
532
513
 
533
514
  // Rotate post-extract non-90 angle
534
- if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) {
515
+ if (!baton->rotateBefore && baton->rotationAngle != 0.0) {
535
516
  MultiPageUnsupported(nPages, "Rotate");
536
517
  image = sharp::StaySequential(image);
537
518
  std::vector<double> background;
@@ -668,26 +649,20 @@ class PipelineWorker : public Napi::AsyncWorker {
668
649
  sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
669
650
  composite->input->access = access;
670
651
  std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
671
- compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspacePipeline);
672
652
 
673
653
  if (composite->input->autoOrient) {
674
654
  // Respect EXIF Orientation
675
655
  VipsAngle compositeAutoRotation = VIPS_ANGLE_D0;
676
- bool compositeAutoFlip = false;
677
656
  bool compositeAutoFlop = false;
678
- std::tie(compositeAutoRotation, compositeAutoFlip, compositeAutoFlop) =
679
- CalculateExifRotationAndFlip(sharp::ExifOrientation(compositeImage));
657
+ std::tie(compositeAutoRotation, compositeAutoFlop) =
658
+ CalculateExifRotationAndFlop(sharp::ExifOrientation(compositeImage));
680
659
 
681
660
  compositeImage = sharp::RemoveExifOrientation(compositeImage);
682
- compositeImage = sharp::StaySequential(compositeImage,
683
- compositeAutoRotation != VIPS_ANGLE_D0 || compositeAutoFlip);
661
+ compositeImage = sharp::StaySequential(compositeImage, compositeAutoRotation != VIPS_ANGLE_D0);
684
662
 
685
663
  if (compositeAutoRotation != VIPS_ANGLE_D0) {
686
664
  compositeImage = compositeImage.rot(compositeAutoRotation);
687
665
  }
688
- if (compositeAutoFlip) {
689
- compositeImage = compositeImage.flip(VIPS_DIRECTION_VERTICAL);
690
- }
691
666
  if (compositeAutoFlop) {
692
667
  compositeImage = compositeImage.flip(VIPS_DIRECTION_HORIZONTAL);
693
668
  }
@@ -733,8 +708,7 @@ class PipelineWorker : public Napi::AsyncWorker {
733
708
  // gravity was used for extract_area, set it back to its default value of 0
734
709
  composite->gravity = 0;
735
710
  }
736
- // Ensure image to composite is sRGB with unpremultiplied alpha
737
- compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
711
+ // Ensure image to composite is with unpremultiplied alpha
738
712
  compositeImage = sharp::EnsureAlpha(compositeImage, 1);
739
713
  if (composite->premultiplied) compositeImage = compositeImage.unpremultiply();
740
714
  // Calculate position
@@ -759,7 +733,12 @@ class PipelineWorker : public Napi::AsyncWorker {
759
733
  xs.push_back(left);
760
734
  ys.push_back(top);
761
735
  }
762
- image = VImage::composite(images, modes, VImage::option()->set("x", xs)->set("y", ys));
736
+ image = VImage::composite(images, modes, VImage::option()
737
+ ->set("compositing_space", baton->colourspacePipeline == VIPS_INTERPRETATION_LAST
738
+ ? VIPS_INTERPRETATION_sRGB
739
+ : baton->colourspacePipeline)
740
+ ->set("x", xs)
741
+ ->set("y", ys));
763
742
  image = sharp::RemoveGifPalette(image);
764
743
  }
765
744
 
@@ -891,7 +870,12 @@ class PipelineWorker : public Napi::AsyncWorker {
891
870
  image.set(s.first.data(), s.second.data());
892
871
  }
893
872
  }
894
-
873
+ // XMP buffer
874
+ if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_XMP) && !baton->withXmp.empty()) {
875
+ image = image.copy();
876
+ image.set(VIPS_META_XMP_NAME, nullptr,
877
+ const_cast<void*>(static_cast<void const*>(baton->withXmp.c_str())), baton->withXmp.size());
878
+ }
895
879
  // Number of channels used in output image
896
880
  baton->channels = image.bands();
897
881
  baton->width = image.width();
@@ -1002,6 +986,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1002
986
  ->set("interlace", baton->gifProgressive)
1003
987
  ->set("interframe_maxerror", baton->gifInterFrameMaxError)
1004
988
  ->set("interpalette_maxerror", baton->gifInterPaletteMaxError)
989
+ ->set("keep_duplicate_frames", baton->gifKeepDuplicateFrames)
1005
990
  ->set("dither", baton->gifDither)));
1006
991
  baton->bufferOut = static_cast<char*>(area->data);
1007
992
  baton->bufferOutLength = area->length;
@@ -1205,6 +1190,9 @@ class PipelineWorker : public Napi::AsyncWorker {
1205
1190
  ->set("effort", baton->gifEffort)
1206
1191
  ->set("reuse", baton->gifReuse)
1207
1192
  ->set("interlace", baton->gifProgressive)
1193
+ ->set("interframe_maxerror", baton->gifInterFrameMaxError)
1194
+ ->set("interpalette_maxerror", baton->gifInterPaletteMaxError)
1195
+ ->set("keep_duplicate_frames", baton->gifKeepDuplicateFrames)
1208
1196
  ->set("dither", baton->gifDither));
1209
1197
  baton->formatOut = "gif";
1210
1198
  } else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
@@ -1359,7 +1347,8 @@ class PipelineWorker : public Napi::AsyncWorker {
1359
1347
  // Add file size to info
1360
1348
  if (baton->formatOut != "dz" || sharp::IsDzZip(baton->fileOut)) {
1361
1349
  try {
1362
- uint32_t const size = static_cast<uint32_t>(std::filesystem::file_size(baton->fileOut));
1350
+ uint32_t const size = static_cast<uint32_t>(
1351
+ std::filesystem::file_size(std::filesystem::u8path(baton->fileOut)));
1363
1352
  info.Set("size", size);
1364
1353
  } catch (...) {}
1365
1354
  }
@@ -1405,21 +1394,20 @@ class PipelineWorker : public Napi::AsyncWorker {
1405
1394
  Calculate the angle of rotation and need-to-flip for the given Exif orientation
1406
1395
  By default, returns zero, i.e. no rotation.
1407
1396
  */
1408
- std::tuple<VipsAngle, bool, bool>
1409
- CalculateExifRotationAndFlip(int const exifOrientation) {
1397
+ std::tuple<VipsAngle, bool>
1398
+ CalculateExifRotationAndFlop(int const exifOrientation) {
1410
1399
  VipsAngle rotate = VIPS_ANGLE_D0;
1411
- bool flip = false;
1412
1400
  bool flop = false;
1413
1401
  switch (exifOrientation) {
1414
1402
  case 6: rotate = VIPS_ANGLE_D90; break;
1415
1403
  case 3: rotate = VIPS_ANGLE_D180; break;
1416
1404
  case 8: rotate = VIPS_ANGLE_D270; break;
1417
- case 2: flop = true; break; // flop 1
1418
- case 7: flip = true; rotate = VIPS_ANGLE_D90; break; // flip 6
1419
- case 4: flop = true; rotate = VIPS_ANGLE_D180; break; // flop 3
1420
- case 5: flip = true; rotate = VIPS_ANGLE_D270; break; // flip 8
1405
+ case 2: flop = true; break;
1406
+ case 7: flop = true; rotate = VIPS_ANGLE_D270; break;
1407
+ case 4: flop = true; rotate = VIPS_ANGLE_D180; break;
1408
+ case 5: flop = true; rotate = VIPS_ANGLE_D90; break;
1421
1409
  }
1422
- return std::make_tuple(rotate, flip, flop);
1410
+ return std::make_tuple(rotate, flop);
1423
1411
  }
1424
1412
 
1425
1413
  /*
@@ -1644,7 +1632,8 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1644
1632
  baton->angle = sharp::AttrAsInt32(options, "angle");
1645
1633
  baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
1646
1634
  baton->rotationBackground = sharp::AttrAsVectorOfDouble(options, "rotationBackground");
1647
- baton->rotateBeforePreExtract = sharp::AttrAsBool(options, "rotateBeforePreExtract");
1635
+ baton->rotateBefore = sharp::AttrAsBool(options, "rotateBefore");
1636
+ baton->orientBefore = sharp::AttrAsBool(options, "orientBefore");
1648
1637
  baton->flip = sharp::AttrAsBool(options, "flip");
1649
1638
  baton->flop = sharp::AttrAsBool(options, "flop");
1650
1639
  baton->extendTop = sharp::AttrAsInt32(options, "extendTop");
@@ -1716,6 +1705,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1716
1705
  }
1717
1706
  }
1718
1707
  baton->withExifMerge = sharp::AttrAsBool(options, "withExifMerge");
1708
+ baton->withXmp = sharp::AttrAsStr(options, "withXmp");
1719
1709
  baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
1720
1710
  baton->loop = sharp::AttrAsUint32(options, "loop");
1721
1711
  baton->delay = sharp::AttrAsInt32Vector(options, "delay");
@@ -1756,6 +1746,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1756
1746
  baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
1757
1747
  baton->gifInterFrameMaxError = sharp::AttrAsDouble(options, "gifInterFrameMaxError");
1758
1748
  baton->gifInterPaletteMaxError = sharp::AttrAsDouble(options, "gifInterPaletteMaxError");
1749
+ baton->gifKeepDuplicateFrames = sharp::AttrAsBool(options, "gifKeepDuplicateFrames");
1759
1750
  baton->gifReuse = sharp::AttrAsBool(options, "gifReuse");
1760
1751
  baton->gifProgressive = sharp::AttrAsBool(options, "gifProgressive");
1761
1752
  baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
package/src/pipeline.h CHANGED
@@ -115,7 +115,8 @@ struct PipelineBaton {
115
115
  int angle;
116
116
  double rotationAngle;
117
117
  std::vector<double> rotationBackground;
118
- bool rotateBeforePreExtract;
118
+ bool rotateBefore;
119
+ bool orientBefore;
119
120
  bool flip;
120
121
  bool flop;
121
122
  int extendTop;
@@ -169,6 +170,7 @@ struct PipelineBaton {
169
170
  double gifDither;
170
171
  double gifInterFrameMaxError;
171
172
  double gifInterPaletteMaxError;
173
+ bool gifKeepDuplicateFrames;
172
174
  bool gifReuse;
173
175
  bool gifProgressive;
174
176
  int tiffQuality;
@@ -201,6 +203,7 @@ struct PipelineBaton {
201
203
  std::string withIccProfile;
202
204
  std::unordered_map<std::string, std::string> withExif;
203
205
  bool withExifMerge;
206
+ std::string withXmp;
204
207
  int timeoutSeconds;
205
208
  std::vector<double> convKernel;
206
209
  int convKernelWidth;
@@ -342,6 +345,7 @@ struct PipelineBaton {
342
345
  gifDither(1.0),
343
346
  gifInterFrameMaxError(0.0),
344
347
  gifInterPaletteMaxError(3.0),
348
+ gifKeepDuplicateFrames(false),
345
349
  gifReuse(true),
346
350
  gifProgressive(false),
347
351
  tiffQuality(80),
package/src/stats.cc CHANGED
@@ -60,7 +60,7 @@ class StatsWorker : public Napi::AsyncWorker {
60
60
  // Image is not opaque when alpha layer is present and contains a non-mamixa value
61
61
  if (image.has_alpha()) {
62
62
  double const minAlpha = static_cast<double>(stats.getpoint(STAT_MIN_INDEX, bands).front());
63
- if (minAlpha != sharp::MaximumImageAlpha(image.interpretation())) {
63
+ if (minAlpha != vips_interpretation_max_alpha(image.interpretation())) {
64
64
  baton->isOpaque = false;
65
65
  }
66
66
  }
package/src/utilities.cc CHANGED
@@ -119,7 +119,7 @@ Napi::Value format(const Napi::CallbackInfo& info) {
119
119
  Napi::Object format = Napi::Object::New(env);
120
120
  for (std::string const f : {
121
121
  "jpeg", "png", "webp", "tiff", "magick", "openslide", "dz",
122
- "ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k", "jxl", "rad"
122
+ "ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k", "jxl", "rad", "dcraw"
123
123
  }) {
124
124
  // Input
125
125
  const VipsObjectClass *oc = vips_class_find("VipsOperation", (f + "load").c_str());