sharp 0.30.5 → 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/README.md +1 -1
- package/binding.gyp +4 -3
- package/lib/composite.js +14 -0
- package/lib/constructor.js +41 -4
- package/lib/input.js +95 -3
- package/lib/libvips.js +1 -0
- package/lib/operation.js +57 -12
- package/lib/output.js +87 -23
- package/lib/resize.js +90 -13
- package/lib/sharp.js +3 -0
- package/lib/utility.js +4 -0
- package/package.json +25 -23
- package/src/common.cc +95 -45
- package/src/common.h +33 -15
- package/src/libvips/cplusplus/VConnection.cpp +0 -1
- package/src/libvips/cplusplus/VError.cpp +0 -1
- package/src/libvips/cplusplus/VImage.cpp +27 -2
- package/src/libvips/cplusplus/VInterpolate.cpp +0 -1
- package/src/libvips/cplusplus/VRegion.cpp +27 -0
- package/src/libvips/cplusplus/vips-operators.cpp +16 -1
- package/src/operations.cc +69 -25
- package/src/operations.h +7 -2
- package/src/pipeline.cc +162 -164
- package/src/pipeline.h +18 -5
- package/src/sharp.cc +0 -1
- package/src/utilities.cc +13 -2
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
|
}
|
|
@@ -188,8 +188,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
188
188
|
if (jpegShrinkOnLoad > 1 && static_cast<int>(shrink) == jpegShrinkOnLoad) {
|
|
189
189
|
jpegShrinkOnLoad /= 2;
|
|
190
190
|
}
|
|
191
|
-
} else if (inputImageType == sharp::ImageType::WEBP
|
|
192
|
-
|
|
191
|
+
} else if (inputImageType == sharp::ImageType::WEBP && shrink > 1.0) {
|
|
192
|
+
// Avoid upscaling via webp
|
|
193
|
+
scale = 1.0 / shrink;
|
|
194
|
+
} else if (inputImageType == sharp::ImageType::SVG ||
|
|
193
195
|
inputImageType == sharp::ImageType::PDF) {
|
|
194
196
|
scale = 1.0 / shrink;
|
|
195
197
|
}
|
|
@@ -202,6 +204,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
202
204
|
vips::VOption *option = VImage::option()
|
|
203
205
|
->set("access", baton->input->access)
|
|
204
206
|
->set("shrink", jpegShrinkOnLoad)
|
|
207
|
+
->set("unlimited", baton->input->unlimited)
|
|
205
208
|
->set("fail_on", baton->input->failOn);
|
|
206
209
|
if (baton->input->buffer != nullptr) {
|
|
207
210
|
// Reload JPEG buffer
|
|
@@ -300,8 +303,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
300
303
|
if (
|
|
301
304
|
sharp::HasProfile(image) &&
|
|
302
305
|
image.interpretation() != VIPS_INTERPRETATION_LABS &&
|
|
303
|
-
image.interpretation() != VIPS_INTERPRETATION_GREY16
|
|
304
|
-
image.interpretation() != VIPS_INTERPRETATION_B_W
|
|
306
|
+
image.interpretation() != VIPS_INTERPRETATION_GREY16
|
|
305
307
|
) {
|
|
306
308
|
// Convert to sRGB/P3 using embedded profile
|
|
307
309
|
try {
|
|
@@ -320,16 +322,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
320
322
|
|
|
321
323
|
// Flatten image to remove alpha channel
|
|
322
324
|
if (baton->flatten && sharp::HasAlpha(image)) {
|
|
323
|
-
|
|
324
|
-
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
|
|
325
|
-
// Background colour
|
|
326
|
-
std::vector<double> background {
|
|
327
|
-
baton->flattenBackground[0] * multiplier,
|
|
328
|
-
baton->flattenBackground[1] * multiplier,
|
|
329
|
-
baton->flattenBackground[2] * multiplier
|
|
330
|
-
};
|
|
331
|
-
image = image.flatten(VImage::option()
|
|
332
|
-
->set("background", background));
|
|
325
|
+
image = sharp::Flatten(image, baton->flattenBackground);
|
|
333
326
|
}
|
|
334
327
|
|
|
335
328
|
// Negate the colours in the image
|
|
@@ -351,11 +344,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
351
344
|
bool const shouldBlur = baton->blurSigma != 0.0;
|
|
352
345
|
bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0;
|
|
353
346
|
bool const shouldSharpen = baton->sharpenSigma != 0.0;
|
|
354
|
-
bool const shouldApplyMedian = baton->medianSize > 0;
|
|
355
347
|
bool const shouldComposite = !baton->composite.empty();
|
|
356
|
-
bool const shouldModulate = baton->brightness != 1.0 || baton->saturation != 1.0 ||
|
|
357
|
-
baton->hue != 0.0 || baton->lightness != 0.0;
|
|
358
|
-
bool const shouldApplyClahe = baton->claheWidth != 0 && baton->claheHeight != 0;
|
|
359
348
|
|
|
360
349
|
if (shouldComposite && !sharp::HasAlpha(image)) {
|
|
361
350
|
image = sharp::EnsureAlpha(image, 1);
|
|
@@ -364,26 +353,27 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
364
353
|
bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
|
|
365
354
|
(shouldResize || shouldBlur || shouldConv || shouldSharpen);
|
|
366
355
|
|
|
367
|
-
// Premultiply image alpha channel before all transformations to avoid
|
|
368
|
-
// dark fringing around bright pixels
|
|
369
|
-
// See: http://entropymine.com/imageworsener/resizealpha/
|
|
370
356
|
if (shouldPremultiplyAlpha) {
|
|
371
357
|
image = image.premultiply();
|
|
372
358
|
}
|
|
373
359
|
|
|
374
360
|
// Resize
|
|
375
361
|
if (shouldResize) {
|
|
376
|
-
VipsKernel kernel = static_cast<VipsKernel>(
|
|
377
|
-
vips_enum_from_nick(nullptr, VIPS_TYPE_KERNEL, baton->kernel.data()));
|
|
378
|
-
if (
|
|
379
|
-
kernel != VIPS_KERNEL_NEAREST && kernel != VIPS_KERNEL_CUBIC && kernel != VIPS_KERNEL_LANCZOS2 &&
|
|
380
|
-
kernel != VIPS_KERNEL_LANCZOS3 && kernel != VIPS_KERNEL_MITCHELL
|
|
381
|
-
) {
|
|
382
|
-
throw vips::VError("Unknown kernel");
|
|
383
|
-
}
|
|
384
362
|
image = image.resize(1.0 / hshrink, VImage::option()
|
|
385
363
|
->set("vscale", 1.0 / vshrink)
|
|
386
|
-
->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);
|
|
387
377
|
}
|
|
388
378
|
|
|
389
379
|
// Rotate post-extract 90-angle
|
|
@@ -400,18 +390,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
400
390
|
image = sharp::RemoveExifOrientation(image);
|
|
401
391
|
}
|
|
402
392
|
|
|
403
|
-
// Flip (mirror about Y axis)
|
|
404
|
-
if (baton->flip || flip) {
|
|
405
|
-
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
406
|
-
image = sharp::RemoveExifOrientation(image);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// Flop (mirror about X axis)
|
|
410
|
-
if (baton->flop || flop) {
|
|
411
|
-
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
412
|
-
image = sharp::RemoveExifOrientation(image);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
393
|
// Join additional color channels to the image
|
|
416
394
|
if (baton->joinChannelIn.size() > 0) {
|
|
417
395
|
VImage joinImage;
|
|
@@ -443,18 +421,12 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
443
421
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->resizeBackground, shouldPremultiplyAlpha);
|
|
444
422
|
|
|
445
423
|
// Embed
|
|
446
|
-
|
|
447
|
-
// Calculate where to position the embedded image if gravity specified, else center.
|
|
448
424
|
int left;
|
|
449
425
|
int top;
|
|
450
|
-
|
|
451
|
-
left = static_cast<int>(round((baton->width - inputWidth) / 2));
|
|
452
|
-
top = static_cast<int>(round((baton->height - inputHeight) / 2));
|
|
453
|
-
|
|
454
|
-
int width = std::max(inputWidth, baton->width);
|
|
455
|
-
int height = std::max(inputHeight, baton->height);
|
|
456
426
|
std::tie(left, top) = sharp::CalculateEmbedPosition(
|
|
457
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);
|
|
458
430
|
|
|
459
431
|
image = nPages > 1
|
|
460
432
|
? sharp::EmbedMultiPage(image,
|
|
@@ -553,7 +525,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
553
525
|
VImage::option()->set("extend", VIPS_EXTEND_BACKGROUND)->set("background", background));
|
|
554
526
|
}
|
|
555
527
|
// Median - must happen before blurring, due to the utility of blurring after thresholding
|
|
556
|
-
if (
|
|
528
|
+
if (baton->medianSize > 0) {
|
|
557
529
|
image = image.median(baton->medianSize);
|
|
558
530
|
}
|
|
559
531
|
// Threshold - must happen before blurring, due to the utility of blurring after thresholding
|
|
@@ -579,7 +551,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
579
551
|
image = sharp::Recomb(image, baton->recombMatrix);
|
|
580
552
|
}
|
|
581
553
|
|
|
582
|
-
|
|
554
|
+
// Modulate
|
|
555
|
+
if (baton->brightness != 1.0 || baton->saturation != 1.0 || baton->hue != 0.0 || baton->lightness != 0.0) {
|
|
583
556
|
image = sharp::Modulate(image, baton->brightness, baton->saturation, baton->hue, baton->lightness);
|
|
584
557
|
}
|
|
585
558
|
|
|
@@ -589,6 +562,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
589
562
|
baton->sharpenX1, baton->sharpenY2, baton->sharpenY3);
|
|
590
563
|
}
|
|
591
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
|
+
|
|
592
577
|
// Composite
|
|
593
578
|
if (shouldComposite) {
|
|
594
579
|
std::vector<VImage> images = { image };
|
|
@@ -607,14 +592,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
607
592
|
int across = 0;
|
|
608
593
|
int down = 0;
|
|
609
594
|
// Use gravity in overlay
|
|
610
|
-
if (compositeImage.width() <=
|
|
595
|
+
if (compositeImage.width() <= image.width()) {
|
|
611
596
|
across = static_cast<int>(ceil(static_cast<double>(image.width()) / compositeImage.width()));
|
|
612
597
|
// Ensure odd number of tiles across when gravity is centre, north or south
|
|
613
598
|
if (composite->gravity == 0 || composite->gravity == 1 || composite->gravity == 3) {
|
|
614
599
|
across |= 1;
|
|
615
600
|
}
|
|
616
601
|
}
|
|
617
|
-
if (compositeImage.height() <=
|
|
602
|
+
if (compositeImage.height() <= image.height()) {
|
|
618
603
|
down = static_cast<int>(ceil(static_cast<double>(image.height()) / compositeImage.height()));
|
|
619
604
|
// Ensure odd number of tiles down when gravity is centre, east or west
|
|
620
605
|
if (composite->gravity == 0 || composite->gravity == 2 || composite->gravity == 4) {
|
|
@@ -666,28 +651,16 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
666
651
|
xs.push_back(left);
|
|
667
652
|
ys.push_back(top);
|
|
668
653
|
}
|
|
669
|
-
image =
|
|
654
|
+
image = VImage::composite(images, modes, VImage::option()->set("x", xs)->set("y", ys));
|
|
670
655
|
}
|
|
671
656
|
|
|
672
|
-
// Reverse premultiplication after all transformations:
|
|
673
|
-
if (shouldPremultiplyAlpha) {
|
|
674
|
-
image = image.unpremultiply();
|
|
675
|
-
// Cast pixel values to integer
|
|
676
|
-
if (sharp::Is16Bit(image.interpretation())) {
|
|
677
|
-
image = image.cast(VIPS_FORMAT_USHORT);
|
|
678
|
-
} else {
|
|
679
|
-
image = image.cast(VIPS_FORMAT_UCHAR);
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
baton->premultiplied = shouldPremultiplyAlpha;
|
|
683
|
-
|
|
684
657
|
// Gamma decoding (brighten)
|
|
685
658
|
if (baton->gammaOut >= 1 && baton->gammaOut <= 3) {
|
|
686
659
|
image = sharp::Gamma(image, baton->gammaOut);
|
|
687
660
|
}
|
|
688
661
|
|
|
689
662
|
// Linear adjustment (a * in + b)
|
|
690
|
-
if (baton->linearA
|
|
663
|
+
if (!baton->linearA.empty()) {
|
|
691
664
|
image = sharp::Linear(image, baton->linearA, baton->linearB);
|
|
692
665
|
}
|
|
693
666
|
|
|
@@ -697,7 +670,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
697
670
|
}
|
|
698
671
|
|
|
699
672
|
// Apply contrast limiting adaptive histogram equalization (CLAHE)
|
|
700
|
-
if (
|
|
673
|
+
if (baton->claheWidth != 0 && baton->claheHeight != 0) {
|
|
701
674
|
image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
|
|
702
675
|
}
|
|
703
676
|
|
|
@@ -871,6 +844,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
871
844
|
->set("near_lossless", baton->webpNearLossless)
|
|
872
845
|
->set("smart_subsample", baton->webpSmartSubsample)
|
|
873
846
|
->set("effort", baton->webpEffort)
|
|
847
|
+
->set("min_size", baton->webpMinSize)
|
|
848
|
+
->set("mixed", baton->webpMixed)
|
|
874
849
|
->set("alpha_q", baton->webpAlphaQuality)));
|
|
875
850
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
876
851
|
baton->bufferOutLength = area->length;
|
|
@@ -885,6 +860,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
885
860
|
->set("strip", !baton->withMetadata)
|
|
886
861
|
->set("bitdepth", baton->gifBitdepth)
|
|
887
862
|
->set("effort", baton->gifEffort)
|
|
863
|
+
->set("reoptimise", baton->gifReoptimise)
|
|
888
864
|
->set("dither", baton->gifDither)));
|
|
889
865
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
890
866
|
baton->bufferOutLength = area->length;
|
|
@@ -937,6 +913,19 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
937
913
|
area->free_fn = nullptr;
|
|
938
914
|
vips_area_unref(area);
|
|
939
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";
|
|
940
929
|
} else if (baton->formatOut == "raw" ||
|
|
941
930
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::RAW)) {
|
|
942
931
|
// Write raw, uncompressed image data to buffer
|
|
@@ -1038,6 +1027,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1038
1027
|
->set("near_lossless", baton->webpNearLossless)
|
|
1039
1028
|
->set("smart_subsample", baton->webpSmartSubsample)
|
|
1040
1029
|
->set("effort", baton->webpEffort)
|
|
1030
|
+
->set("min_size", baton->webpMinSize)
|
|
1031
|
+
->set("mixed", baton->webpMixed)
|
|
1041
1032
|
->set("alpha_q", baton->webpAlphaQuality));
|
|
1042
1033
|
baton->formatOut = "webp";
|
|
1043
1034
|
} else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
|
|
@@ -1048,6 +1039,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1048
1039
|
->set("strip", !baton->withMetadata)
|
|
1049
1040
|
->set("bitdepth", baton->gifBitdepth)
|
|
1050
1041
|
->set("effort", baton->gifEffort)
|
|
1042
|
+
->set("reoptimise", baton->gifReoptimise)
|
|
1051
1043
|
->set("dither", baton->gifDither));
|
|
1052
1044
|
baton->formatOut = "gif";
|
|
1053
1045
|
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
|
@@ -1089,64 +1081,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1089
1081
|
->set("lossless", baton->heifLossless));
|
|
1090
1082
|
baton->formatOut = "heif";
|
|
1091
1083
|
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
|
1084
|
+
// Write DZ to file
|
|
1092
1085
|
if (isDzZip) {
|
|
1093
1086
|
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
|
1094
1087
|
}
|
|
1095
|
-
// Forward format options through suffix
|
|
1096
|
-
std::string suffix;
|
|
1097
|
-
if (baton->tileFormat == "png") {
|
|
1098
|
-
std::vector<std::pair<std::string, std::string>> options {
|
|
1099
|
-
{"interlace", baton->pngProgressive ? "TRUE" : "FALSE"},
|
|
1100
|
-
{"compression", std::to_string(baton->pngCompressionLevel)},
|
|
1101
|
-
{"filter", baton->pngAdaptiveFiltering ? "all" : "none"}
|
|
1102
|
-
};
|
|
1103
|
-
suffix = AssembleSuffixString(".png", options);
|
|
1104
|
-
} else if (baton->tileFormat == "webp") {
|
|
1105
|
-
std::vector<std::pair<std::string, std::string>> options {
|
|
1106
|
-
{"Q", std::to_string(baton->webpQuality)},
|
|
1107
|
-
{"alpha_q", std::to_string(baton->webpAlphaQuality)},
|
|
1108
|
-
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
|
|
1109
|
-
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
|
|
1110
|
-
{"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
|
|
1111
|
-
{"effort", std::to_string(baton->webpEffort)}
|
|
1112
|
-
};
|
|
1113
|
-
suffix = AssembleSuffixString(".webp", options);
|
|
1114
|
-
} else {
|
|
1115
|
-
std::vector<std::pair<std::string, std::string>> options {
|
|
1116
|
-
{"Q", std::to_string(baton->jpegQuality)},
|
|
1117
|
-
{"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
|
|
1118
|
-
{"subsample_mode", baton->jpegChromaSubsampling == "4:4:4" ? "off" : "on"},
|
|
1119
|
-
{"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"},
|
|
1120
|
-
{"quant_table", std::to_string(baton->jpegQuantisationTable)},
|
|
1121
|
-
{"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"},
|
|
1122
|
-
{"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"},
|
|
1123
|
-
{"optimize_coding", baton->jpegOptimiseCoding ? "TRUE": "FALSE"}
|
|
1124
|
-
};
|
|
1125
|
-
std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_DZ ? ".jpeg" : ".jpg";
|
|
1126
|
-
suffix = AssembleSuffixString(extname, options);
|
|
1127
|
-
}
|
|
1128
|
-
// Remove alpha channel from tile background if image does not contain an alpha channel
|
|
1129
1088
|
if (!sharp::HasAlpha(image)) {
|
|
1130
1089
|
baton->tileBackground.pop_back();
|
|
1131
1090
|
}
|
|
1132
|
-
|
|
1133
|
-
vips::VOption *options = VImage::option()
|
|
1134
|
-
->set("strip", !baton->withMetadata)
|
|
1135
|
-
->set("tile_size", baton->tileSize)
|
|
1136
|
-
->set("overlap", baton->tileOverlap)
|
|
1137
|
-
->set("container", baton->tileContainer)
|
|
1138
|
-
->set("layout", baton->tileLayout)
|
|
1139
|
-
->set("suffix", const_cast<char*>(suffix.data()))
|
|
1140
|
-
->set("angle", CalculateAngleRotation(baton->tileAngle))
|
|
1141
|
-
->set("background", baton->tileBackground)
|
|
1142
|
-
->set("centre", baton->tileCentre)
|
|
1143
|
-
->set("id", const_cast<char*>(baton->tileId.data()))
|
|
1144
|
-
->set("skip_blanks", baton->tileSkipBlanks);
|
|
1145
|
-
// libvips chooses a default depth based on layout. Instead of replicating that logic here by
|
|
1146
|
-
// not passing anything - libvips will handle choice
|
|
1147
|
-
if (baton->tileDepth < VIPS_FOREIGN_DZ_DEPTH_LAST) {
|
|
1148
|
-
options->set("depth", baton->tileDepth);
|
|
1149
|
-
}
|
|
1091
|
+
vips::VOption *options = BuildOptionsDZ(baton);
|
|
1150
1092
|
image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
|
|
1151
1093
|
baton->formatOut = "dz";
|
|
1152
1094
|
} else if (baton->formatOut == "v" || (mightMatchInput && isV) ||
|
|
@@ -1215,6 +1157,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1215
1157
|
info.Set("trimOffsetTop", static_cast<int32_t>(baton->trimOffsetTop));
|
|
1216
1158
|
}
|
|
1217
1159
|
|
|
1160
|
+
if (baton->input->textAutofitDpi) {
|
|
1161
|
+
info.Set("textAutofitDpi", static_cast<uint32_t>(baton->input->textAutofitDpi));
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1218
1164
|
if (baton->bufferOutLength > 0) {
|
|
1219
1165
|
// Add buffer size to info
|
|
1220
1166
|
info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
|
|
@@ -1303,7 +1249,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1303
1249
|
|
|
1304
1250
|
/*
|
|
1305
1251
|
Assemble the suffix argument to dzsave, which is the format (by extname)
|
|
1306
|
-
|
|
1252
|
+
alongside comma-separated arguments to the corresponding `formatsave` vips
|
|
1307
1253
|
action.
|
|
1308
1254
|
*/
|
|
1309
1255
|
std::string
|
|
@@ -1318,6 +1264,67 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1318
1264
|
return extname + "[" + argument + "]";
|
|
1319
1265
|
}
|
|
1320
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
|
+
|
|
1321
1328
|
/*
|
|
1322
1329
|
Clear all thread-local data.
|
|
1323
1330
|
*/
|
|
@@ -1363,17 +1370,13 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1363
1370
|
} else if (canvas == "ignore_aspect") {
|
|
1364
1371
|
baton->canvas = sharp::Canvas::IGNORE_ASPECT;
|
|
1365
1372
|
}
|
|
1366
|
-
// Tint chroma
|
|
1367
|
-
baton->tintA = sharp::AttrAsDouble(options, "tintA");
|
|
1368
|
-
baton->tintB = sharp::AttrAsDouble(options, "tintB");
|
|
1369
1373
|
// Composite
|
|
1370
1374
|
Napi::Array compositeArray = options.Get("composite").As<Napi::Array>();
|
|
1371
1375
|
for (unsigned int i = 0; i < compositeArray.Length(); i++) {
|
|
1372
1376
|
Napi::Object compositeObject = compositeArray.Get(i).As<Napi::Object>();
|
|
1373
1377
|
Composite *composite = new Composite;
|
|
1374
1378
|
composite->input = sharp::CreateInputDescriptor(compositeObject.Get("input").As<Napi::Object>());
|
|
1375
|
-
composite->mode =
|
|
1376
|
-
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);
|
|
1377
1380
|
composite->gravity = sharp::AttrAsUint32(compositeObject, "gravity");
|
|
1378
1381
|
composite->left = sharp::AttrAsInt32(compositeObject, "left");
|
|
1379
1382
|
composite->top = sharp::AttrAsInt32(compositeObject, "top");
|
|
@@ -1387,7 +1390,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1387
1390
|
baton->withoutReduction = sharp::AttrAsBool(options, "withoutReduction");
|
|
1388
1391
|
baton->position = sharp::AttrAsInt32(options, "position");
|
|
1389
1392
|
baton->resizeBackground = sharp::AttrAsVectorOfDouble(options, "resizeBackground");
|
|
1390
|
-
baton->kernel = sharp::
|
|
1393
|
+
baton->kernel = sharp::AttrAsEnum<VipsKernel>(options, "kernel", VIPS_TYPE_KERNEL);
|
|
1391
1394
|
baton->fastShrinkOnLoad = sharp::AttrAsBool(options, "fastShrinkOnLoad");
|
|
1392
1395
|
// Join Channel Options
|
|
1393
1396
|
if (options.Has("joinChannelIn")) {
|
|
@@ -1416,13 +1419,16 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1416
1419
|
baton->sharpenY3 = sharp::AttrAsDouble(options, "sharpenY3");
|
|
1417
1420
|
baton->threshold = sharp::AttrAsInt32(options, "threshold");
|
|
1418
1421
|
baton->thresholdGrayscale = sharp::AttrAsBool(options, "thresholdGrayscale");
|
|
1422
|
+
baton->trimBackground = sharp::AttrAsVectorOfDouble(options, "trimBackground");
|
|
1419
1423
|
baton->trimThreshold = sharp::AttrAsDouble(options, "trimThreshold");
|
|
1420
1424
|
baton->gamma = sharp::AttrAsDouble(options, "gamma");
|
|
1421
1425
|
baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
|
|
1422
|
-
baton->linearA = sharp::
|
|
1423
|
-
baton->linearB = sharp::
|
|
1426
|
+
baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA");
|
|
1427
|
+
baton->linearB = sharp::AttrAsVectorOfDouble(options, "linearB");
|
|
1424
1428
|
baton->greyscale = sharp::AttrAsBool(options, "greyscale");
|
|
1425
1429
|
baton->normalise = sharp::AttrAsBool(options, "normalise");
|
|
1430
|
+
baton->tintA = sharp::AttrAsDouble(options, "tintA");
|
|
1431
|
+
baton->tintB = sharp::AttrAsDouble(options, "tintB");
|
|
1426
1432
|
baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
|
|
1427
1433
|
baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight");
|
|
1428
1434
|
baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope");
|
|
@@ -1450,10 +1456,10 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1450
1456
|
baton->ensureAlpha = sharp::AttrAsDouble(options, "ensureAlpha");
|
|
1451
1457
|
if (options.Has("boolean")) {
|
|
1452
1458
|
baton->boolean = sharp::CreateInputDescriptor(options.Get("boolean").As<Napi::Object>());
|
|
1453
|
-
baton->booleanOp = sharp::
|
|
1459
|
+
baton->booleanOp = sharp::AttrAsEnum<VipsOperationBoolean>(options, "booleanOp", VIPS_TYPE_OPERATION_BOOLEAN);
|
|
1454
1460
|
}
|
|
1455
1461
|
if (options.Has("bandBoolOp")) {
|
|
1456
|
-
baton->bandBoolOp = sharp::
|
|
1462
|
+
baton->bandBoolOp = sharp::AttrAsEnum<VipsOperationBoolean>(options, "bandBoolOp", VIPS_TYPE_OPERATION_BOOLEAN);
|
|
1457
1463
|
}
|
|
1458
1464
|
if (options.Has("convKernel")) {
|
|
1459
1465
|
Napi::Object kernel = options.Get("convKernel").As<Napi::Object>();
|
|
@@ -1475,11 +1481,12 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1475
1481
|
baton->recombMatrix[i] = sharp::AttrAsDouble(recombMatrix, i);
|
|
1476
1482
|
}
|
|
1477
1483
|
}
|
|
1478
|
-
baton->colourspaceInput = sharp::
|
|
1484
|
+
baton->colourspaceInput = sharp::AttrAsEnum<VipsInterpretation>(
|
|
1485
|
+
options, "colourspaceInput", VIPS_TYPE_INTERPRETATION);
|
|
1479
1486
|
if (baton->colourspaceInput == VIPS_INTERPRETATION_ERROR) {
|
|
1480
1487
|
baton->colourspaceInput = VIPS_INTERPRETATION_LAST;
|
|
1481
1488
|
}
|
|
1482
|
-
baton->colourspace = sharp::
|
|
1489
|
+
baton->colourspace = sharp::AttrAsEnum<VipsInterpretation>(options, "colourspace", VIPS_TYPE_INTERPRETATION);
|
|
1483
1490
|
if (baton->colourspace == VIPS_INTERPRETATION_ERROR) {
|
|
1484
1491
|
baton->colourspace = VIPS_INTERPRETATION_sRGB;
|
|
1485
1492
|
}
|
|
@@ -1494,7 +1501,9 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1494
1501
|
Napi::Array mdStrKeys = mdStrs.GetPropertyNames();
|
|
1495
1502
|
for (unsigned int i = 0; i < mdStrKeys.Length(); i++) {
|
|
1496
1503
|
std::string k = sharp::AttrAsStr(mdStrKeys, i);
|
|
1497
|
-
|
|
1504
|
+
if (mdStrs.HasOwnProperty(k)) {
|
|
1505
|
+
baton->withMetadataStrs.insert(std::make_pair(k, sharp::AttrAsStr(mdStrs, k)));
|
|
1506
|
+
}
|
|
1498
1507
|
}
|
|
1499
1508
|
baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
|
|
1500
1509
|
// Format-specific
|
|
@@ -1525,9 +1534,12 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1525
1534
|
baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
|
|
1526
1535
|
baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
|
|
1527
1536
|
baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
|
|
1537
|
+
baton->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
|
|
1538
|
+
baton->webpMixed = sharp::AttrAsBool(options, "webpMixed");
|
|
1528
1539
|
baton->gifBitdepth = sharp::AttrAsUint32(options, "gifBitdepth");
|
|
1529
1540
|
baton->gifEffort = sharp::AttrAsUint32(options, "gifEffort");
|
|
1530
1541
|
baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
|
|
1542
|
+
baton->gifReoptimise = sharp::AttrAsBool(options, "gifReoptimise");
|
|
1531
1543
|
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
|
|
1532
1544
|
baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
|
|
1533
1545
|
baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
|
|
@@ -1539,28 +1551,19 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1539
1551
|
if (baton->tiffXres == 1.0 && baton->tiffYres == 1.0 && baton->withMetadataDensity > 0) {
|
|
1540
1552
|
baton->tiffXres = baton->tiffYres = baton->withMetadataDensity / 25.4;
|
|
1541
1553
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
baton->
|
|
1547
|
-
|
|
1548
|
-
sharp::AttrAsStr(options, "tiffPredictor").data()));
|
|
1549
|
-
baton->tiffResolutionUnit = static_cast<VipsForeignTiffResunit>(
|
|
1550
|
-
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_RESUNIT,
|
|
1551
|
-
sharp::AttrAsStr(options, "tiffResolutionUnit").data()));
|
|
1552
|
-
|
|
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);
|
|
1553
1560
|
baton->heifQuality = sharp::AttrAsUint32(options, "heifQuality");
|
|
1554
1561
|
baton->heifLossless = sharp::AttrAsBool(options, "heifLossless");
|
|
1555
|
-
baton->heifCompression =
|
|
1556
|
-
|
|
1557
|
-
sharp::AttrAsStr(options, "heifCompression").data()));
|
|
1562
|
+
baton->heifCompression = sharp::AttrAsEnum<VipsForeignHeifCompression>(
|
|
1563
|
+
options, "heifCompression", VIPS_TYPE_FOREIGN_HEIF_COMPRESSION);
|
|
1558
1564
|
baton->heifEffort = sharp::AttrAsUint32(options, "heifEffort");
|
|
1559
1565
|
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
|
|
1560
|
-
|
|
1561
|
-
baton->rawDepth = static_cast<VipsBandFormat>(
|
|
1562
|
-
vips_enum_from_nick(nullptr, VIPS_TYPE_BAND_FORMAT,
|
|
1563
|
-
sharp::AttrAsStr(options, "rawDepth").data()));
|
|
1566
|
+
baton->rawDepth = sharp::AttrAsEnum<VipsBandFormat>(options, "rawDepth", VIPS_TYPE_BAND_FORMAT);
|
|
1564
1567
|
// Animated output properties
|
|
1565
1568
|
if (sharp::HasAttr(options, "loop")) {
|
|
1566
1569
|
baton->loop = sharp::AttrAsUint32(options, "loop");
|
|
@@ -1568,24 +1571,19 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1568
1571
|
if (sharp::HasAttr(options, "delay")) {
|
|
1569
1572
|
baton->delay = sharp::AttrAsInt32Vector(options, "delay");
|
|
1570
1573
|
}
|
|
1571
|
-
// Tile output
|
|
1572
1574
|
baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
|
|
1573
1575
|
baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
|
|
1574
1576
|
baton->tileAngle = sharp::AttrAsInt32(options, "tileAngle");
|
|
1575
1577
|
baton->tileBackground = sharp::AttrAsVectorOfDouble(options, "tileBackground");
|
|
1576
1578
|
baton->tileSkipBlanks = sharp::AttrAsInt32(options, "tileSkipBlanks");
|
|
1577
|
-
baton->tileContainer =
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
baton->tileLayout = static_cast<VipsForeignDzLayout>(
|
|
1581
|
-
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_LAYOUT,
|
|
1582
|
-
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);
|
|
1583
1582
|
baton->tileFormat = sharp::AttrAsStr(options, "tileFormat");
|
|
1584
|
-
baton->tileDepth =
|
|
1585
|
-
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_DEPTH,
|
|
1586
|
-
sharp::AttrAsStr(options, "tileDepth").data()));
|
|
1583
|
+
baton->tileDepth = sharp::AttrAsEnum<VipsForeignDzDepth>(options, "tileDepth", VIPS_TYPE_FOREIGN_DZ_DEPTH);
|
|
1587
1584
|
baton->tileCentre = sharp::AttrAsBool(options, "tileCentre");
|
|
1588
1585
|
baton->tileId = sharp::AttrAsStr(options, "tileId");
|
|
1586
|
+
baton->tileBasename = sharp::AttrAsStr(options, "tileBasename");
|
|
1589
1587
|
|
|
1590
1588
|
// Force random access for certain operations
|
|
1591
1589
|
if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) {
|