sharp 0.33.5 → 0.34.0-rc.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/src/pipeline.cc CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  #include <algorithm>
5
5
  #include <cmath>
6
+ #include <filesystem>
6
7
  #include <map>
7
8
  #include <memory>
8
9
  #include <numeric>
@@ -20,17 +21,6 @@
20
21
  #include "operations.h"
21
22
  #include "pipeline.h"
22
23
 
23
- #ifdef _WIN32
24
- #define STAT64_STRUCT __stat64
25
- #define STAT64_FUNCTION _stat64
26
- #elif defined(_LARGEFILE64_SOURCE)
27
- #define STAT64_STRUCT stat64
28
- #define STAT64_FUNCTION stat64
29
- #else
30
- #define STAT64_STRUCT stat
31
- #define STAT64_FUNCTION stat
32
- #endif
33
-
34
24
  class PipelineWorker : public Napi::AsyncWorker {
35
25
  public:
36
26
  PipelineWorker(Napi::Function callback, PipelineBaton *baton,
@@ -52,7 +42,39 @@ class PipelineWorker : public Napi::AsyncWorker {
52
42
  // Open input
53
43
  vips::VImage image;
54
44
  sharp::ImageType inputImageType;
55
- std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
45
+ if (baton->join.empty()) {
46
+ std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
47
+ } else {
48
+ std::vector<VImage> images;
49
+ bool hasAlpha = false;
50
+ for (auto &join : baton->join) {
51
+ std::tie(image, inputImageType) = sharp::OpenInput(join);
52
+ image = sharp::EnsureColourspace(image, baton->colourspacePipeline);
53
+ images.push_back(image);
54
+ hasAlpha |= image.has_alpha();
55
+ }
56
+ if (hasAlpha) {
57
+ for (auto &image : images) {
58
+ if (!image.has_alpha()) {
59
+ image = sharp::EnsureAlpha(image, 1);
60
+ }
61
+ }
62
+ } else {
63
+ baton->input->joinBackground.pop_back();
64
+ }
65
+ inputImageType = sharp::ImageType::PNG;
66
+ image = VImage::arrayjoin(images, VImage::option()
67
+ ->set("across", baton->input->joinAcross)
68
+ ->set("shim", baton->input->joinShim)
69
+ ->set("background", baton->input->joinBackground)
70
+ ->set("halign", baton->input->joinHalign)
71
+ ->set("valign", baton->input->joinValign));
72
+ if (baton->input->joinAnimated) {
73
+ image = image.copy();
74
+ image.set(VIPS_META_N_PAGES, static_cast<int>(images.size()));
75
+ image.set(VIPS_META_PAGE_HEIGHT, static_cast<int>(image.height() / images.size()));
76
+ }
77
+ }
56
78
  VipsAccess access = baton->input->access;
57
79
  image = sharp::EnsureColourspace(image, baton->colourspacePipeline);
58
80
 
@@ -73,14 +95,14 @@ class PipelineWorker : public Napi::AsyncWorker {
73
95
  bool autoFlip = false;
74
96
  bool autoFlop = false;
75
97
 
76
- if (baton->useExifOrientation) {
98
+ if (baton->input->autoOrient) {
77
99
  // Rotate and flip image according to Exif orientation
78
100
  std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
79
101
  image = sharp::RemoveExifOrientation(image);
80
- } else {
81
- rotation = CalculateAngleRotation(baton->angle);
82
102
  }
83
103
 
104
+ rotation = CalculateAngleRotation(baton->angle);
105
+
84
106
  // Rotate pre-extract
85
107
  bool const shouldRotateBefore = baton->rotateBeforePreExtract &&
86
108
  (rotation != VIPS_ANGLE_D0 || autoRotation != VIPS_ANGLE_D0 ||
@@ -102,18 +124,14 @@ class PipelineWorker : public Napi::AsyncWorker {
102
124
  image = image.rot(autoRotation);
103
125
  autoRotation = VIPS_ANGLE_D0;
104
126
  }
105
- if (autoFlip) {
127
+ if (autoFlip != baton->flip) {
106
128
  image = image.flip(VIPS_DIRECTION_VERTICAL);
107
129
  autoFlip = false;
108
- } else if (baton->flip) {
109
- image = image.flip(VIPS_DIRECTION_VERTICAL);
110
130
  baton->flip = false;
111
131
  }
112
- if (autoFlop) {
132
+ if (autoFlop != baton->flop) {
113
133
  image = image.flip(VIPS_DIRECTION_HORIZONTAL);
114
134
  autoFlop = false;
115
- } else if (baton->flop) {
116
- image = image.flip(VIPS_DIRECTION_HORIZONTAL);
117
135
  baton->flop = false;
118
136
  }
119
137
  if (rotation != VIPS_ANGLE_D0) {
@@ -354,7 +372,7 @@ class PipelineWorker : public Napi::AsyncWorker {
354
372
  }
355
373
 
356
374
  // Flatten image to remove alpha channel
357
- if (baton->flatten && sharp::HasAlpha(image)) {
375
+ if (baton->flatten && image.has_alpha()) {
358
376
  image = sharp::Flatten(image, baton->flattenBackground);
359
377
  }
360
378
 
@@ -374,12 +392,12 @@ class PipelineWorker : public Napi::AsyncWorker {
374
392
  bool const shouldSharpen = baton->sharpenSigma != 0.0;
375
393
  bool const shouldComposite = !baton->composite.empty();
376
394
 
377
- if (shouldComposite && !sharp::HasAlpha(image)) {
395
+ if (shouldComposite && !image.has_alpha()) {
378
396
  image = sharp::EnsureAlpha(image, 1);
379
397
  }
380
398
 
381
399
  VipsBandFormat premultiplyFormat = image.format();
382
- bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
400
+ bool const shouldPremultiplyAlpha = image.has_alpha() &&
383
401
  (shouldResize || shouldBlur || shouldConv || shouldSharpen);
384
402
 
385
403
  if (shouldPremultiplyAlpha) {
@@ -406,11 +424,11 @@ class PipelineWorker : public Napi::AsyncWorker {
406
424
  image = image.rot(autoRotation);
407
425
  }
408
426
  // Mirror vertically (up-down) about the x-axis
409
- if (baton->flip || autoFlip) {
427
+ if (baton->flip != autoFlip) {
410
428
  image = image.flip(VIPS_DIRECTION_VERTICAL);
411
429
  }
412
430
  // Mirror horizontally (left-right) about the y-axis
413
- if (baton->flop || autoFlop) {
431
+ if (baton->flop != autoFlop) {
414
432
  image = image.flip(VIPS_DIRECTION_HORIZONTAL);
415
433
  }
416
434
  // Rotate post-extract 90-angle
@@ -641,6 +659,30 @@ class PipelineWorker : public Napi::AsyncWorker {
641
659
  composite->input->access = access;
642
660
  std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
643
661
  compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspacePipeline);
662
+
663
+ if (composite->input->autoOrient) {
664
+ // Respect EXIF Orientation
665
+ VipsAngle compositeAutoRotation = VIPS_ANGLE_D0;
666
+ bool compositeAutoFlip = false;
667
+ bool compositeAutoFlop = false;
668
+ std::tie(compositeAutoRotation, compositeAutoFlip, compositeAutoFlop) =
669
+ CalculateExifRotationAndFlip(sharp::ExifOrientation(compositeImage));
670
+
671
+ compositeImage = sharp::RemoveExifOrientation(compositeImage);
672
+ compositeImage = sharp::StaySequential(compositeImage,
673
+ compositeAutoRotation != VIPS_ANGLE_D0 || compositeAutoFlip);
674
+
675
+ if (compositeAutoRotation != VIPS_ANGLE_D0) {
676
+ compositeImage = compositeImage.rot(compositeAutoRotation);
677
+ }
678
+ if (compositeAutoFlip) {
679
+ compositeImage = compositeImage.flip(VIPS_DIRECTION_VERTICAL);
680
+ }
681
+ if (compositeAutoFlop) {
682
+ compositeImage = compositeImage.flip(VIPS_DIRECTION_HORIZONTAL);
683
+ }
684
+ }
685
+
644
686
  // Verify within current dimensions
645
687
  if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
646
688
  throw vips::VError("Image to composite must have same dimensions or smaller");
@@ -683,9 +725,7 @@ class PipelineWorker : public Napi::AsyncWorker {
683
725
  }
684
726
  // Ensure image to composite is sRGB with unpremultiplied alpha
685
727
  compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
686
- if (!sharp::HasAlpha(compositeImage)) {
687
- compositeImage = sharp::EnsureAlpha(compositeImage, 1);
688
- }
728
+ compositeImage = sharp::EnsureAlpha(compositeImage, 1);
689
729
  if (composite->premultiplied) compositeImage = compositeImage.unpremultiply();
690
730
  // Calculate position
691
731
  int left;
@@ -786,7 +826,7 @@ class PipelineWorker : public Napi::AsyncWorker {
786
826
  // Extract channel
787
827
  if (baton->extractChannel > -1) {
788
828
  if (baton->extractChannel >= image.bands()) {
789
- if (baton->extractChannel == 3 && sharp::HasAlpha(image)) {
829
+ if (baton->extractChannel == 3 && image.has_alpha()) {
790
830
  baton->extractChannel = image.bands() - 1;
791
831
  } else {
792
832
  (baton->err)
@@ -929,6 +969,7 @@ class PipelineWorker : public Napi::AsyncWorker {
929
969
  ->set("lossless", baton->webpLossless)
930
970
  ->set("near_lossless", baton->webpNearLossless)
931
971
  ->set("smart_subsample", baton->webpSmartSubsample)
972
+ ->set("smart_deblock", baton->webpSmartDeblock)
932
973
  ->set("preset", baton->webpPreset)
933
974
  ->set("effort", baton->webpEffort)
934
975
  ->set("min_size", baton->webpMinSize)
@@ -1009,7 +1050,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1009
1050
  } else if (baton->formatOut == "dz") {
1010
1051
  // Write DZ to buffer
1011
1052
  baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
1012
- if (!sharp::HasAlpha(image)) {
1053
+ if (!image.has_alpha()) {
1013
1054
  baton->tileBackground.pop_back();
1014
1055
  }
1015
1056
  image = sharp::StaySequential(image, baton->tileAngle != 0);
@@ -1058,6 +1099,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1058
1099
  // Unsupported output format
1059
1100
  (baton->err).append("Unsupported output format ");
1060
1101
  if (baton->formatOut == "input") {
1102
+ (baton->err).append("when trying to match input format of ");
1061
1103
  (baton->err).append(ImageTypeId(inputImageType));
1062
1104
  } else {
1063
1105
  (baton->err).append(baton->formatOut);
@@ -1136,6 +1178,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1136
1178
  ->set("lossless", baton->webpLossless)
1137
1179
  ->set("near_lossless", baton->webpNearLossless)
1138
1180
  ->set("smart_subsample", baton->webpSmartSubsample)
1181
+ ->set("smart_deblock", baton->webpSmartDeblock)
1139
1182
  ->set("preset", baton->webpPreset)
1140
1183
  ->set("effort", baton->webpEffort)
1141
1184
  ->set("min_size", baton->webpMinSize)
@@ -1211,7 +1254,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1211
1254
  if (isDzZip) {
1212
1255
  baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
1213
1256
  }
1214
- if (!sharp::HasAlpha(image)) {
1257
+ if (!image.has_alpha()) {
1215
1258
  baton->tileBackground.pop_back();
1216
1259
  }
1217
1260
  image = sharp::StaySequential(image, baton->tileAngle != 0);
@@ -1304,9 +1347,11 @@ class PipelineWorker : public Napi::AsyncWorker {
1304
1347
  Callback().Call(Receiver().Value(), { env.Null(), data, info });
1305
1348
  } else {
1306
1349
  // Add file size to info
1307
- struct STAT64_STRUCT st;
1308
- if (STAT64_FUNCTION(baton->fileOut.data(), &st) == 0) {
1309
- info.Set("size", static_cast<uint32_t>(st.st_size));
1350
+ if (baton->formatOut != "dz" || sharp::IsDzZip(baton->fileOut)) {
1351
+ try {
1352
+ uint32_t const size = static_cast<uint32_t>(std::filesystem::file_size(baton->fileOut));
1353
+ info.Set("size", size);
1354
+ } catch (...) {}
1310
1355
  }
1311
1356
  Callback().Call(Receiver().Value(), { env.Null(), info });
1312
1357
  }
@@ -1324,6 +1369,9 @@ class PipelineWorker : public Napi::AsyncWorker {
1324
1369
  for (sharp::InputDescriptor *input : baton->joinChannelIn) {
1325
1370
  delete input;
1326
1371
  }
1372
+ for (sharp::InputDescriptor *input : baton->join) {
1373
+ delete input;
1374
+ }
1327
1375
  delete baton;
1328
1376
 
1329
1377
  // Decrement processing task counter
@@ -1419,6 +1467,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1419
1467
  {"lossless", baton->webpLossless ? "true" : "false"},
1420
1468
  {"near_lossless", baton->webpNearLossless ? "true" : "false"},
1421
1469
  {"smart_subsample", baton->webpSmartSubsample ? "true" : "false"},
1470
+ {"smart_deblock", baton->webpSmartDeblock ? "true" : "false"},
1422
1471
  {"preset", vips_enum_nick(VIPS_TYPE_FOREIGN_WEBP_PRESET, baton->webpPreset)},
1423
1472
  {"min_size", baton->webpMinSize ? "true" : "false"},
1424
1473
  {"mixed", baton->webpMixed ? "true" : "false"},
@@ -1480,6 +1529,14 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1480
1529
 
1481
1530
  // Input
1482
1531
  baton->input = sharp::CreateInputDescriptor(options.Get("input").As<Napi::Object>());
1532
+ // Join images together
1533
+ if (sharp::HasAttr(options, "join")) {
1534
+ Napi::Array join = options.Get("join").As<Napi::Array>();
1535
+ for (unsigned int i = 0; i < join.Length(); i++) {
1536
+ baton->join.push_back(
1537
+ sharp::CreateInputDescriptor(join.Get(i).As<Napi::Object>()));
1538
+ }
1539
+ }
1483
1540
  // Extract image options
1484
1541
  baton->topOffsetPre = sharp::AttrAsInt32(options, "topOffsetPre");
1485
1542
  baton->leftOffsetPre = sharp::AttrAsInt32(options, "leftOffsetPre");
@@ -1572,7 +1629,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1572
1629
  baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
1573
1630
  baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight");
1574
1631
  baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope");
1575
- baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
1576
1632
  baton->angle = sharp::AttrAsInt32(options, "angle");
1577
1633
  baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
1578
1634
  baton->rotationBackground = sharp::AttrAsVectorOfDouble(options, "rotationBackground");
@@ -1676,6 +1732,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1676
1732
  baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
1677
1733
  baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
1678
1734
  baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
1735
+ baton->webpSmartDeblock = sharp::AttrAsBool(options, "webpSmartDeblock");
1679
1736
  baton->webpPreset = sharp::AttrAsEnum<VipsForeignWebpPreset>(options, "webpPreset", VIPS_TYPE_FOREIGN_WEBP_PRESET);
1680
1737
  baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
1681
1738
  baton->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
package/src/pipeline.h CHANGED
@@ -39,6 +39,7 @@ struct Composite {
39
39
 
40
40
  struct PipelineBaton {
41
41
  sharp::InputDescriptor *input;
42
+ std::vector<sharp::InputDescriptor *> join;
42
43
  std::string formatOut;
43
44
  std::string fileOut;
44
45
  void *bufferOut;
@@ -109,7 +110,6 @@ struct PipelineBaton {
109
110
  int claheWidth;
110
111
  int claheHeight;
111
112
  int claheMaxSlope;
112
- bool useExifOrientation;
113
113
  int angle;
114
114
  double rotationAngle;
115
115
  std::vector<double> rotationBackground;
@@ -157,6 +157,7 @@ struct PipelineBaton {
157
157
  bool webpNearLossless;
158
158
  bool webpLossless;
159
159
  bool webpSmartSubsample;
160
+ bool webpSmartDeblock;
160
161
  VipsForeignWebpPreset webpPreset;
161
162
  int webpEffort;
162
163
  bool webpMinSize;
@@ -281,7 +282,6 @@ struct PipelineBaton {
281
282
  claheWidth(0),
282
283
  claheHeight(0),
283
284
  claheMaxSlope(3),
284
- useExifOrientation(false),
285
285
  angle(0),
286
286
  rotationAngle(0.0),
287
287
  rotationBackground{ 0.0, 0.0, 0.0, 255.0 },
@@ -328,6 +328,7 @@ struct PipelineBaton {
328
328
  webpNearLossless(false),
329
329
  webpLossless(false),
330
330
  webpSmartSubsample(false),
331
+ webpSmartDeblock(false),
331
332
  webpPreset(VIPS_FOREIGN_WEBP_PRESET_DEFAULT),
332
333
  webpEffort(4),
333
334
  webpMinSize(false),
package/src/stats.cc CHANGED
@@ -58,7 +58,7 @@ class StatsWorker : public Napi::AsyncWorker {
58
58
  baton->channelStats.push_back(cStats);
59
59
  }
60
60
  // Image is not opaque when alpha layer is present and contains a non-mamixa value
61
- if (sharp::HasAlpha(image)) {
61
+ if (image.has_alpha()) {
62
62
  double const minAlpha = static_cast<double>(stats.getpoint(STAT_MIN_INDEX, bands).front());
63
63
  if (minAlpha != sharp::MaximumImageAlpha(image.interpretation())) {
64
64
  baton->isOpaque = false;
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"
122
+ "ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k", "jxl", "rad"
123
123
  }) {
124
124
  // Input
125
125
  const VipsObjectClass *oc = vips_class_find("VipsOperation", (f + "load").c_str());
@@ -233,10 +233,10 @@ Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {
233
233
  double maxColourDistance;
234
234
  try {
235
235
  // Premultiply and remove alpha
236
- if (sharp::HasAlpha(image1)) {
236
+ if (image1.has_alpha()) {
237
237
  image1 = image1.premultiply().extract_band(1, VImage::option()->set("n", image1.bands() - 1));
238
238
  }
239
- if (sharp::HasAlpha(image2)) {
239
+ if (image2.has_alpha()) {
240
240
  image2 = image2.premultiply().extract_band(1, VImage::option()->set("n", image2.bands() - 1));
241
241
  }
242
242
  // Calculate colour distance