sharp 0.33.5 → 0.34.0-rc.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/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
@@ -591,6 +609,16 @@ class PipelineWorker : public Napi::AsyncWorker {
591
609
  image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale);
592
610
  }
593
611
 
612
+ // Dilate - must happen before blurring, due to the utility of dilating after thresholding
613
+ if (baton->dilateWidth != 0) {
614
+ image = sharp::Dilate(image, baton->dilateWidth);
615
+ }
616
+
617
+ // Erode - must happen before blurring, due to the utility of eroding after thresholding
618
+ if (baton->erodeWidth != 0) {
619
+ image = sharp::Erode(image, baton->erodeWidth);
620
+ }
621
+
594
622
  // Blur
595
623
  if (shouldBlur) {
596
624
  image = sharp::Blur(image, baton->blurSigma, baton->precision, baton->minAmpl);
@@ -641,6 +669,30 @@ class PipelineWorker : public Napi::AsyncWorker {
641
669
  composite->input->access = access;
642
670
  std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
643
671
  compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspacePipeline);
672
+
673
+ if (composite->input->autoOrient) {
674
+ // Respect EXIF Orientation
675
+ VipsAngle compositeAutoRotation = VIPS_ANGLE_D0;
676
+ bool compositeAutoFlip = false;
677
+ bool compositeAutoFlop = false;
678
+ std::tie(compositeAutoRotation, compositeAutoFlip, compositeAutoFlop) =
679
+ CalculateExifRotationAndFlip(sharp::ExifOrientation(compositeImage));
680
+
681
+ compositeImage = sharp::RemoveExifOrientation(compositeImage);
682
+ compositeImage = sharp::StaySequential(compositeImage,
683
+ compositeAutoRotation != VIPS_ANGLE_D0 || compositeAutoFlip);
684
+
685
+ if (compositeAutoRotation != VIPS_ANGLE_D0) {
686
+ compositeImage = compositeImage.rot(compositeAutoRotation);
687
+ }
688
+ if (compositeAutoFlip) {
689
+ compositeImage = compositeImage.flip(VIPS_DIRECTION_VERTICAL);
690
+ }
691
+ if (compositeAutoFlop) {
692
+ compositeImage = compositeImage.flip(VIPS_DIRECTION_HORIZONTAL);
693
+ }
694
+ }
695
+
644
696
  // Verify within current dimensions
645
697
  if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
646
698
  throw vips::VError("Image to composite must have same dimensions or smaller");
@@ -683,9 +735,7 @@ class PipelineWorker : public Napi::AsyncWorker {
683
735
  }
684
736
  // Ensure image to composite is sRGB with unpremultiplied alpha
685
737
  compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
686
- if (!sharp::HasAlpha(compositeImage)) {
687
- compositeImage = sharp::EnsureAlpha(compositeImage, 1);
688
- }
738
+ compositeImage = sharp::EnsureAlpha(compositeImage, 1);
689
739
  if (composite->premultiplied) compositeImage = compositeImage.unpremultiply();
690
740
  // Calculate position
691
741
  int left;
@@ -786,7 +836,7 @@ class PipelineWorker : public Napi::AsyncWorker {
786
836
  // Extract channel
787
837
  if (baton->extractChannel > -1) {
788
838
  if (baton->extractChannel >= image.bands()) {
789
- if (baton->extractChannel == 3 && sharp::HasAlpha(image)) {
839
+ if (baton->extractChannel == 3 && image.has_alpha()) {
790
840
  baton->extractChannel = image.bands() - 1;
791
841
  } else {
792
842
  (baton->err)
@@ -929,6 +979,7 @@ class PipelineWorker : public Napi::AsyncWorker {
929
979
  ->set("lossless", baton->webpLossless)
930
980
  ->set("near_lossless", baton->webpNearLossless)
931
981
  ->set("smart_subsample", baton->webpSmartSubsample)
982
+ ->set("smart_deblock", baton->webpSmartDeblock)
932
983
  ->set("preset", baton->webpPreset)
933
984
  ->set("effort", baton->webpEffort)
934
985
  ->set("min_size", baton->webpMinSize)
@@ -991,7 +1042,7 @@ class PipelineWorker : public Napi::AsyncWorker {
991
1042
  (baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
992
1043
  // Write HEIF to buffer
993
1044
  sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
994
- image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
1045
+ image = sharp::RemoveAnimationProperties(image);
995
1046
  VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
996
1047
  ->set("keep", baton->keepMetadata)
997
1048
  ->set("Q", baton->heifQuality)
@@ -1009,7 +1060,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1009
1060
  } else if (baton->formatOut == "dz") {
1010
1061
  // Write DZ to buffer
1011
1062
  baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
1012
- if (!sharp::HasAlpha(image)) {
1063
+ if (!image.has_alpha()) {
1013
1064
  baton->tileBackground.pop_back();
1014
1065
  }
1015
1066
  image = sharp::StaySequential(image, baton->tileAngle != 0);
@@ -1058,6 +1109,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1058
1109
  // Unsupported output format
1059
1110
  (baton->err).append("Unsupported output format ");
1060
1111
  if (baton->formatOut == "input") {
1112
+ (baton->err).append("when trying to match input format of ");
1061
1113
  (baton->err).append(ImageTypeId(inputImageType));
1062
1114
  } else {
1063
1115
  (baton->err).append(baton->formatOut);
@@ -1136,6 +1188,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1136
1188
  ->set("lossless", baton->webpLossless)
1137
1189
  ->set("near_lossless", baton->webpNearLossless)
1138
1190
  ->set("smart_subsample", baton->webpSmartSubsample)
1191
+ ->set("smart_deblock", baton->webpSmartDeblock)
1139
1192
  ->set("preset", baton->webpPreset)
1140
1193
  ->set("effort", baton->webpEffort)
1141
1194
  ->set("min_size", baton->webpMinSize)
@@ -1184,7 +1237,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1184
1237
  (willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
1185
1238
  // Write HEIF to file
1186
1239
  sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
1187
- image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
1240
+ image = sharp::RemoveAnimationProperties(image);
1188
1241
  image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
1189
1242
  ->set("keep", baton->keepMetadata)
1190
1243
  ->set("Q", baton->heifQuality)
@@ -1211,7 +1264,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1211
1264
  if (isDzZip) {
1212
1265
  baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
1213
1266
  }
1214
- if (!sharp::HasAlpha(image)) {
1267
+ if (!image.has_alpha()) {
1215
1268
  baton->tileBackground.pop_back();
1216
1269
  }
1217
1270
  image = sharp::StaySequential(image, baton->tileAngle != 0);
@@ -1304,9 +1357,11 @@ class PipelineWorker : public Napi::AsyncWorker {
1304
1357
  Callback().Call(Receiver().Value(), { env.Null(), data, info });
1305
1358
  } else {
1306
1359
  // 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));
1360
+ if (baton->formatOut != "dz" || sharp::IsDzZip(baton->fileOut)) {
1361
+ try {
1362
+ uint32_t const size = static_cast<uint32_t>(std::filesystem::file_size(baton->fileOut));
1363
+ info.Set("size", size);
1364
+ } catch (...) {}
1310
1365
  }
1311
1366
  Callback().Call(Receiver().Value(), { env.Null(), info });
1312
1367
  }
@@ -1324,6 +1379,9 @@ class PipelineWorker : public Napi::AsyncWorker {
1324
1379
  for (sharp::InputDescriptor *input : baton->joinChannelIn) {
1325
1380
  delete input;
1326
1381
  }
1382
+ for (sharp::InputDescriptor *input : baton->join) {
1383
+ delete input;
1384
+ }
1327
1385
  delete baton;
1328
1386
 
1329
1387
  // Decrement processing task counter
@@ -1419,6 +1477,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1419
1477
  {"lossless", baton->webpLossless ? "true" : "false"},
1420
1478
  {"near_lossless", baton->webpNearLossless ? "true" : "false"},
1421
1479
  {"smart_subsample", baton->webpSmartSubsample ? "true" : "false"},
1480
+ {"smart_deblock", baton->webpSmartDeblock ? "true" : "false"},
1422
1481
  {"preset", vips_enum_nick(VIPS_TYPE_FOREIGN_WEBP_PRESET, baton->webpPreset)},
1423
1482
  {"min_size", baton->webpMinSize ? "true" : "false"},
1424
1483
  {"mixed", baton->webpMixed ? "true" : "false"},
@@ -1480,6 +1539,14 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1480
1539
 
1481
1540
  // Input
1482
1541
  baton->input = sharp::CreateInputDescriptor(options.Get("input").As<Napi::Object>());
1542
+ // Join images together
1543
+ if (sharp::HasAttr(options, "join")) {
1544
+ Napi::Array join = options.Get("join").As<Napi::Array>();
1545
+ for (unsigned int i = 0; i < join.Length(); i++) {
1546
+ baton->join.push_back(
1547
+ sharp::CreateInputDescriptor(join.Get(i).As<Napi::Object>()));
1548
+ }
1549
+ }
1483
1550
  // Extract image options
1484
1551
  baton->topOffsetPre = sharp::AttrAsInt32(options, "topOffsetPre");
1485
1552
  baton->leftOffsetPre = sharp::AttrAsInt32(options, "leftOffsetPre");
@@ -1564,6 +1631,8 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1564
1631
  baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
1565
1632
  baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA");
1566
1633
  baton->linearB = sharp::AttrAsVectorOfDouble(options, "linearB");
1634
+ baton->dilateWidth = sharp::AttrAsUint32(options, "dilateWidth");
1635
+ baton->erodeWidth = sharp::AttrAsUint32(options, "erodeWidth");
1567
1636
  baton->greyscale = sharp::AttrAsBool(options, "greyscale");
1568
1637
  baton->normalise = sharp::AttrAsBool(options, "normalise");
1569
1638
  baton->normaliseLower = sharp::AttrAsUint32(options, "normaliseLower");
@@ -1572,7 +1641,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1572
1641
  baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
1573
1642
  baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight");
1574
1643
  baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope");
1575
- baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
1576
1644
  baton->angle = sharp::AttrAsInt32(options, "angle");
1577
1645
  baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
1578
1646
  baton->rotationBackground = sharp::AttrAsVectorOfDouble(options, "rotationBackground");
@@ -1649,6 +1717,8 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1649
1717
  }
1650
1718
  baton->withExifMerge = sharp::AttrAsBool(options, "withExifMerge");
1651
1719
  baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
1720
+ baton->loop = sharp::AttrAsUint32(options, "loop");
1721
+ baton->delay = sharp::AttrAsInt32Vector(options, "delay");
1652
1722
  // Format-specific
1653
1723
  baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
1654
1724
  baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
@@ -1676,6 +1746,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1676
1746
  baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
1677
1747
  baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
1678
1748
  baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
1749
+ baton->webpSmartDeblock = sharp::AttrAsBool(options, "webpSmartDeblock");
1679
1750
  baton->webpPreset = sharp::AttrAsEnum<VipsForeignWebpPreset>(options, "webpPreset", VIPS_TYPE_FOREIGN_WEBP_PRESET);
1680
1751
  baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
1681
1752
  baton->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
@@ -1717,13 +1788,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1717
1788
  baton->jxlEffort = sharp::AttrAsUint32(options, "jxlEffort");
1718
1789
  baton->jxlLossless = sharp::AttrAsBool(options, "jxlLossless");
1719
1790
  baton->rawDepth = sharp::AttrAsEnum<VipsBandFormat>(options, "rawDepth", VIPS_TYPE_BAND_FORMAT);
1720
- // Animated output properties
1721
- if (sharp::HasAttr(options, "loop")) {
1722
- baton->loop = sharp::AttrAsUint32(options, "loop");
1723
- }
1724
- if (sharp::HasAttr(options, "delay")) {
1725
- baton->delay = sharp::AttrAsInt32Vector(options, "delay");
1726
- }
1727
1791
  baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
1728
1792
  baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
1729
1793
  baton->tileAngle = sharp::AttrAsInt32(options, "tileAngle");
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;
@@ -100,6 +101,8 @@ struct PipelineBaton {
100
101
  int trimOffsetTop;
101
102
  std::vector<double> linearA;
102
103
  std::vector<double> linearB;
104
+ int dilateWidth;
105
+ int erodeWidth;
103
106
  double gamma;
104
107
  double gammaOut;
105
108
  bool greyscale;
@@ -109,7 +112,6 @@ struct PipelineBaton {
109
112
  int claheWidth;
110
113
  int claheHeight;
111
114
  int claheMaxSlope;
112
- bool useExifOrientation;
113
115
  int angle;
114
116
  double rotationAngle;
115
117
  std::vector<double> rotationBackground;
@@ -157,6 +159,7 @@ struct PipelineBaton {
157
159
  bool webpNearLossless;
158
160
  bool webpLossless;
159
161
  bool webpSmartSubsample;
162
+ bool webpSmartDeblock;
160
163
  VipsForeignWebpPreset webpPreset;
161
164
  int webpEffort;
162
165
  bool webpMinSize;
@@ -273,6 +276,8 @@ struct PipelineBaton {
273
276
  trimOffsetTop(0),
274
277
  linearA{},
275
278
  linearB{},
279
+ dilateWidth(0),
280
+ erodeWidth(0),
276
281
  gamma(0.0),
277
282
  greyscale(false),
278
283
  normalise(false),
@@ -281,7 +286,6 @@ struct PipelineBaton {
281
286
  claheWidth(0),
282
287
  claheHeight(0),
283
288
  claheMaxSlope(3),
284
- useExifOrientation(false),
285
289
  angle(0),
286
290
  rotationAngle(0.0),
287
291
  rotationBackground{ 0.0, 0.0, 0.0, 255.0 },
@@ -328,6 +332,7 @@ struct PipelineBaton {
328
332
  webpNearLossless(false),
329
333
  webpLossless(false),
330
334
  webpSmartSubsample(false),
335
+ webpSmartDeblock(false),
331
336
  webpPreset(VIPS_FOREIGN_WEBP_PRESET_DEFAULT),
332
337
  webpEffort(4),
333
338
  webpMinSize(false),
@@ -379,7 +384,7 @@ struct PipelineBaton {
379
384
  ensureAlpha(-1.0),
380
385
  colourspacePipeline(VIPS_INTERPRETATION_LAST),
381
386
  colourspace(VIPS_INTERPRETATION_LAST),
382
- loop(-1),
387
+ loop(1),
383
388
  tileSize(256),
384
389
  tileOverlap(0),
385
390
  tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
package/src/sharp.cc CHANGED
@@ -33,6 +33,7 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
33
33
  exports.Set("block", Napi::Function::New(env, block));
34
34
  exports.Set("_maxColourDistance", Napi::Function::New(env, _maxColourDistance));
35
35
  exports.Set("_isUsingJemalloc", Napi::Function::New(env, _isUsingJemalloc));
36
+ exports.Set("_isUsingX64V2", Napi::Function::New(env, _isUsingX64V2));
36
37
  exports.Set("stats", Napi::Function::New(env, stats));
37
38
  return exports;
38
39
  }
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
@@ -267,3 +267,20 @@ Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info) {
267
267
  return Napi::Boolean::New(env, false);
268
268
  }
269
269
  #endif
270
+
271
+ #if defined(__GNUC__) && defined(__x86_64__)
272
+ // Are SSE 4.2 intrinsics available at runtime?
273
+ Napi::Value _isUsingX64V2(const Napi::CallbackInfo& info) {
274
+ Napi::Env env = info.Env();
275
+ unsigned int eax, ebx, ecx, edx;
276
+ __asm__ __volatile__("cpuid"
277
+ : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
278
+ : "a"(1));
279
+ return Napi::Boolean::New(env, (ecx & 1U << 20) != 0);
280
+ }
281
+ #else
282
+ Napi::Value _isUsingX64V2(const Napi::CallbackInfo& info) {
283
+ Napi::Env env = info.Env();
284
+ return Napi::Boolean::New(env, false);
285
+ }
286
+ #endif
package/src/utilities.h CHANGED
@@ -15,5 +15,6 @@ Napi::Value format(const Napi::CallbackInfo& info);
15
15
  void block(const Napi::CallbackInfo& info);
16
16
  Napi::Value _maxColourDistance(const Napi::CallbackInfo& info);
17
17
  Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info);
18
+ Napi::Value _isUsingX64V2(const Napi::CallbackInfo& info);
18
19
 
19
20
  #endif // SRC_UTILITIES_H_