sharp 0.24.0 → 0.25.2

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
@@ -25,8 +25,7 @@
25
25
  #include <sys/stat.h>
26
26
 
27
27
  #include <vips/vips8>
28
- #include <node.h>
29
- #include <nan.h>
28
+ #include <napi.h>
30
29
 
31
30
  #include "common.h"
32
31
  #include "operations.h"
@@ -46,28 +45,18 @@
46
45
  #define STAT64_FUNCTION stat64
47
46
  #endif
48
47
 
49
- class PipelineWorker : public Nan::AsyncWorker {
48
+ class PipelineWorker : public Napi::AsyncWorker {
50
49
  public:
51
- PipelineWorker(
52
- Nan::Callback *callback, PipelineBaton *baton, Nan::Callback *debuglog, Nan::Callback *queueListener,
53
- std::vector<v8::Local<v8::Object>> const buffersToPersist) :
54
- Nan::AsyncWorker(callback, "sharp:PipelineWorker"),
55
- baton(baton), debuglog(debuglog), queueListener(queueListener),
56
- buffersToPersist(buffersToPersist) {
57
- // Protect Buffer objects from GC, keyed on index
58
- std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
59
- [this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
60
- SaveToPersistent(index, buffer);
61
- return index + 1;
62
- });
63
- }
50
+ PipelineWorker(Napi::Function callback, PipelineBaton *baton,
51
+ Napi::Function debuglog, Napi::Function queueListener) :
52
+ Napi::AsyncWorker(callback),
53
+ baton(baton),
54
+ debuglog(Napi::Persistent(debuglog)),
55
+ queueListener(Napi::Persistent(queueListener)) {}
64
56
  ~PipelineWorker() {}
65
57
 
66
58
  // libuv worker
67
59
  void Execute() {
68
- using sharp::HasAlpha;
69
- using sharp::ImageType;
70
-
71
60
  // Decrement queued task counter
72
61
  g_atomic_int_dec_and_test(&sharp::counterQueue);
73
62
  // Increment processing task counter
@@ -76,7 +65,7 @@ class PipelineWorker : public Nan::AsyncWorker {
76
65
  try {
77
66
  // Open input
78
67
  vips::VImage image;
79
- ImageType inputImageType;
68
+ sharp::ImageType inputImageType;
80
69
  std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
81
70
 
82
71
  // Calculate angle of rotation
@@ -236,7 +225,7 @@ class PipelineWorker : public Nan::AsyncWorker {
236
225
  }
237
226
  if (
238
227
  xshrink == yshrink && xshrink >= 2 * shrink_on_load_factor &&
239
- (inputImageType == ImageType::JPEG || inputImageType == ImageType::WEBP) &&
228
+ (inputImageType == sharp::ImageType::JPEG || inputImageType == sharp::ImageType::WEBP) &&
240
229
  baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0
241
230
  ) {
242
231
  if (xshrink >= 8 * shrink_on_load_factor) {
@@ -267,7 +256,7 @@ class PipelineWorker : public Nan::AsyncWorker {
267
256
  ->set("fail", baton->input->failOnError);
268
257
  if (baton->input->buffer != nullptr) {
269
258
  VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
270
- if (inputImageType == ImageType::JPEG) {
259
+ if (inputImageType == sharp::ImageType::JPEG) {
271
260
  // Reload JPEG buffer
272
261
  image = VImage::jpegload_buffer(blob, option);
273
262
  } else {
@@ -276,7 +265,7 @@ class PipelineWorker : public Nan::AsyncWorker {
276
265
  }
277
266
  vips_area_unref(reinterpret_cast<VipsArea*>(blob));
278
267
  } else {
279
- if (inputImageType == ImageType::JPEG) {
268
+ if (inputImageType == sharp::ImageType::JPEG) {
280
269
  // Reload JPEG file
281
270
  image = VImage::jpegload(const_cast<char*>(baton->input->file.data()), option);
282
271
  } else {
@@ -320,7 +309,7 @@ class PipelineWorker : public Nan::AsyncWorker {
320
309
  }
321
310
 
322
311
  // Flatten image to remove alpha channel
323
- if (baton->flatten && HasAlpha(image)) {
312
+ if (baton->flatten && sharp::HasAlpha(image)) {
324
313
  // Scale up 8-bit values to match 16-bit input image
325
314
  double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
326
315
  // Background colour
@@ -356,11 +345,11 @@ class PipelineWorker : public Nan::AsyncWorker {
356
345
  bool const shouldComposite = !baton->composite.empty();
357
346
  bool const shouldModulate = baton->brightness != 1.0 || baton->saturation != 1.0 || baton->hue != 0.0;
358
347
 
359
- if (shouldComposite && !HasAlpha(image)) {
348
+ if (shouldComposite && !sharp::HasAlpha(image)) {
360
349
  image = sharp::EnsureAlpha(image);
361
350
  }
362
351
 
363
- bool const shouldPremultiplyAlpha = HasAlpha(image) &&
352
+ bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
364
353
  (shouldResize || shouldBlur || shouldConv || shouldSharpen || shouldComposite);
365
354
 
366
355
  // Premultiply image alpha channel before all transformations to avoid
@@ -416,7 +405,7 @@ class PipelineWorker : public Nan::AsyncWorker {
416
405
  // Join additional color channels to the image
417
406
  if (baton->joinChannelIn.size() > 0) {
418
407
  VImage joinImage;
419
- ImageType joinImageType = ImageType::UNKNOWN;
408
+ sharp::ImageType joinImageType = sharp::ImageType::UNKNOWN;
420
409
 
421
410
  for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
422
411
  std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
@@ -548,7 +537,7 @@ class PipelineWorker : public Nan::AsyncWorker {
548
537
  if (shouldComposite) {
549
538
  for (Composite *composite : baton->composite) {
550
539
  VImage compositeImage;
551
- ImageType compositeImageType = ImageType::UNKNOWN;
540
+ sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
552
541
  std::tie(compositeImage, compositeImageType) = OpenInput(composite->input);
553
542
  // Verify within current dimensions
554
543
  if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
@@ -584,7 +573,7 @@ class PipelineWorker : public Nan::AsyncWorker {
584
573
  }
585
574
  // Ensure image to composite is sRGB with premultiplied alpha
586
575
  compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
587
- if (!HasAlpha(compositeImage)) {
576
+ if (!sharp::HasAlpha(compositeImage)) {
588
577
  compositeImage = sharp::EnsureAlpha(compositeImage);
589
578
  }
590
579
  if (!composite->premultiplied) compositeImage = compositeImage.premultiply();
@@ -638,7 +627,7 @@ class PipelineWorker : public Nan::AsyncWorker {
638
627
  // Apply bitwise boolean operation between images
639
628
  if (baton->boolean != nullptr) {
640
629
  VImage booleanImage;
641
- ImageType booleanImageType = ImageType::UNKNOWN;
630
+ sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
642
631
  std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
643
632
  image = sharp::Boolean(image, booleanImage, baton->booleanOp);
644
633
  }
@@ -703,9 +692,9 @@ class PipelineWorker : public Nan::AsyncWorker {
703
692
  // Output
704
693
  if (baton->fileOut.empty()) {
705
694
  // Buffer output
706
- if (baton->formatOut == "jpeg" || (baton->formatOut == "input" && inputImageType == ImageType::JPEG)) {
695
+ if (baton->formatOut == "jpeg" || (baton->formatOut == "input" && inputImageType == sharp::ImageType::JPEG)) {
707
696
  // Write JPEG to buffer
708
- sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
697
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
709
698
  VipsArea *area = VIPS_AREA(image.jpegsave_buffer(VImage::option()
710
699
  ->set("strip", !baton->withMetadata)
711
700
  ->set("Q", baton->jpegQuality)
@@ -727,9 +716,10 @@ class PipelineWorker : public Nan::AsyncWorker {
727
716
  baton->channels = std::min(baton->channels, 3);
728
717
  }
729
718
  } else if (baton->formatOut == "png" || (baton->formatOut == "input" &&
730
- (inputImageType == ImageType::PNG || inputImageType == ImageType::GIF || inputImageType == ImageType::SVG))) {
719
+ (inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::GIF ||
720
+ inputImageType == sharp::ImageType::SVG))) {
731
721
  // Write PNG to buffer
732
- sharp::AssertImageTypeDimensions(image, ImageType::PNG);
722
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
733
723
  VipsArea *area = VIPS_AREA(image.pngsave_buffer(VImage::option()
734
724
  ->set("strip", !baton->withMetadata)
735
725
  ->set("interlace", baton->pngProgressive)
@@ -744,9 +734,10 @@ class PipelineWorker : public Nan::AsyncWorker {
744
734
  area->free_fn = nullptr;
745
735
  vips_area_unref(area);
746
736
  baton->formatOut = "png";
747
- } else if (baton->formatOut == "webp" || (baton->formatOut == "input" && inputImageType == ImageType::WEBP)) {
737
+ } else if (baton->formatOut == "webp" ||
738
+ (baton->formatOut == "input" && inputImageType == sharp::ImageType::WEBP)) {
748
739
  // Write WEBP to buffer
749
- sharp::AssertImageTypeDimensions(image, ImageType::WEBP);
740
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::WEBP);
750
741
  VipsArea *area = VIPS_AREA(image.webpsave_buffer(VImage::option()
751
742
  ->set("strip", !baton->withMetadata)
752
743
  ->set("Q", baton->webpQuality)
@@ -760,10 +751,12 @@ class PipelineWorker : public Nan::AsyncWorker {
760
751
  area->free_fn = nullptr;
761
752
  vips_area_unref(area);
762
753
  baton->formatOut = "webp";
763
- } else if (baton->formatOut == "tiff" || (baton->formatOut == "input" && inputImageType == ImageType::TIFF)) {
754
+ } else if (baton->formatOut == "tiff" ||
755
+ (baton->formatOut == "input" && inputImageType == sharp::ImageType::TIFF)) {
764
756
  // Write TIFF to buffer
765
757
  if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
766
- sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
758
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
759
+ baton->channels = std::min(baton->channels, 3);
767
760
  }
768
761
  // Cast pixel values to float, if required
769
762
  if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
@@ -786,8 +779,8 @@ class PipelineWorker : public Nan::AsyncWorker {
786
779
  area->free_fn = nullptr;
787
780
  vips_area_unref(area);
788
781
  baton->formatOut = "tiff";
789
- baton->channels = std::min(baton->channels, 3);
790
- } else if (baton->formatOut == "heif" || (baton->formatOut == "input" && inputImageType == ImageType::HEIF)) {
782
+ } else if (baton->formatOut == "heif" ||
783
+ (baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
791
784
  // Write HEIF to buffer
792
785
  VipsArea *area = VIPS_AREA(image.heifsave_buffer(VImage::option()
793
786
  ->set("strip", !baton->withMetadata)
@@ -799,7 +792,8 @@ class PipelineWorker : public Nan::AsyncWorker {
799
792
  area->free_fn = nullptr;
800
793
  vips_area_unref(area);
801
794
  baton->formatOut = "heif";
802
- } else if (baton->formatOut == "raw" || (baton->formatOut == "input" && inputImageType == ImageType::RAW)) {
795
+ } else if (baton->formatOut == "raw" ||
796
+ (baton->formatOut == "input" && inputImageType == sharp::ImageType::RAW)) {
803
797
  // Write raw, uncompressed image data to buffer
804
798
  if (baton->greyscale || image.interpretation() == VIPS_INTERPRETATION_B_W) {
805
799
  // Extract first band for greyscale image
@@ -840,9 +834,9 @@ class PipelineWorker : public Nan::AsyncWorker {
840
834
  bool const mightMatchInput = baton->formatOut == "input";
841
835
  bool const willMatchInput = mightMatchInput && !(isJpeg || isPng || isWebp || isTiff || isDz || isDzZip || isV);
842
836
  if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) ||
843
- (willMatchInput && inputImageType == ImageType::JPEG)) {
837
+ (willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
844
838
  // Write JPEG to file
845
- sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
839
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
846
840
  image.jpegsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
847
841
  ->set("strip", !baton->withMetadata)
848
842
  ->set("Q", baton->jpegQuality)
@@ -856,9 +850,10 @@ class PipelineWorker : public Nan::AsyncWorker {
856
850
  baton->formatOut = "jpeg";
857
851
  baton->channels = std::min(baton->channels, 3);
858
852
  } else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput &&
859
- (inputImageType == ImageType::PNG || inputImageType == ImageType::GIF || inputImageType == ImageType::SVG))) {
853
+ (inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::GIF ||
854
+ inputImageType == sharp::ImageType::SVG))) {
860
855
  // Write PNG to file
861
- sharp::AssertImageTypeDimensions(image, ImageType::PNG);
856
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
862
857
  image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
863
858
  ->set("strip", !baton->withMetadata)
864
859
  ->set("interlace", baton->pngProgressive)
@@ -870,9 +865,9 @@ class PipelineWorker : public Nan::AsyncWorker {
870
865
  ->set("dither", baton->pngDither));
871
866
  baton->formatOut = "png";
872
867
  } else if (baton->formatOut == "webp" || (mightMatchInput && isWebp) ||
873
- (willMatchInput && inputImageType == ImageType::WEBP)) {
868
+ (willMatchInput && inputImageType == sharp::ImageType::WEBP)) {
874
869
  // Write WEBP to file
875
- sharp::AssertImageTypeDimensions(image, ImageType::WEBP);
870
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::WEBP);
876
871
  image.webpsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
877
872
  ->set("strip", !baton->withMetadata)
878
873
  ->set("Q", baton->webpQuality)
@@ -883,10 +878,11 @@ class PipelineWorker : public Nan::AsyncWorker {
883
878
  ->set("alpha_q", baton->webpAlphaQuality));
884
879
  baton->formatOut = "webp";
885
880
  } else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
886
- (willMatchInput && inputImageType == ImageType::TIFF)) {
881
+ (willMatchInput && inputImageType == sharp::ImageType::TIFF)) {
887
882
  // Write TIFF to file
888
883
  if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
889
- sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
884
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
885
+ baton->channels = std::min(baton->channels, 3);
890
886
  }
891
887
  image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
892
888
  ->set("strip", !baton->withMetadata)
@@ -901,9 +897,8 @@ class PipelineWorker : public Nan::AsyncWorker {
901
897
  ->set("xres", baton->tiffXres)
902
898
  ->set("yres", baton->tiffYres));
903
899
  baton->formatOut = "tiff";
904
- baton->channels = std::min(baton->channels, 3);
905
900
  } else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
906
- (willMatchInput && inputImageType == ImageType::HEIF)) {
901
+ (willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
907
902
  // Write HEIF to file
908
903
  if (sharp::IsAvif(baton->fileOut)) {
909
904
  baton->heifCompression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
@@ -938,9 +933,6 @@ class PipelineWorker : public Nan::AsyncWorker {
938
933
  };
939
934
  suffix = AssembleSuffixString(".webp", options);
940
935
  } else {
941
- std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_GOOGLE
942
- || baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY
943
- ? ".jpg" : ".jpeg";
944
936
  std::vector<std::pair<std::string, std::string>> options {
945
937
  {"Q", std::to_string(baton->jpegQuality)},
946
938
  {"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
@@ -951,10 +943,11 @@ class PipelineWorker : public Nan::AsyncWorker {
951
943
  {"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"},
952
944
  {"optimize_coding", baton->jpegOptimiseCoding ? "TRUE": "FALSE"}
953
945
  };
946
+ std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_DZ ? ".jpeg" : ".jpg";
954
947
  suffix = AssembleSuffixString(extname, options);
955
948
  }
956
949
  // Remove alpha channel from tile background if image does not contain an alpha channel
957
- if (!HasAlpha(image)) {
950
+ if (!sharp::HasAlpha(image)) {
958
951
  baton->tileBackground.pop_back();
959
952
  }
960
953
  // Write DZ to file
@@ -976,7 +969,7 @@ class PipelineWorker : public Nan::AsyncWorker {
976
969
  image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
977
970
  baton->formatOut = "dz";
978
971
  } else if (baton->formatOut == "v" || (mightMatchInput && isV) ||
979
- (willMatchInput && inputImageType == ImageType::VIPS)) {
972
+ (willMatchInput && inputImageType == sharp::ImageType::VIPS)) {
980
973
  // Write V to file
981
974
  image.vipssave(const_cast<char*>(baton->fileOut.data()), VImage::option()
982
975
  ->set("strip", !baton->withMetadata));
@@ -1000,16 +993,18 @@ class PipelineWorker : public Nan::AsyncWorker {
1000
993
  vips_thread_shutdown();
1001
994
  }
1002
995
 
1003
- void HandleOKCallback() {
1004
- using Nan::New;
1005
- using Nan::Set;
1006
- Nan::HandleScope();
996
+ void OnOK() {
997
+ Napi::Env env = Env();
998
+ Napi::HandleScope scope(env);
1007
999
 
1008
- v8::Local<v8::Value> argv[3] = { Nan::Null(), Nan::Null(), Nan::Null() };
1009
- if (!baton->err.empty()) {
1010
- // Error
1011
- argv[0] = Nan::Error(baton->err.data());
1012
- } else {
1000
+ // Handle warnings
1001
+ std::string warning = sharp::VipsWarningPop();
1002
+ while (!warning.empty()) {
1003
+ debuglog.Call({ Napi::String::New(env, warning) });
1004
+ warning = sharp::VipsWarningPop();
1005
+ }
1006
+
1007
+ if (baton->err.empty()) {
1013
1008
  int width = baton->width;
1014
1009
  int height = baton->height;
1015
1010
  if (baton->topOffsetPre != -1 && (baton->width == -1 || baton->height == -1)) {
@@ -1021,50 +1016,40 @@ class PipelineWorker : public Nan::AsyncWorker {
1021
1016
  height = baton->heightPost;
1022
1017
  }
1023
1018
  // Info Object
1024
- v8::Local<v8::Object> info = New<v8::Object>();
1025
- Set(info, New("format").ToLocalChecked(), New<v8::String>(baton->formatOut).ToLocalChecked());
1026
- Set(info, New("width").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(width)));
1027
- Set(info, New("height").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(height)));
1028
- Set(info, New("channels").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->channels)));
1029
- Set(info, New("premultiplied").ToLocalChecked(), New<v8::Boolean>(baton->premultiplied));
1019
+ Napi::Object info = Napi::Object::New(env);
1020
+ info.Set("format", baton->formatOut);
1021
+ info.Set("width", static_cast<uint32_t>(width));
1022
+ info.Set("height", static_cast<uint32_t>(height));
1023
+ info.Set("channels", static_cast<uint32_t>(baton->channels));
1024
+ info.Set("premultiplied", baton->premultiplied);
1030
1025
  if (baton->hasCropOffset) {
1031
- Set(info, New("cropOffsetLeft").ToLocalChecked(),
1032
- New<v8::Int32>(static_cast<int32_t>(baton->cropOffsetLeft)));
1033
- Set(info, New("cropOffsetTop").ToLocalChecked(),
1034
- New<v8::Int32>(static_cast<int32_t>(baton->cropOffsetTop)));
1026
+ info.Set("cropOffsetLeft", static_cast<int32_t>(baton->cropOffsetLeft));
1027
+ info.Set("cropOffsetTop", static_cast<int32_t>(baton->cropOffsetTop));
1035
1028
  }
1036
1029
  if (baton->trimThreshold > 0.0) {
1037
- Set(info, New("trimOffsetLeft").ToLocalChecked(),
1038
- New<v8::Int32>(static_cast<int32_t>(baton->trimOffsetLeft)));
1039
- Set(info, New("trimOffsetTop").ToLocalChecked(),
1040
- New<v8::Int32>(static_cast<int32_t>(baton->trimOffsetTop)));
1030
+ info.Set("trimOffsetLeft", static_cast<int32_t>(baton->trimOffsetLeft));
1031
+ info.Set("trimOffsetTop", static_cast<int32_t>(baton->trimOffsetTop));
1041
1032
  }
1042
1033
 
1043
1034
  if (baton->bufferOutLength > 0) {
1044
- // Pass ownership of output data to Buffer instance
1045
- argv[1] = Nan::NewBuffer(
1046
- static_cast<char*>(baton->bufferOut), baton->bufferOutLength, sharp::FreeCallback, nullptr)
1047
- .ToLocalChecked();
1048
1035
  // Add buffer size to info
1049
- Set(info, New("size").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->bufferOutLength)));
1050
- argv[2] = info;
1036
+ info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
1037
+ // Pass ownership of output data to Buffer instance
1038
+ Napi::Buffer<char> data = Napi::Buffer<char>::New(env, static_cast<char*>(baton->bufferOut),
1039
+ baton->bufferOutLength, sharp::FreeCallback);
1040
+ Callback().MakeCallback(Receiver().Value(), { env.Null(), data, info });
1051
1041
  } else {
1052
1042
  // Add file size to info
1053
1043
  struct STAT64_STRUCT st;
1054
1044
  if (STAT64_FUNCTION(baton->fileOut.data(), &st) == 0) {
1055
- Set(info, New("size").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(st.st_size)));
1045
+ info.Set("size", static_cast<uint32_t>(st.st_size));
1056
1046
  }
1057
- argv[1] = info;
1047
+ Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
1058
1048
  }
1049
+ } else {
1050
+ Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
1059
1051
  }
1060
1052
 
1061
- // Dispose of Persistent wrapper around input Buffers so they can be garbage collected
1062
- std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
1063
- [this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
1064
- GetFromPersistent(index);
1065
- return index + 1;
1066
- });
1067
-
1068
1053
  // Delete baton
1069
1054
  delete baton->input;
1070
1055
  delete baton->boolean;
@@ -1077,29 +1062,16 @@ class PipelineWorker : public Nan::AsyncWorker {
1077
1062
  }
1078
1063
  delete baton;
1079
1064
 
1080
- // Handle warnings
1081
- std::string warning = sharp::VipsWarningPop();
1082
- while (!warning.empty()) {
1083
- v8::Local<v8::Value> message[1] = { New(warning).ToLocalChecked() };
1084
- debuglog->Call(1, message, async_resource);
1085
- warning = sharp::VipsWarningPop();
1086
- }
1087
-
1088
1065
  // Decrement processing task counter
1089
1066
  g_atomic_int_dec_and_test(&sharp::counterProcess);
1090
- v8::Local<v8::Value> queueLength[1] = { New<v8::Uint32>(sharp::counterQueue) };
1091
- queueListener->Call(1, queueLength, async_resource);
1092
- delete queueListener;
1093
-
1094
- // Return to JavaScript
1095
- callback->Call(3, argv, async_resource);
1067
+ Napi::Number queueLength = Napi::Number::New(env, static_cast<double>(sharp::counterQueue));
1068
+ queueListener.Call(Receiver().Value(), { queueLength });
1096
1069
  }
1097
1070
 
1098
1071
  private:
1099
1072
  PipelineBaton *baton;
1100
- Nan::Callback *debuglog;
1101
- Nan::Callback *queueListener;
1102
- std::vector<v8::Local<v8::Object>> buffersToPersist;
1073
+ Napi::FunctionReference debuglog;
1074
+ Napi::FunctionReference queueListener;
1103
1075
 
1104
1076
  /*
1105
1077
  Calculate the angle of rotation and need-to-flip for the given Exif orientation
@@ -1169,37 +1141,27 @@ class PipelineWorker : public Nan::AsyncWorker {
1169
1141
  /*
1170
1142
  pipeline(options, output, callback)
1171
1143
  */
1172
- NAN_METHOD(pipeline) {
1173
- using sharp::HasAttr;
1174
- using sharp::AttrTo;
1175
- using sharp::AttrAs;
1176
- using sharp::AttrAsStr;
1177
- using sharp::AttrAsRgba;
1178
- using sharp::CreateInputDescriptor;
1179
-
1180
- // Input Buffers must not undergo GC compaction during processing
1181
- std::vector<v8::Local<v8::Object>> buffersToPersist;
1182
-
1144
+ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1183
1145
  // V8 objects are converted to non-V8 types held in the baton struct
1184
1146
  PipelineBaton *baton = new PipelineBaton;
1185
- v8::Local<v8::Object> options = info[0].As<v8::Object>();
1147
+ Napi::Object options = info[0].As<Napi::Object>();
1186
1148
 
1187
1149
  // Input
1188
- baton->input = CreateInputDescriptor(AttrAs<v8::Object>(options, "input"), buffersToPersist);
1150
+ baton->input = sharp::CreateInputDescriptor(options.Get("input").As<Napi::Object>());
1189
1151
  // Extract image options
1190
- baton->topOffsetPre = AttrTo<int32_t>(options, "topOffsetPre");
1191
- baton->leftOffsetPre = AttrTo<int32_t>(options, "leftOffsetPre");
1192
- baton->widthPre = AttrTo<int32_t>(options, "widthPre");
1193
- baton->heightPre = AttrTo<int32_t>(options, "heightPre");
1194
- baton->topOffsetPost = AttrTo<int32_t>(options, "topOffsetPost");
1195
- baton->leftOffsetPost = AttrTo<int32_t>(options, "leftOffsetPost");
1196
- baton->widthPost = AttrTo<int32_t>(options, "widthPost");
1197
- baton->heightPost = AttrTo<int32_t>(options, "heightPost");
1152
+ baton->topOffsetPre = sharp::AttrAsInt32(options, "topOffsetPre");
1153
+ baton->leftOffsetPre = sharp::AttrAsInt32(options, "leftOffsetPre");
1154
+ baton->widthPre = sharp::AttrAsInt32(options, "widthPre");
1155
+ baton->heightPre = sharp::AttrAsInt32(options, "heightPre");
1156
+ baton->topOffsetPost = sharp::AttrAsInt32(options, "topOffsetPost");
1157
+ baton->leftOffsetPost = sharp::AttrAsInt32(options, "leftOffsetPost");
1158
+ baton->widthPost = sharp::AttrAsInt32(options, "widthPost");
1159
+ baton->heightPost = sharp::AttrAsInt32(options, "heightPost");
1198
1160
  // Output image dimensions
1199
- baton->width = AttrTo<int32_t>(options, "width");
1200
- baton->height = AttrTo<int32_t>(options, "height");
1161
+ baton->width = sharp::AttrAsInt32(options, "width");
1162
+ baton->height = sharp::AttrAsInt32(options, "height");
1201
1163
  // Canvas option
1202
- std::string canvas = AttrAsStr(options, "canvas");
1164
+ std::string canvas = sharp::AttrAsStr(options, "canvas");
1203
1165
  if (canvas == "crop") {
1204
1166
  baton->canvas = Canvas::CROP;
1205
1167
  } else if (canvas == "embed") {
@@ -1212,191 +1174,168 @@ NAN_METHOD(pipeline) {
1212
1174
  baton->canvas = Canvas::IGNORE_ASPECT;
1213
1175
  }
1214
1176
  // Tint chroma
1215
- baton->tintA = AttrTo<double>(options, "tintA");
1216
- baton->tintB = AttrTo<double>(options, "tintB");
1177
+ baton->tintA = sharp::AttrAsDouble(options, "tintA");
1178
+ baton->tintB = sharp::AttrAsDouble(options, "tintB");
1217
1179
  // Composite
1218
- v8::Local<v8::Array> compositeArray = Nan::Get(options, Nan::New("composite").ToLocalChecked())
1219
- .ToLocalChecked().As<v8::Array>();
1220
- int const compositeArrayLength = AttrTo<uint32_t>(compositeArray, "length");
1221
- for (int i = 0; i < compositeArrayLength; i++) {
1222
- v8::Local<v8::Object> compositeObject = Nan::Get(compositeArray, i).ToLocalChecked().As<v8::Object>();
1180
+ Napi::Array compositeArray = options.Get("composite").As<Napi::Array>();
1181
+ for (unsigned int i = 0; i < compositeArray.Length(); i++) {
1182
+ Napi::Object compositeObject = compositeArray.Get(i).As<Napi::Object>();
1223
1183
  Composite *composite = new Composite;
1224
- composite->input = CreateInputDescriptor(AttrAs<v8::Object>(compositeObject, "input"), buffersToPersist);
1184
+ composite->input = sharp::CreateInputDescriptor(compositeObject.Get("input").As<Napi::Object>());
1225
1185
  composite->mode = static_cast<VipsBlendMode>(
1226
- vips_enum_from_nick(nullptr, VIPS_TYPE_BLEND_MODE, AttrAsStr(compositeObject, "blend").data()));
1227
- composite->gravity = AttrTo<uint32_t>(compositeObject, "gravity");
1228
- composite->left = AttrTo<int32_t>(compositeObject, "left");
1229
- composite->top = AttrTo<int32_t>(compositeObject, "top");
1230
- composite->tile = AttrTo<bool>(compositeObject, "tile");
1231
- composite->premultiplied = AttrTo<bool>(compositeObject, "premultiplied");
1186
+ vips_enum_from_nick(nullptr, VIPS_TYPE_BLEND_MODE, sharp::AttrAsStr(compositeObject, "blend").data()));
1187
+ composite->gravity = sharp::AttrAsUint32(compositeObject, "gravity");
1188
+ composite->left = sharp::AttrAsInt32(compositeObject, "left");
1189
+ composite->top = sharp::AttrAsInt32(compositeObject, "top");
1190
+ composite->tile = sharp::AttrAsBool(compositeObject, "tile");
1191
+ composite->premultiplied = sharp::AttrAsBool(compositeObject, "premultiplied");
1232
1192
  baton->composite.push_back(composite);
1233
1193
  }
1234
1194
  // Resize options
1235
- baton->withoutEnlargement = AttrTo<bool>(options, "withoutEnlargement");
1236
- baton->position = AttrTo<int32_t>(options, "position");
1237
- baton->resizeBackground = AttrAsRgba(options, "resizeBackground");
1238
- baton->kernel = AttrAsStr(options, "kernel");
1239
- baton->fastShrinkOnLoad = AttrTo<bool>(options, "fastShrinkOnLoad");
1195
+ baton->withoutEnlargement = sharp::AttrAsBool(options, "withoutEnlargement");
1196
+ baton->position = sharp::AttrAsInt32(options, "position");
1197
+ baton->resizeBackground = sharp::AttrAsRgba(options, "resizeBackground");
1198
+ baton->kernel = sharp::AttrAsStr(options, "kernel");
1199
+ baton->fastShrinkOnLoad = sharp::AttrAsBool(options, "fastShrinkOnLoad");
1240
1200
  // Join Channel Options
1241
- if (HasAttr(options, "joinChannelIn")) {
1242
- v8::Local<v8::Object> joinChannelObject = Nan::Get(options, Nan::New("joinChannelIn").ToLocalChecked())
1243
- .ToLocalChecked().As<v8::Object>();
1244
- v8::Local<v8::Array> joinChannelArray = joinChannelObject.As<v8::Array>();
1245
- int joinChannelArrayLength = AttrTo<int32_t>(joinChannelObject, "length");
1246
- for (int i = 0; i < joinChannelArrayLength; i++) {
1201
+ if (options.Has("joinChannelIn")) {
1202
+ Napi::Array joinChannelArray = options.Get("joinChannelIn").As<Napi::Array>();
1203
+ for (unsigned int i = 0; i < joinChannelArray.Length(); i++) {
1247
1204
  baton->joinChannelIn.push_back(
1248
- CreateInputDescriptor(
1249
- Nan::Get(joinChannelArray, i).ToLocalChecked().As<v8::Object>(),
1250
- buffersToPersist));
1205
+ sharp::CreateInputDescriptor(joinChannelArray.Get(i).As<Napi::Object>()));
1251
1206
  }
1252
1207
  }
1253
1208
  // Operators
1254
- baton->flatten = AttrTo<bool>(options, "flatten");
1255
- baton->flattenBackground = AttrAsRgba(options, "flattenBackground");
1256
- baton->negate = AttrTo<bool>(options, "negate");
1257
- baton->blurSigma = AttrTo<double>(options, "blurSigma");
1258
- baton->brightness = AttrTo<double>(options, "brightness");
1259
- baton->saturation = AttrTo<double>(options, "saturation");
1260
- baton->hue = AttrTo<int32_t>(options, "hue");
1261
- baton->medianSize = AttrTo<uint32_t>(options, "medianSize");
1262
- baton->sharpenSigma = AttrTo<double>(options, "sharpenSigma");
1263
- baton->sharpenFlat = AttrTo<double>(options, "sharpenFlat");
1264
- baton->sharpenJagged = AttrTo<double>(options, "sharpenJagged");
1265
- baton->threshold = AttrTo<int32_t>(options, "threshold");
1266
- baton->thresholdGrayscale = AttrTo<bool>(options, "thresholdGrayscale");
1267
- baton->trimThreshold = AttrTo<double>(options, "trimThreshold");
1268
- baton->gamma = AttrTo<double>(options, "gamma");
1269
- baton->gammaOut = AttrTo<double>(options, "gammaOut");
1270
- baton->linearA = AttrTo<double>(options, "linearA");
1271
- baton->linearB = AttrTo<double>(options, "linearB");
1272
- baton->greyscale = AttrTo<bool>(options, "greyscale");
1273
- baton->normalise = AttrTo<bool>(options, "normalise");
1274
- baton->useExifOrientation = AttrTo<bool>(options, "useExifOrientation");
1275
- baton->angle = AttrTo<int32_t>(options, "angle");
1276
- baton->rotationAngle = AttrTo<double>(options, "rotationAngle");
1277
- baton->rotationBackground = AttrAsRgba(options, "rotationBackground");
1278
- baton->rotateBeforePreExtract = AttrTo<bool>(options, "rotateBeforePreExtract");
1279
- baton->flip = AttrTo<bool>(options, "flip");
1280
- baton->flop = AttrTo<bool>(options, "flop");
1281
- baton->extendTop = AttrTo<int32_t>(options, "extendTop");
1282
- baton->extendBottom = AttrTo<int32_t>(options, "extendBottom");
1283
- baton->extendLeft = AttrTo<int32_t>(options, "extendLeft");
1284
- baton->extendRight = AttrTo<int32_t>(options, "extendRight");
1285
- baton->extendBackground = AttrAsRgba(options, "extendBackground");
1286
- baton->extractChannel = AttrTo<int32_t>(options, "extractChannel");
1287
-
1288
- baton->removeAlpha = AttrTo<bool>(options, "removeAlpha");
1289
- baton->ensureAlpha = AttrTo<bool>(options, "ensureAlpha");
1290
- if (HasAttr(options, "boolean")) {
1291
- baton->boolean = CreateInputDescriptor(AttrAs<v8::Object>(options, "boolean"), buffersToPersist);
1292
- baton->booleanOp = sharp::GetBooleanOperation(AttrAsStr(options, "booleanOp"));
1209
+ baton->flatten = sharp::AttrAsBool(options, "flatten");
1210
+ baton->flattenBackground = sharp::AttrAsRgba(options, "flattenBackground");
1211
+ baton->negate = sharp::AttrAsBool(options, "negate");
1212
+ baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
1213
+ baton->brightness = sharp::AttrAsDouble(options, "brightness");
1214
+ baton->saturation = sharp::AttrAsDouble(options, "saturation");
1215
+ baton->hue = sharp::AttrAsInt32(options, "hue");
1216
+ baton->medianSize = sharp::AttrAsUint32(options, "medianSize");
1217
+ baton->sharpenSigma = sharp::AttrAsDouble(options, "sharpenSigma");
1218
+ baton->sharpenFlat = sharp::AttrAsDouble(options, "sharpenFlat");
1219
+ baton->sharpenJagged = sharp::AttrAsDouble(options, "sharpenJagged");
1220
+ baton->threshold = sharp::AttrAsInt32(options, "threshold");
1221
+ baton->thresholdGrayscale = sharp::AttrAsBool(options, "thresholdGrayscale");
1222
+ baton->trimThreshold = sharp::AttrAsDouble(options, "trimThreshold");
1223
+ baton->gamma = sharp::AttrAsDouble(options, "gamma");
1224
+ baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
1225
+ baton->linearA = sharp::AttrAsDouble(options, "linearA");
1226
+ baton->linearB = sharp::AttrAsDouble(options, "linearB");
1227
+ baton->greyscale = sharp::AttrAsBool(options, "greyscale");
1228
+ baton->normalise = sharp::AttrAsBool(options, "normalise");
1229
+ baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
1230
+ baton->angle = sharp::AttrAsInt32(options, "angle");
1231
+ baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
1232
+ baton->rotationBackground = sharp::AttrAsRgba(options, "rotationBackground");
1233
+ baton->rotateBeforePreExtract = sharp::AttrAsBool(options, "rotateBeforePreExtract");
1234
+ baton->flip = sharp::AttrAsBool(options, "flip");
1235
+ baton->flop = sharp::AttrAsBool(options, "flop");
1236
+ baton->extendTop = sharp::AttrAsInt32(options, "extendTop");
1237
+ baton->extendBottom = sharp::AttrAsInt32(options, "extendBottom");
1238
+ baton->extendLeft = sharp::AttrAsInt32(options, "extendLeft");
1239
+ baton->extendRight = sharp::AttrAsInt32(options, "extendRight");
1240
+ baton->extendBackground = sharp::AttrAsRgba(options, "extendBackground");
1241
+ baton->extractChannel = sharp::AttrAsInt32(options, "extractChannel");
1242
+
1243
+ baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha");
1244
+ baton->ensureAlpha = sharp::AttrAsBool(options, "ensureAlpha");
1245
+ if (options.Has("boolean")) {
1246
+ baton->boolean = sharp::CreateInputDescriptor(options.Get("boolean").As<Napi::Object>());
1247
+ baton->booleanOp = sharp::GetBooleanOperation(sharp::AttrAsStr(options, "booleanOp"));
1293
1248
  }
1294
- if (HasAttr(options, "bandBoolOp")) {
1295
- baton->bandBoolOp = sharp::GetBooleanOperation(AttrAsStr(options, "bandBoolOp"));
1249
+ if (options.Has("bandBoolOp")) {
1250
+ baton->bandBoolOp = sharp::GetBooleanOperation(sharp::AttrAsStr(options, "bandBoolOp"));
1296
1251
  }
1297
- if (HasAttr(options, "convKernel")) {
1298
- v8::Local<v8::Object> kernel = AttrAs<v8::Object>(options, "convKernel");
1299
- baton->convKernelWidth = AttrTo<uint32_t>(kernel, "width");
1300
- baton->convKernelHeight = AttrTo<uint32_t>(kernel, "height");
1301
- baton->convKernelScale = AttrTo<double>(kernel, "scale");
1302
- baton->convKernelOffset = AttrTo<double>(kernel, "offset");
1252
+ if (options.Has("convKernel")) {
1253
+ Napi::Object kernel = options.Get("convKernel").As<Napi::Object>();
1254
+ baton->convKernelWidth = sharp::AttrAsUint32(kernel, "width");
1255
+ baton->convKernelHeight = sharp::AttrAsUint32(kernel, "height");
1256
+ baton->convKernelScale = sharp::AttrAsDouble(kernel, "scale");
1257
+ baton->convKernelOffset = sharp::AttrAsDouble(kernel, "offset");
1303
1258
  size_t const kernelSize = static_cast<size_t>(baton->convKernelWidth * baton->convKernelHeight);
1304
1259
  baton->convKernel = std::unique_ptr<double[]>(new double[kernelSize]);
1305
- v8::Local<v8::Array> kdata = AttrAs<v8::Array>(kernel, "kernel");
1260
+ Napi::Array kdata = kernel.Get("kernel").As<Napi::Array>();
1306
1261
  for (unsigned int i = 0; i < kernelSize; i++) {
1307
- baton->convKernel[i] = AttrTo<double>(kdata, i);
1262
+ baton->convKernel[i] = sharp::AttrAsDouble(kdata, i);
1308
1263
  }
1309
1264
  }
1310
- if (HasAttr(options, "recombMatrix")) {
1265
+ if (options.Has("recombMatrix")) {
1311
1266
  baton->recombMatrix = std::unique_ptr<double[]>(new double[9]);
1312
- v8::Local<v8::Array> recombMatrix = AttrAs<v8::Array>(options, "recombMatrix");
1267
+ Napi::Array recombMatrix = options.Get("recombMatrix").As<Napi::Array>();
1313
1268
  for (unsigned int i = 0; i < 9; i++) {
1314
- baton->recombMatrix[i] = AttrTo<double>(recombMatrix, i);
1269
+ baton->recombMatrix[i] = sharp::AttrAsDouble(recombMatrix, i);
1315
1270
  }
1316
1271
  }
1317
- baton->colourspace = sharp::GetInterpretation(AttrAsStr(options, "colourspace"));
1272
+ baton->colourspace = sharp::GetInterpretation(sharp::AttrAsStr(options, "colourspace"));
1318
1273
  if (baton->colourspace == VIPS_INTERPRETATION_ERROR) {
1319
1274
  baton->colourspace = VIPS_INTERPRETATION_sRGB;
1320
1275
  }
1321
1276
  // Output
1322
- baton->formatOut = AttrAsStr(options, "formatOut");
1323
- baton->fileOut = AttrAsStr(options, "fileOut");
1324
- baton->withMetadata = AttrTo<bool>(options, "withMetadata");
1325
- baton->withMetadataOrientation = AttrTo<uint32_t>(options, "withMetadataOrientation");
1277
+ baton->formatOut = sharp::AttrAsStr(options, "formatOut");
1278
+ baton->fileOut = sharp::AttrAsStr(options, "fileOut");
1279
+ baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
1280
+ baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
1326
1281
  // Format-specific
1327
- baton->jpegQuality = AttrTo<uint32_t>(options, "jpegQuality");
1328
- baton->jpegProgressive = AttrTo<bool>(options, "jpegProgressive");
1329
- baton->jpegChromaSubsampling = AttrAsStr(options, "jpegChromaSubsampling");
1330
- baton->jpegTrellisQuantisation = AttrTo<bool>(options, "jpegTrellisQuantisation");
1331
- baton->jpegQuantisationTable = AttrTo<uint32_t>(options, "jpegQuantisationTable");
1332
- baton->jpegOvershootDeringing = AttrTo<bool>(options, "jpegOvershootDeringing");
1333
- baton->jpegOptimiseScans = AttrTo<bool>(options, "jpegOptimiseScans");
1334
- baton->jpegOptimiseCoding = AttrTo<bool>(options, "jpegOptimiseCoding");
1335
- baton->pngProgressive = AttrTo<bool>(options, "pngProgressive");
1336
- baton->pngCompressionLevel = AttrTo<uint32_t>(options, "pngCompressionLevel");
1337
- baton->pngAdaptiveFiltering = AttrTo<bool>(options, "pngAdaptiveFiltering");
1338
- baton->pngPalette = AttrTo<bool>(options, "pngPalette");
1339
- baton->pngQuality = AttrTo<uint32_t>(options, "pngQuality");
1340
- baton->pngColours = AttrTo<uint32_t>(options, "pngColours");
1341
- baton->pngDither = AttrTo<double>(options, "pngDither");
1342
- baton->webpQuality = AttrTo<uint32_t>(options, "webpQuality");
1343
- baton->webpAlphaQuality = AttrTo<uint32_t>(options, "webpAlphaQuality");
1344
- baton->webpLossless = AttrTo<bool>(options, "webpLossless");
1345
- baton->webpNearLossless = AttrTo<bool>(options, "webpNearLossless");
1346
- baton->webpSmartSubsample = AttrTo<bool>(options, "webpSmartSubsample");
1347
- baton->webpReductionEffort = AttrTo<uint32_t>(options, "webpReductionEffort");
1348
- baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality");
1349
- baton->tiffPyramid = AttrTo<bool>(options, "tiffPyramid");
1350
- baton->tiffSquash = AttrTo<bool>(options, "tiffSquash");
1351
- baton->tiffTile = AttrTo<bool>(options, "tiffTile");
1352
- baton->tiffTileWidth = AttrTo<uint32_t>(options, "tiffTileWidth");
1353
- baton->tiffTileHeight = AttrTo<uint32_t>(options, "tiffTileHeight");
1354
- baton->tiffXres = AttrTo<double>(options, "tiffXres");
1355
- baton->tiffYres = AttrTo<double>(options, "tiffYres");
1282
+ baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
1283
+ baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
1284
+ baton->jpegChromaSubsampling = sharp::AttrAsStr(options, "jpegChromaSubsampling");
1285
+ baton->jpegTrellisQuantisation = sharp::AttrAsBool(options, "jpegTrellisQuantisation");
1286
+ baton->jpegQuantisationTable = sharp::AttrAsUint32(options, "jpegQuantisationTable");
1287
+ baton->jpegOvershootDeringing = sharp::AttrAsBool(options, "jpegOvershootDeringing");
1288
+ baton->jpegOptimiseScans = sharp::AttrAsBool(options, "jpegOptimiseScans");
1289
+ baton->jpegOptimiseCoding = sharp::AttrAsBool(options, "jpegOptimiseCoding");
1290
+ baton->pngProgressive = sharp::AttrAsBool(options, "pngProgressive");
1291
+ baton->pngCompressionLevel = sharp::AttrAsUint32(options, "pngCompressionLevel");
1292
+ baton->pngAdaptiveFiltering = sharp::AttrAsBool(options, "pngAdaptiveFiltering");
1293
+ baton->pngPalette = sharp::AttrAsBool(options, "pngPalette");
1294
+ baton->pngQuality = sharp::AttrAsUint32(options, "pngQuality");
1295
+ baton->pngColours = sharp::AttrAsUint32(options, "pngColours");
1296
+ baton->pngDither = sharp::AttrAsDouble(options, "pngDither");
1297
+ baton->webpQuality = sharp::AttrAsUint32(options, "webpQuality");
1298
+ baton->webpAlphaQuality = sharp::AttrAsUint32(options, "webpAlphaQuality");
1299
+ baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
1300
+ baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
1301
+ baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
1302
+ baton->webpReductionEffort = sharp::AttrAsUint32(options, "webpReductionEffort");
1303
+ baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
1304
+ baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
1305
+ baton->tiffSquash = sharp::AttrAsBool(options, "tiffSquash");
1306
+ baton->tiffTile = sharp::AttrAsBool(options, "tiffTile");
1307
+ baton->tiffTileWidth = sharp::AttrAsUint32(options, "tiffTileWidth");
1308
+ baton->tiffTileHeight = sharp::AttrAsUint32(options, "tiffTileHeight");
1309
+ baton->tiffXres = sharp::AttrAsDouble(options, "tiffXres");
1310
+ baton->tiffYres = sharp::AttrAsDouble(options, "tiffYres");
1356
1311
  // tiff compression options
1357
1312
  baton->tiffCompression = static_cast<VipsForeignTiffCompression>(
1358
1313
  vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_COMPRESSION,
1359
- AttrAsStr(options, "tiffCompression").data()));
1314
+ sharp::AttrAsStr(options, "tiffCompression").data()));
1360
1315
  baton->tiffPredictor = static_cast<VipsForeignTiffPredictor>(
1361
1316
  vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_PREDICTOR,
1362
- AttrAsStr(options, "tiffPredictor").data()));
1363
- baton->heifQuality = AttrTo<uint32_t>(options, "heifQuality");
1364
- baton->heifLossless = AttrTo<bool>(options, "heifLossless");
1317
+ sharp::AttrAsStr(options, "tiffPredictor").data()));
1318
+ baton->heifQuality = sharp::AttrAsUint32(options, "heifQuality");
1319
+ baton->heifLossless = sharp::AttrAsBool(options, "heifLossless");
1365
1320
  baton->heifCompression = static_cast<VipsForeignHeifCompression>(
1366
- vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
1367
- AttrAsStr(options, "heifCompression").data()));
1321
+ vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
1322
+ sharp::AttrAsStr(options, "heifCompression").data()));
1368
1323
  // Tile output
1369
- baton->tileSize = AttrTo<uint32_t>(options, "tileSize");
1370
- baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap");
1371
- std::string tileContainer = AttrAsStr(options, "tileContainer");
1372
- baton->tileAngle = AttrTo<int32_t>(options, "tileAngle");
1373
- baton->tileBackground = AttrAsRgba(options, "tileBackground");
1374
- baton->tileSkipBlanks = AttrTo<int32_t>(options, "tileSkipBlanks");
1375
- if (tileContainer == "zip") {
1376
- baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
1377
- } else {
1378
- baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_FS;
1379
- }
1380
- std::string tileLayout = AttrAsStr(options, "tileLayout");
1381
- if (tileLayout == "google") {
1382
- baton->tileLayout = VIPS_FOREIGN_DZ_LAYOUT_GOOGLE;
1383
- } else if (tileLayout == "zoomify") {
1384
- baton->tileLayout = VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY;
1385
- } else {
1386
- baton->tileLayout = VIPS_FOREIGN_DZ_LAYOUT_DZ;
1387
- }
1388
- baton->tileFormat = AttrAsStr(options, "tileFormat");
1389
- std::string tileDepth = AttrAsStr(options, "tileDepth");
1390
- if (tileDepth == "onetile") {
1391
- baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_ONETILE;
1392
- } else if (tileDepth == "one") {
1393
- baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_ONE;
1394
- } else if (tileDepth == "onepixel") {
1395
- baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_ONEPIXEL;
1396
- } else {
1397
- // signal that we do not want to pass any value to dzSave
1398
- baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_LAST;
1399
- }
1324
+ baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
1325
+ baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
1326
+ baton->tileAngle = sharp::AttrAsInt32(options, "tileAngle");
1327
+ baton->tileBackground = sharp::AttrAsRgba(options, "tileBackground");
1328
+ baton->tileSkipBlanks = sharp::AttrAsInt32(options, "tileSkipBlanks");
1329
+ baton->tileContainer = static_cast<VipsForeignDzContainer>(
1330
+ vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_CONTAINER,
1331
+ sharp::AttrAsStr(options, "tileContainer").data()));
1332
+ baton->tileLayout = static_cast<VipsForeignDzLayout>(
1333
+ vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_LAYOUT,
1334
+ sharp::AttrAsStr(options, "tileLayout").data()));
1335
+ baton->tileFormat = sharp::AttrAsStr(options, "tileFormat");
1336
+ baton->tileDepth = static_cast<VipsForeignDzDepth>(
1337
+ vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_DEPTH,
1338
+ sharp::AttrAsStr(options, "tileDepth").data()));
1400
1339
 
1401
1340
  // Force random access for certain operations
1402
1341
  if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) {
@@ -1404,25 +1343,30 @@ NAN_METHOD(pipeline) {
1404
1343
  baton->trimThreshold > 0.0 ||
1405
1344
  baton->normalise ||
1406
1345
  baton->position == 16 || baton->position == 17 ||
1407
- baton->angle != 0 || baton->rotationAngle != 0.0
1346
+ baton->angle % 360 != 0 ||
1347
+ fmod(baton->rotationAngle, 360.0) != 0.0 ||
1348
+ baton->useExifOrientation
1408
1349
  ) {
1409
1350
  baton->input->access = VIPS_ACCESS_RANDOM;
1410
1351
  }
1411
1352
  }
1412
1353
 
1413
1354
  // Function to notify of libvips warnings
1414
- Nan::Callback *debuglog = new Nan::Callback(AttrAs<v8::Function>(options, "debuglog"));
1355
+ Napi::Function debuglog = options.Get("debuglog").As<Napi::Function>();
1415
1356
 
1416
1357
  // Function to notify of queue length changes
1417
- Nan::Callback *queueListener = new Nan::Callback(AttrAs<v8::Function>(options, "queueListener"));
1358
+ Napi::Function queueListener = options.Get("queueListener").As<Napi::Function>();
1418
1359
 
1419
1360
  // Join queue for worker thread
1420
- Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>());
1421
- Nan::AsyncQueueWorker(new PipelineWorker(callback, baton, debuglog, queueListener, buffersToPersist));
1361
+ Napi::Function callback = info[1].As<Napi::Function>();
1362
+ PipelineWorker *worker = new PipelineWorker(callback, baton, debuglog, queueListener);
1363
+ worker->Receiver().Set("options", options);
1364
+ worker->Queue();
1422
1365
 
1423
1366
  // Increment queued task counter
1424
1367
  g_atomic_int_inc(&sharp::counterQueue);
1425
- v8::Local<v8::Value> queueLength[1] = { Nan::New<v8::Uint32>(sharp::counterQueue) };
1426
- v8::Local<v8::Object> recv = Nan::New<v8::Object>();
1427
- Nan::Call(*queueListener, recv, 1, queueLength);
1368
+ Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<double>(sharp::counterQueue));
1369
+ queueListener.Call(info.This(), { queueLength });
1370
+
1371
+ return info.Env().Undefined();
1428
1372
  }