sharp 0.30.7 → 0.31.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
@@ -118,7 +118,7 @@ class PipelineWorker : public Napi::AsyncWorker {
118
118
  // Trim
119
119
  if (baton->trimThreshold > 0.0) {
120
120
  MultiPageUnsupported(nPages, "Trim");
121
- image = sharp::Trim(image, baton->trimThreshold);
121
+ image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold);
122
122
  baton->trimOffsetLeft = image.xoffset();
123
123
  baton->trimOffsetTop = image.yoffset();
124
124
  }
@@ -204,6 +204,7 @@ class PipelineWorker : public Napi::AsyncWorker {
204
204
  vips::VOption *option = VImage::option()
205
205
  ->set("access", baton->input->access)
206
206
  ->set("shrink", jpegShrinkOnLoad)
207
+ ->set("unlimited", baton->input->unlimited)
207
208
  ->set("fail_on", baton->input->failOn);
208
209
  if (baton->input->buffer != nullptr) {
209
210
  // Reload JPEG buffer
@@ -302,8 +303,7 @@ class PipelineWorker : public Napi::AsyncWorker {
302
303
  if (
303
304
  sharp::HasProfile(image) &&
304
305
  image.interpretation() != VIPS_INTERPRETATION_LABS &&
305
- image.interpretation() != VIPS_INTERPRETATION_GREY16 &&
306
- image.interpretation() != VIPS_INTERPRETATION_B_W
306
+ image.interpretation() != VIPS_INTERPRETATION_GREY16
307
307
  ) {
308
308
  // Convert to sRGB/P3 using embedded profile
309
309
  try {
@@ -322,16 +322,7 @@ class PipelineWorker : public Napi::AsyncWorker {
322
322
 
323
323
  // Flatten image to remove alpha channel
324
324
  if (baton->flatten && sharp::HasAlpha(image)) {
325
- // Scale up 8-bit values to match 16-bit input image
326
- double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
327
- // Background colour
328
- std::vector<double> background {
329
- baton->flattenBackground[0] * multiplier,
330
- baton->flattenBackground[1] * multiplier,
331
- baton->flattenBackground[2] * multiplier
332
- };
333
- image = image.flatten(VImage::option()
334
- ->set("background", background));
325
+ image = sharp::Flatten(image, baton->flattenBackground);
335
326
  }
336
327
 
337
328
  // Negate the colours in the image
@@ -353,11 +344,7 @@ class PipelineWorker : public Napi::AsyncWorker {
353
344
  bool const shouldBlur = baton->blurSigma != 0.0;
354
345
  bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0;
355
346
  bool const shouldSharpen = baton->sharpenSigma != 0.0;
356
- bool const shouldApplyMedian = baton->medianSize > 0;
357
347
  bool const shouldComposite = !baton->composite.empty();
358
- bool const shouldModulate = baton->brightness != 1.0 || baton->saturation != 1.0 ||
359
- baton->hue != 0.0 || baton->lightness != 0.0;
360
- bool const shouldApplyClahe = baton->claheWidth != 0 && baton->claheHeight != 0;
361
348
 
362
349
  if (shouldComposite && !sharp::HasAlpha(image)) {
363
350
  image = sharp::EnsureAlpha(image, 1);
@@ -366,26 +353,27 @@ class PipelineWorker : public Napi::AsyncWorker {
366
353
  bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
367
354
  (shouldResize || shouldBlur || shouldConv || shouldSharpen);
368
355
 
369
- // Premultiply image alpha channel before all transformations to avoid
370
- // dark fringing around bright pixels
371
- // See: http://entropymine.com/imageworsener/resizealpha/
372
356
  if (shouldPremultiplyAlpha) {
373
357
  image = image.premultiply();
374
358
  }
375
359
 
376
360
  // Resize
377
361
  if (shouldResize) {
378
- VipsKernel kernel = static_cast<VipsKernel>(
379
- vips_enum_from_nick(nullptr, VIPS_TYPE_KERNEL, baton->kernel.data()));
380
- if (
381
- kernel != VIPS_KERNEL_NEAREST && kernel != VIPS_KERNEL_CUBIC && kernel != VIPS_KERNEL_LANCZOS2 &&
382
- kernel != VIPS_KERNEL_LANCZOS3 && kernel != VIPS_KERNEL_MITCHELL
383
- ) {
384
- throw vips::VError("Unknown kernel");
385
- }
386
362
  image = image.resize(1.0 / hshrink, VImage::option()
387
363
  ->set("vscale", 1.0 / vshrink)
388
- ->set("kernel", kernel));
364
+ ->set("kernel", baton->kernel));
365
+ }
366
+
367
+ // Flip (mirror about Y axis)
368
+ if (baton->flip || flip) {
369
+ image = image.flip(VIPS_DIRECTION_VERTICAL);
370
+ image = sharp::RemoveExifOrientation(image);
371
+ }
372
+
373
+ // Flop (mirror about X axis)
374
+ if (baton->flop || flop) {
375
+ image = image.flip(VIPS_DIRECTION_HORIZONTAL);
376
+ image = sharp::RemoveExifOrientation(image);
389
377
  }
390
378
 
391
379
  // Rotate post-extract 90-angle
@@ -402,18 +390,6 @@ class PipelineWorker : public Napi::AsyncWorker {
402
390
  image = sharp::RemoveExifOrientation(image);
403
391
  }
404
392
 
405
- // Flip (mirror about Y axis)
406
- if (baton->flip || flip) {
407
- image = image.flip(VIPS_DIRECTION_VERTICAL);
408
- image = sharp::RemoveExifOrientation(image);
409
- }
410
-
411
- // Flop (mirror about X axis)
412
- if (baton->flop || flop) {
413
- image = image.flip(VIPS_DIRECTION_HORIZONTAL);
414
- image = sharp::RemoveExifOrientation(image);
415
- }
416
-
417
393
  // Join additional color channels to the image
418
394
  if (baton->joinChannelIn.size() > 0) {
419
395
  VImage joinImage;
@@ -445,18 +421,12 @@ class PipelineWorker : public Napi::AsyncWorker {
445
421
  std::tie(image, background) = sharp::ApplyAlpha(image, baton->resizeBackground, shouldPremultiplyAlpha);
446
422
 
447
423
  // Embed
448
-
449
- // Calculate where to position the embedded image if gravity specified, else center.
450
424
  int left;
451
425
  int top;
452
-
453
- left = static_cast<int>(round((baton->width - inputWidth) / 2));
454
- top = static_cast<int>(round((baton->height - inputHeight) / 2));
455
-
456
- int width = std::max(inputWidth, baton->width);
457
- int height = std::max(inputHeight, baton->height);
458
426
  std::tie(left, top) = sharp::CalculateEmbedPosition(
459
427
  inputWidth, inputHeight, baton->width, baton->height, baton->position);
428
+ int width = std::max(inputWidth, baton->width);
429
+ int height = std::max(inputHeight, baton->height);
460
430
 
461
431
  image = nPages > 1
462
432
  ? sharp::EmbedMultiPage(image,
@@ -555,7 +525,7 @@ class PipelineWorker : public Napi::AsyncWorker {
555
525
  VImage::option()->set("extend", VIPS_EXTEND_BACKGROUND)->set("background", background));
556
526
  }
557
527
  // Median - must happen before blurring, due to the utility of blurring after thresholding
558
- if (shouldApplyMedian) {
528
+ if (baton->medianSize > 0) {
559
529
  image = image.median(baton->medianSize);
560
530
  }
561
531
  // Threshold - must happen before blurring, due to the utility of blurring after thresholding
@@ -581,7 +551,8 @@ class PipelineWorker : public Napi::AsyncWorker {
581
551
  image = sharp::Recomb(image, baton->recombMatrix);
582
552
  }
583
553
 
584
- if (shouldModulate) {
554
+ // Modulate
555
+ if (baton->brightness != 1.0 || baton->saturation != 1.0 || baton->hue != 0.0 || baton->lightness != 0.0) {
585
556
  image = sharp::Modulate(image, baton->brightness, baton->saturation, baton->hue, baton->lightness);
586
557
  }
587
558
 
@@ -591,6 +562,18 @@ class PipelineWorker : public Napi::AsyncWorker {
591
562
  baton->sharpenX1, baton->sharpenY2, baton->sharpenY3);
592
563
  }
593
564
 
565
+ // Reverse premultiplication after all transformations
566
+ if (shouldPremultiplyAlpha) {
567
+ image = image.unpremultiply();
568
+ // Cast pixel values to integer
569
+ if (sharp::Is16Bit(image.interpretation())) {
570
+ image = image.cast(VIPS_FORMAT_USHORT);
571
+ } else {
572
+ image = image.cast(VIPS_FORMAT_UCHAR);
573
+ }
574
+ }
575
+ baton->premultiplied = shouldPremultiplyAlpha;
576
+
594
577
  // Composite
595
578
  if (shouldComposite) {
596
579
  std::vector<VImage> images = { image };
@@ -668,28 +651,16 @@ class PipelineWorker : public Napi::AsyncWorker {
668
651
  xs.push_back(left);
669
652
  ys.push_back(top);
670
653
  }
671
- image = image.composite(images, modes, VImage::option()->set("x", xs)->set("y", ys));
654
+ image = VImage::composite(images, modes, VImage::option()->set("x", xs)->set("y", ys));
672
655
  }
673
656
 
674
- // Reverse premultiplication after all transformations:
675
- if (shouldPremultiplyAlpha) {
676
- image = image.unpremultiply();
677
- // Cast pixel values to integer
678
- if (sharp::Is16Bit(image.interpretation())) {
679
- image = image.cast(VIPS_FORMAT_USHORT);
680
- } else {
681
- image = image.cast(VIPS_FORMAT_UCHAR);
682
- }
683
- }
684
- baton->premultiplied = shouldPremultiplyAlpha;
685
-
686
657
  // Gamma decoding (brighten)
687
658
  if (baton->gammaOut >= 1 && baton->gammaOut <= 3) {
688
659
  image = sharp::Gamma(image, baton->gammaOut);
689
660
  }
690
661
 
691
662
  // Linear adjustment (a * in + b)
692
- if (baton->linearA != 1.0 || baton->linearB != 0.0) {
663
+ if (!baton->linearA.empty()) {
693
664
  image = sharp::Linear(image, baton->linearA, baton->linearB);
694
665
  }
695
666
 
@@ -699,7 +670,7 @@ class PipelineWorker : public Napi::AsyncWorker {
699
670
  }
700
671
 
701
672
  // Apply contrast limiting adaptive histogram equalization (CLAHE)
702
- if (shouldApplyClahe) {
673
+ if (baton->claheWidth != 0 && baton->claheHeight != 0) {
703
674
  image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
704
675
  }
705
676
 
@@ -873,6 +844,8 @@ class PipelineWorker : public Napi::AsyncWorker {
873
844
  ->set("near_lossless", baton->webpNearLossless)
874
845
  ->set("smart_subsample", baton->webpSmartSubsample)
875
846
  ->set("effort", baton->webpEffort)
847
+ ->set("min_size", baton->webpMinSize)
848
+ ->set("mixed", baton->webpMixed)
876
849
  ->set("alpha_q", baton->webpAlphaQuality)));
877
850
  baton->bufferOut = static_cast<char*>(area->data);
878
851
  baton->bufferOutLength = area->length;
@@ -887,6 +860,7 @@ class PipelineWorker : public Napi::AsyncWorker {
887
860
  ->set("strip", !baton->withMetadata)
888
861
  ->set("bitdepth", baton->gifBitdepth)
889
862
  ->set("effort", baton->gifEffort)
863
+ ->set("reoptimise", baton->gifReoptimise)
890
864
  ->set("dither", baton->gifDither)));
891
865
  baton->bufferOut = static_cast<char*>(area->data);
892
866
  baton->bufferOutLength = area->length;
@@ -939,6 +913,19 @@ class PipelineWorker : public Napi::AsyncWorker {
939
913
  area->free_fn = nullptr;
940
914
  vips_area_unref(area);
941
915
  baton->formatOut = "heif";
916
+ } else if (baton->formatOut == "dz") {
917
+ // Write DZ to buffer
918
+ baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
919
+ if (!sharp::HasAlpha(image)) {
920
+ baton->tileBackground.pop_back();
921
+ }
922
+ vips::VOption *options = BuildOptionsDZ(baton);
923
+ VipsArea *area = reinterpret_cast<VipsArea*>(image.dzsave_buffer(options));
924
+ baton->bufferOut = static_cast<char*>(area->data);
925
+ baton->bufferOutLength = area->length;
926
+ area->free_fn = nullptr;
927
+ vips_area_unref(area);
928
+ baton->formatOut = "dz";
942
929
  } else if (baton->formatOut == "raw" ||
943
930
  (baton->formatOut == "input" && inputImageType == sharp::ImageType::RAW)) {
944
931
  // Write raw, uncompressed image data to buffer
@@ -1040,6 +1027,8 @@ class PipelineWorker : public Napi::AsyncWorker {
1040
1027
  ->set("near_lossless", baton->webpNearLossless)
1041
1028
  ->set("smart_subsample", baton->webpSmartSubsample)
1042
1029
  ->set("effort", baton->webpEffort)
1030
+ ->set("min_size", baton->webpMinSize)
1031
+ ->set("mixed", baton->webpMixed)
1043
1032
  ->set("alpha_q", baton->webpAlphaQuality));
1044
1033
  baton->formatOut = "webp";
1045
1034
  } else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
@@ -1050,6 +1039,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1050
1039
  ->set("strip", !baton->withMetadata)
1051
1040
  ->set("bitdepth", baton->gifBitdepth)
1052
1041
  ->set("effort", baton->gifEffort)
1042
+ ->set("reoptimise", baton->gifReoptimise)
1053
1043
  ->set("dither", baton->gifDither));
1054
1044
  baton->formatOut = "gif";
1055
1045
  } else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
@@ -1091,64 +1081,14 @@ class PipelineWorker : public Napi::AsyncWorker {
1091
1081
  ->set("lossless", baton->heifLossless));
1092
1082
  baton->formatOut = "heif";
1093
1083
  } else if (baton->formatOut == "dz" || isDz || isDzZip) {
1084
+ // Write DZ to file
1094
1085
  if (isDzZip) {
1095
1086
  baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
1096
1087
  }
1097
- // Forward format options through suffix
1098
- std::string suffix;
1099
- if (baton->tileFormat == "png") {
1100
- std::vector<std::pair<std::string, std::string>> options {
1101
- {"interlace", baton->pngProgressive ? "TRUE" : "FALSE"},
1102
- {"compression", std::to_string(baton->pngCompressionLevel)},
1103
- {"filter", baton->pngAdaptiveFiltering ? "all" : "none"}
1104
- };
1105
- suffix = AssembleSuffixString(".png", options);
1106
- } else if (baton->tileFormat == "webp") {
1107
- std::vector<std::pair<std::string, std::string>> options {
1108
- {"Q", std::to_string(baton->webpQuality)},
1109
- {"alpha_q", std::to_string(baton->webpAlphaQuality)},
1110
- {"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
1111
- {"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
1112
- {"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
1113
- {"effort", std::to_string(baton->webpEffort)}
1114
- };
1115
- suffix = AssembleSuffixString(".webp", options);
1116
- } else {
1117
- std::vector<std::pair<std::string, std::string>> options {
1118
- {"Q", std::to_string(baton->jpegQuality)},
1119
- {"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
1120
- {"subsample_mode", baton->jpegChromaSubsampling == "4:4:4" ? "off" : "on"},
1121
- {"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"},
1122
- {"quant_table", std::to_string(baton->jpegQuantisationTable)},
1123
- {"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"},
1124
- {"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"},
1125
- {"optimize_coding", baton->jpegOptimiseCoding ? "TRUE": "FALSE"}
1126
- };
1127
- std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_DZ ? ".jpeg" : ".jpg";
1128
- suffix = AssembleSuffixString(extname, options);
1129
- }
1130
- // Remove alpha channel from tile background if image does not contain an alpha channel
1131
1088
  if (!sharp::HasAlpha(image)) {
1132
1089
  baton->tileBackground.pop_back();
1133
1090
  }
1134
- // Write DZ to file
1135
- vips::VOption *options = VImage::option()
1136
- ->set("strip", !baton->withMetadata)
1137
- ->set("tile_size", baton->tileSize)
1138
- ->set("overlap", baton->tileOverlap)
1139
- ->set("container", baton->tileContainer)
1140
- ->set("layout", baton->tileLayout)
1141
- ->set("suffix", const_cast<char*>(suffix.data()))
1142
- ->set("angle", CalculateAngleRotation(baton->tileAngle))
1143
- ->set("background", baton->tileBackground)
1144
- ->set("centre", baton->tileCentre)
1145
- ->set("id", const_cast<char*>(baton->tileId.data()))
1146
- ->set("skip_blanks", baton->tileSkipBlanks);
1147
- // libvips chooses a default depth based on layout. Instead of replicating that logic here by
1148
- // not passing anything - libvips will handle choice
1149
- if (baton->tileDepth < VIPS_FOREIGN_DZ_DEPTH_LAST) {
1150
- options->set("depth", baton->tileDepth);
1151
- }
1091
+ vips::VOption *options = BuildOptionsDZ(baton);
1152
1092
  image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
1153
1093
  baton->formatOut = "dz";
1154
1094
  } else if (baton->formatOut == "v" || (mightMatchInput && isV) ||
@@ -1217,6 +1157,10 @@ class PipelineWorker : public Napi::AsyncWorker {
1217
1157
  info.Set("trimOffsetTop", static_cast<int32_t>(baton->trimOffsetTop));
1218
1158
  }
1219
1159
 
1160
+ if (baton->input->textAutofitDpi) {
1161
+ info.Set("textAutofitDpi", static_cast<uint32_t>(baton->input->textAutofitDpi));
1162
+ }
1163
+
1220
1164
  if (baton->bufferOutLength > 0) {
1221
1165
  // Add buffer size to info
1222
1166
  info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
@@ -1305,7 +1249,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1305
1249
 
1306
1250
  /*
1307
1251
  Assemble the suffix argument to dzsave, which is the format (by extname)
1308
- alongisde comma-separated arguments to the corresponding `formatsave` vips
1252
+ alongside comma-separated arguments to the corresponding `formatsave` vips
1309
1253
  action.
1310
1254
  */
1311
1255
  std::string
@@ -1320,6 +1264,67 @@ class PipelineWorker : public Napi::AsyncWorker {
1320
1264
  return extname + "[" + argument + "]";
1321
1265
  }
1322
1266
 
1267
+ /*
1268
+ Build VOption for dzsave
1269
+ */
1270
+ vips::VOption*
1271
+ BuildOptionsDZ(PipelineBaton *baton) {
1272
+ // Forward format options through suffix
1273
+ std::string suffix;
1274
+ if (baton->tileFormat == "png") {
1275
+ std::vector<std::pair<std::string, std::string>> options {
1276
+ {"interlace", baton->pngProgressive ? "TRUE" : "FALSE"},
1277
+ {"compression", std::to_string(baton->pngCompressionLevel)},
1278
+ {"filter", baton->pngAdaptiveFiltering ? "all" : "none"}
1279
+ };
1280
+ suffix = AssembleSuffixString(".png", options);
1281
+ } else if (baton->tileFormat == "webp") {
1282
+ std::vector<std::pair<std::string, std::string>> options {
1283
+ {"Q", std::to_string(baton->webpQuality)},
1284
+ {"alpha_q", std::to_string(baton->webpAlphaQuality)},
1285
+ {"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
1286
+ {"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
1287
+ {"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
1288
+ {"min_size", baton->webpMinSize ? "TRUE" : "FALSE"},
1289
+ {"mixed", baton->webpMixed ? "TRUE" : "FALSE"},
1290
+ {"effort", std::to_string(baton->webpEffort)}
1291
+ };
1292
+ suffix = AssembleSuffixString(".webp", options);
1293
+ } else {
1294
+ std::vector<std::pair<std::string, std::string>> options {
1295
+ {"Q", std::to_string(baton->jpegQuality)},
1296
+ {"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
1297
+ {"subsample_mode", baton->jpegChromaSubsampling == "4:4:4" ? "off" : "on"},
1298
+ {"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"},
1299
+ {"quant_table", std::to_string(baton->jpegQuantisationTable)},
1300
+ {"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"},
1301
+ {"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"},
1302
+ {"optimize_coding", baton->jpegOptimiseCoding ? "TRUE": "FALSE"}
1303
+ };
1304
+ std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_DZ ? ".jpeg" : ".jpg";
1305
+ suffix = AssembleSuffixString(extname, options);
1306
+ }
1307
+ vips::VOption *options = VImage::option()
1308
+ ->set("strip", !baton->withMetadata)
1309
+ ->set("tile_size", baton->tileSize)
1310
+ ->set("overlap", baton->tileOverlap)
1311
+ ->set("container", baton->tileContainer)
1312
+ ->set("layout", baton->tileLayout)
1313
+ ->set("suffix", const_cast<char*>(suffix.data()))
1314
+ ->set("angle", CalculateAngleRotation(baton->tileAngle))
1315
+ ->set("background", baton->tileBackground)
1316
+ ->set("centre", baton->tileCentre)
1317
+ ->set("id", const_cast<char*>(baton->tileId.data()))
1318
+ ->set("skip_blanks", baton->tileSkipBlanks);
1319
+ if (baton->tileDepth < VIPS_FOREIGN_DZ_DEPTH_LAST) {
1320
+ options->set("depth", baton->tileDepth);
1321
+ }
1322
+ if (!baton->tileBasename.empty()) {
1323
+ options->set("basename", const_cast<char*>(baton->tileBasename.data()));
1324
+ }
1325
+ return options;
1326
+ }
1327
+
1323
1328
  /*
1324
1329
  Clear all thread-local data.
1325
1330
  */
@@ -1365,17 +1370,13 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1365
1370
  } else if (canvas == "ignore_aspect") {
1366
1371
  baton->canvas = sharp::Canvas::IGNORE_ASPECT;
1367
1372
  }
1368
- // Tint chroma
1369
- baton->tintA = sharp::AttrAsDouble(options, "tintA");
1370
- baton->tintB = sharp::AttrAsDouble(options, "tintB");
1371
1373
  // Composite
1372
1374
  Napi::Array compositeArray = options.Get("composite").As<Napi::Array>();
1373
1375
  for (unsigned int i = 0; i < compositeArray.Length(); i++) {
1374
1376
  Napi::Object compositeObject = compositeArray.Get(i).As<Napi::Object>();
1375
1377
  Composite *composite = new Composite;
1376
1378
  composite->input = sharp::CreateInputDescriptor(compositeObject.Get("input").As<Napi::Object>());
1377
- composite->mode = static_cast<VipsBlendMode>(
1378
- vips_enum_from_nick(nullptr, VIPS_TYPE_BLEND_MODE, sharp::AttrAsStr(compositeObject, "blend").data()));
1379
+ composite->mode = sharp::AttrAsEnum<VipsBlendMode>(compositeObject, "blend", VIPS_TYPE_BLEND_MODE);
1379
1380
  composite->gravity = sharp::AttrAsUint32(compositeObject, "gravity");
1380
1381
  composite->left = sharp::AttrAsInt32(compositeObject, "left");
1381
1382
  composite->top = sharp::AttrAsInt32(compositeObject, "top");
@@ -1389,7 +1390,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1389
1390
  baton->withoutReduction = sharp::AttrAsBool(options, "withoutReduction");
1390
1391
  baton->position = sharp::AttrAsInt32(options, "position");
1391
1392
  baton->resizeBackground = sharp::AttrAsVectorOfDouble(options, "resizeBackground");
1392
- baton->kernel = sharp::AttrAsStr(options, "kernel");
1393
+ baton->kernel = sharp::AttrAsEnum<VipsKernel>(options, "kernel", VIPS_TYPE_KERNEL);
1393
1394
  baton->fastShrinkOnLoad = sharp::AttrAsBool(options, "fastShrinkOnLoad");
1394
1395
  // Join Channel Options
1395
1396
  if (options.Has("joinChannelIn")) {
@@ -1418,13 +1419,16 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1418
1419
  baton->sharpenY3 = sharp::AttrAsDouble(options, "sharpenY3");
1419
1420
  baton->threshold = sharp::AttrAsInt32(options, "threshold");
1420
1421
  baton->thresholdGrayscale = sharp::AttrAsBool(options, "thresholdGrayscale");
1422
+ baton->trimBackground = sharp::AttrAsVectorOfDouble(options, "trimBackground");
1421
1423
  baton->trimThreshold = sharp::AttrAsDouble(options, "trimThreshold");
1422
1424
  baton->gamma = sharp::AttrAsDouble(options, "gamma");
1423
1425
  baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
1424
- baton->linearA = sharp::AttrAsDouble(options, "linearA");
1425
- baton->linearB = sharp::AttrAsDouble(options, "linearB");
1426
+ baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA");
1427
+ baton->linearB = sharp::AttrAsVectorOfDouble(options, "linearB");
1426
1428
  baton->greyscale = sharp::AttrAsBool(options, "greyscale");
1427
1429
  baton->normalise = sharp::AttrAsBool(options, "normalise");
1430
+ baton->tintA = sharp::AttrAsDouble(options, "tintA");
1431
+ baton->tintB = sharp::AttrAsDouble(options, "tintB");
1428
1432
  baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
1429
1433
  baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight");
1430
1434
  baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope");
@@ -1452,10 +1456,10 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1452
1456
  baton->ensureAlpha = sharp::AttrAsDouble(options, "ensureAlpha");
1453
1457
  if (options.Has("boolean")) {
1454
1458
  baton->boolean = sharp::CreateInputDescriptor(options.Get("boolean").As<Napi::Object>());
1455
- baton->booleanOp = sharp::GetBooleanOperation(sharp::AttrAsStr(options, "booleanOp"));
1459
+ baton->booleanOp = sharp::AttrAsEnum<VipsOperationBoolean>(options, "booleanOp", VIPS_TYPE_OPERATION_BOOLEAN);
1456
1460
  }
1457
1461
  if (options.Has("bandBoolOp")) {
1458
- baton->bandBoolOp = sharp::GetBooleanOperation(sharp::AttrAsStr(options, "bandBoolOp"));
1462
+ baton->bandBoolOp = sharp::AttrAsEnum<VipsOperationBoolean>(options, "bandBoolOp", VIPS_TYPE_OPERATION_BOOLEAN);
1459
1463
  }
1460
1464
  if (options.Has("convKernel")) {
1461
1465
  Napi::Object kernel = options.Get("convKernel").As<Napi::Object>();
@@ -1477,11 +1481,12 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1477
1481
  baton->recombMatrix[i] = sharp::AttrAsDouble(recombMatrix, i);
1478
1482
  }
1479
1483
  }
1480
- baton->colourspaceInput = sharp::GetInterpretation(sharp::AttrAsStr(options, "colourspaceInput"));
1484
+ baton->colourspaceInput = sharp::AttrAsEnum<VipsInterpretation>(
1485
+ options, "colourspaceInput", VIPS_TYPE_INTERPRETATION);
1481
1486
  if (baton->colourspaceInput == VIPS_INTERPRETATION_ERROR) {
1482
1487
  baton->colourspaceInput = VIPS_INTERPRETATION_LAST;
1483
1488
  }
1484
- baton->colourspace = sharp::GetInterpretation(sharp::AttrAsStr(options, "colourspace"));
1489
+ baton->colourspace = sharp::AttrAsEnum<VipsInterpretation>(options, "colourspace", VIPS_TYPE_INTERPRETATION);
1485
1490
  if (baton->colourspace == VIPS_INTERPRETATION_ERROR) {
1486
1491
  baton->colourspace = VIPS_INTERPRETATION_sRGB;
1487
1492
  }
@@ -1496,7 +1501,9 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1496
1501
  Napi::Array mdStrKeys = mdStrs.GetPropertyNames();
1497
1502
  for (unsigned int i = 0; i < mdStrKeys.Length(); i++) {
1498
1503
  std::string k = sharp::AttrAsStr(mdStrKeys, i);
1499
- baton->withMetadataStrs.insert(std::make_pair(k, sharp::AttrAsStr(mdStrs, k)));
1504
+ if (mdStrs.HasOwnProperty(k)) {
1505
+ baton->withMetadataStrs.insert(std::make_pair(k, sharp::AttrAsStr(mdStrs, k)));
1506
+ }
1500
1507
  }
1501
1508
  baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
1502
1509
  // Format-specific
@@ -1527,9 +1534,12 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1527
1534
  baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
1528
1535
  baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
1529
1536
  baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
1537
+ baton->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
1538
+ baton->webpMixed = sharp::AttrAsBool(options, "webpMixed");
1530
1539
  baton->gifBitdepth = sharp::AttrAsUint32(options, "gifBitdepth");
1531
1540
  baton->gifEffort = sharp::AttrAsUint32(options, "gifEffort");
1532
1541
  baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
1542
+ baton->gifReoptimise = sharp::AttrAsBool(options, "gifReoptimise");
1533
1543
  baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
1534
1544
  baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
1535
1545
  baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
@@ -1541,28 +1551,19 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1541
1551
  if (baton->tiffXres == 1.0 && baton->tiffYres == 1.0 && baton->withMetadataDensity > 0) {
1542
1552
  baton->tiffXres = baton->tiffYres = baton->withMetadataDensity / 25.4;
1543
1553
  }
1544
- // tiff compression options
1545
- baton->tiffCompression = static_cast<VipsForeignTiffCompression>(
1546
- vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_COMPRESSION,
1547
- sharp::AttrAsStr(options, "tiffCompression").data()));
1548
- baton->tiffPredictor = static_cast<VipsForeignTiffPredictor>(
1549
- vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_PREDICTOR,
1550
- sharp::AttrAsStr(options, "tiffPredictor").data()));
1551
- baton->tiffResolutionUnit = static_cast<VipsForeignTiffResunit>(
1552
- vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_RESUNIT,
1553
- sharp::AttrAsStr(options, "tiffResolutionUnit").data()));
1554
-
1554
+ baton->tiffCompression = sharp::AttrAsEnum<VipsForeignTiffCompression>(
1555
+ options, "tiffCompression", VIPS_TYPE_FOREIGN_TIFF_COMPRESSION);
1556
+ baton->tiffPredictor = sharp::AttrAsEnum<VipsForeignTiffPredictor>(
1557
+ options, "tiffPredictor", VIPS_TYPE_FOREIGN_TIFF_PREDICTOR);
1558
+ baton->tiffResolutionUnit = sharp::AttrAsEnum<VipsForeignTiffResunit>(
1559
+ options, "tiffResolutionUnit", VIPS_TYPE_FOREIGN_TIFF_RESUNIT);
1555
1560
  baton->heifQuality = sharp::AttrAsUint32(options, "heifQuality");
1556
1561
  baton->heifLossless = sharp::AttrAsBool(options, "heifLossless");
1557
- baton->heifCompression = static_cast<VipsForeignHeifCompression>(
1558
- vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
1559
- sharp::AttrAsStr(options, "heifCompression").data()));
1562
+ baton->heifCompression = sharp::AttrAsEnum<VipsForeignHeifCompression>(
1563
+ options, "heifCompression", VIPS_TYPE_FOREIGN_HEIF_COMPRESSION);
1560
1564
  baton->heifEffort = sharp::AttrAsUint32(options, "heifEffort");
1561
1565
  baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
1562
- // Raw output
1563
- baton->rawDepth = static_cast<VipsBandFormat>(
1564
- vips_enum_from_nick(nullptr, VIPS_TYPE_BAND_FORMAT,
1565
- sharp::AttrAsStr(options, "rawDepth").data()));
1566
+ baton->rawDepth = sharp::AttrAsEnum<VipsBandFormat>(options, "rawDepth", VIPS_TYPE_BAND_FORMAT);
1566
1567
  // Animated output properties
1567
1568
  if (sharp::HasAttr(options, "loop")) {
1568
1569
  baton->loop = sharp::AttrAsUint32(options, "loop");
@@ -1570,24 +1571,19 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1570
1571
  if (sharp::HasAttr(options, "delay")) {
1571
1572
  baton->delay = sharp::AttrAsInt32Vector(options, "delay");
1572
1573
  }
1573
- // Tile output
1574
1574
  baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
1575
1575
  baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
1576
1576
  baton->tileAngle = sharp::AttrAsInt32(options, "tileAngle");
1577
1577
  baton->tileBackground = sharp::AttrAsVectorOfDouble(options, "tileBackground");
1578
1578
  baton->tileSkipBlanks = sharp::AttrAsInt32(options, "tileSkipBlanks");
1579
- baton->tileContainer = static_cast<VipsForeignDzContainer>(
1580
- vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_CONTAINER,
1581
- sharp::AttrAsStr(options, "tileContainer").data()));
1582
- baton->tileLayout = static_cast<VipsForeignDzLayout>(
1583
- vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_LAYOUT,
1584
- sharp::AttrAsStr(options, "tileLayout").data()));
1579
+ baton->tileContainer = sharp::AttrAsEnum<VipsForeignDzContainer>(
1580
+ options, "tileContainer", VIPS_TYPE_FOREIGN_DZ_CONTAINER);
1581
+ baton->tileLayout = sharp::AttrAsEnum<VipsForeignDzLayout>(options, "tileLayout", VIPS_TYPE_FOREIGN_DZ_LAYOUT);
1585
1582
  baton->tileFormat = sharp::AttrAsStr(options, "tileFormat");
1586
- baton->tileDepth = static_cast<VipsForeignDzDepth>(
1587
- vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_DEPTH,
1588
- sharp::AttrAsStr(options, "tileDepth").data()));
1583
+ baton->tileDepth = sharp::AttrAsEnum<VipsForeignDzDepth>(options, "tileDepth", VIPS_TYPE_FOREIGN_DZ_DEPTH);
1589
1584
  baton->tileCentre = sharp::AttrAsBool(options, "tileCentre");
1590
1585
  baton->tileId = sharp::AttrAsStr(options, "tileId");
1586
+ baton->tileBasename = sharp::AttrAsStr(options, "tileBasename");
1591
1587
 
1592
1588
  // Force random access for certain operations
1593
1589
  if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) {
package/src/pipeline.h CHANGED
@@ -67,6 +67,7 @@ struct PipelineBaton {
67
67
  int width;
68
68
  int height;
69
69
  int channels;
70
+ VipsKernel kernel;
70
71
  sharp::Canvas canvas;
71
72
  int position;
72
73
  std::vector<double> resizeBackground;
@@ -75,7 +76,6 @@ struct PipelineBaton {
75
76
  int cropOffsetTop;
76
77
  bool premultiplied;
77
78
  bool tileCentre;
78
- std::string kernel;
79
79
  bool fastShrinkOnLoad;
80
80
  double tintA;
81
81
  double tintB;
@@ -97,11 +97,12 @@ struct PipelineBaton {
97
97
  double sharpenY3;
98
98
  int threshold;
99
99
  bool thresholdGrayscale;
100
+ std::vector<double> trimBackground;
100
101
  double trimThreshold;
101
102
  int trimOffsetLeft;
102
103
  int trimOffsetTop;
103
- double linearA;
104
- double linearB;
104
+ std::vector<double> linearA;
105
+ std::vector<double> linearB;
105
106
  double gamma;
106
107
  double gammaOut;
107
108
  bool greyscale;
@@ -157,9 +158,12 @@ struct PipelineBaton {
157
158
  bool webpLossless;
158
159
  bool webpSmartSubsample;
159
160
  int webpEffort;
161
+ bool webpMinSize;
162
+ bool webpMixed;
160
163
  int gifBitdepth;
161
164
  int gifEffort;
162
165
  double gifDither;
166
+ bool gifReoptimise;
163
167
  int tiffQuality;
164
168
  VipsForeignTiffCompression tiffCompression;
165
169
  VipsForeignTiffPredictor tiffPredictor;
@@ -209,6 +213,7 @@ struct PipelineBaton {
209
213
  int tileSkipBlanks;
210
214
  VipsForeignDzDepth tileDepth;
211
215
  std::string tileId;
216
+ std::string tileBasename;
212
217
  std::unique_ptr<double[]> recombMatrix;
213
218
 
214
219
  PipelineBaton():
@@ -217,6 +222,7 @@ struct PipelineBaton {
217
222
  topOffsetPre(-1),
218
223
  topOffsetPost(-1),
219
224
  channels(0),
225
+ kernel(VIPS_KERNEL_LANCZOS3),
220
226
  canvas(sharp::Canvas::CROP),
221
227
  position(0),
222
228
  resizeBackground{ 0.0, 0.0, 0.0, 255.0 },
@@ -244,11 +250,12 @@ struct PipelineBaton {
244
250
  sharpenY3(20.0),
245
251
  threshold(0),
246
252
  thresholdGrayscale(true),
253
+ trimBackground{},
247
254
  trimThreshold(0.0),
248
255
  trimOffsetLeft(0),
249
256
  trimOffsetTop(0),
250
- linearA(1.0),
251
- linearB(0.0),
257
+ linearA{},
258
+ linearB{},
252
259
  gamma(0.0),
253
260
  greyscale(false),
254
261
  normalise(false),
@@ -302,6 +309,12 @@ struct PipelineBaton {
302
309
  webpLossless(false),
303
310
  webpSmartSubsample(false),
304
311
  webpEffort(4),
312
+ webpMinSize(false),
313
+ webpMixed(false),
314
+ gifBitdepth(8),
315
+ gifEffort(7),
316
+ gifDither(1.0),
317
+ gifReoptimise(false),
305
318
  tiffQuality(80),
306
319
  tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
307
320
  tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),