sharp 0.35.0-rc.0 → 0.35.0-rc.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/README.md +6 -16
- package/lib/constructor.js +3 -2
- package/lib/index.d.ts +25 -23
- package/lib/input.js +1 -0
- package/lib/libvips.js +27 -19
- package/lib/output.js +65 -6
- package/lib/sharp.js +60 -20
- package/package.json +41 -39
- package/src/binding.gyp +7 -3
- package/src/common.cc +39 -15
- package/src/common.h +9 -3
- package/src/metadata.cc +52 -4
- package/src/metadata.h +1 -0
- package/src/operations.cc +2 -3
- package/src/pipeline.cc +159 -51
- package/src/pipeline.h +3 -1
- package/src/stats.cc +6 -6
- package/src/utilities.cc +1 -1
package/src/pipeline.cc
CHANGED
|
@@ -84,7 +84,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
84
84
|
if (nPages == -1) {
|
|
85
85
|
// Resolve the number of pages if we need to render until the end of the document
|
|
86
86
|
nPages = image.get_typeof(VIPS_META_N_PAGES) != 0
|
|
87
|
-
? image.get_int(VIPS_META_N_PAGES) - baton->input->page
|
|
87
|
+
? image.get_int(VIPS_META_N_PAGES) - std::max(0, baton->input->page)
|
|
88
88
|
: 1;
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -203,9 +203,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
203
203
|
// - the width or height parameters are specified;
|
|
204
204
|
// - gamma correction doesn't need to be applied;
|
|
205
205
|
// - trimming or pre-resize extract isn't required;
|
|
206
|
+
// - gain map processing is not required;
|
|
206
207
|
// - input colourspace is not specified;
|
|
207
208
|
bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) &&
|
|
208
209
|
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold < 0.0 &&
|
|
210
|
+
!baton->keepGainMap && !baton->withGainMap &&
|
|
209
211
|
baton->colourspacePipeline == VIPS_INTERPRETATION_LAST && !(shouldOrientBefore || shouldRotateBefore);
|
|
210
212
|
|
|
211
213
|
if (shouldPreShrink) {
|
|
@@ -274,7 +276,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
274
276
|
}
|
|
275
277
|
sharp::SetDensity(image, baton->input->density);
|
|
276
278
|
if (image.width() > 32767 || image.height() > 32767) {
|
|
277
|
-
throw
|
|
279
|
+
throw std::runtime_error("Input SVG image will exceed 32767x32767 pixel limit when scaled");
|
|
278
280
|
}
|
|
279
281
|
} else if (inputImageType == sharp::ImageType::PDF) {
|
|
280
282
|
if (baton->input->buffer != nullptr) {
|
|
@@ -290,18 +292,26 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
290
292
|
}
|
|
291
293
|
} else {
|
|
292
294
|
if (inputImageType == sharp::ImageType::SVG && (image.width() > 32767 || image.height() > 32767)) {
|
|
293
|
-
throw
|
|
295
|
+
throw std::runtime_error("Input SVG image exceeds 32767x32767 pixel limit");
|
|
294
296
|
}
|
|
295
297
|
}
|
|
296
298
|
if (baton->input->autoOrient) {
|
|
297
299
|
image = sharp::RemoveExifOrientation(image);
|
|
298
300
|
}
|
|
301
|
+
VImage gainMap;
|
|
302
|
+
int gainMapScaleFactor = 1;
|
|
299
303
|
if (sharp::HasGainMap(image)) {
|
|
300
|
-
if (baton->
|
|
304
|
+
if (baton->keepGainMap) {
|
|
305
|
+
gainMap = image.gainmap();
|
|
306
|
+
if (image.get_typeof("gainmap-scale-factor") == G_TYPE_INT) {
|
|
307
|
+
gainMapScaleFactor = image.get_int("gainmap-scale-factor");
|
|
308
|
+
}
|
|
309
|
+
} else if (baton->withGainMap) {
|
|
301
310
|
image = image.uhdr2scRGB();
|
|
302
311
|
}
|
|
303
312
|
image = sharp::RemoveGainMap(image);
|
|
304
313
|
} else {
|
|
314
|
+
baton->keepGainMap = false;
|
|
305
315
|
baton->withGainMap = false;
|
|
306
316
|
}
|
|
307
317
|
|
|
@@ -401,6 +411,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
401
411
|
image = image.resize(1.0 / hshrink, VImage::option()
|
|
402
412
|
->set("vscale", 1.0 / vshrink)
|
|
403
413
|
->set("kernel", baton->kernel));
|
|
414
|
+
if (baton->keepGainMap) {
|
|
415
|
+
gainMap = gainMap.resize(1.0 / hshrink, VImage::option()
|
|
416
|
+
->set("vscale", 1.0 / vshrink)
|
|
417
|
+
->set("kernel", baton->kernel));
|
|
418
|
+
}
|
|
404
419
|
}
|
|
405
420
|
|
|
406
421
|
image = sharp::StaySequential(image,
|
|
@@ -413,14 +428,23 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
413
428
|
MultiPageUnsupported(nPages, "Rotate");
|
|
414
429
|
}
|
|
415
430
|
image = image.rot(autoRotation);
|
|
431
|
+
if (baton->keepGainMap) {
|
|
432
|
+
gainMap = gainMap.rot(autoRotation);
|
|
433
|
+
}
|
|
416
434
|
}
|
|
417
435
|
// Mirror vertically (up-down) about the x-axis
|
|
418
436
|
if (baton->flip) {
|
|
419
437
|
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
438
|
+
if (baton->keepGainMap) {
|
|
439
|
+
gainMap = gainMap.flip(VIPS_DIRECTION_VERTICAL);
|
|
440
|
+
}
|
|
420
441
|
}
|
|
421
442
|
// Mirror horizontally (left-right) about the y-axis
|
|
422
443
|
if (baton->flop != autoFlop) {
|
|
423
444
|
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
445
|
+
if (baton->keepGainMap) {
|
|
446
|
+
gainMap = gainMap.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
447
|
+
}
|
|
424
448
|
}
|
|
425
449
|
// Rotate post-extract 90-angle
|
|
426
450
|
if (rotation != VIPS_ANGLE_D0) {
|
|
@@ -428,6 +452,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
428
452
|
MultiPageUnsupported(nPages, "Rotate");
|
|
429
453
|
}
|
|
430
454
|
image = image.rot(rotation);
|
|
455
|
+
if (baton->keepGainMap) {
|
|
456
|
+
gainMap = gainMap.rot(rotation);
|
|
457
|
+
}
|
|
431
458
|
}
|
|
432
459
|
|
|
433
460
|
// Join additional color channels to the image
|
|
@@ -474,6 +501,12 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
474
501
|
: image.embed(left, top, width, height, VImage::option()
|
|
475
502
|
->set("extend", VIPS_EXTEND_BACKGROUND)
|
|
476
503
|
->set("background", background));
|
|
504
|
+
if (baton->keepGainMap) {
|
|
505
|
+
gainMap = gainMap.embed(left / gainMapScaleFactor, top / gainMapScaleFactor,
|
|
506
|
+
width / gainMapScaleFactor, height / gainMapScaleFactor, VImage::option()
|
|
507
|
+
->set("extend", VIPS_EXTEND_BACKGROUND)
|
|
508
|
+
->set("background", 0));
|
|
509
|
+
}
|
|
477
510
|
} else if (baton->canvas == sharp::Canvas::CROP) {
|
|
478
511
|
if (baton->width > inputWidth) {
|
|
479
512
|
baton->width = inputWidth;
|
|
@@ -494,12 +527,17 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
494
527
|
? sharp::CropMultiPage(image,
|
|
495
528
|
left, top, width, height, nPages, &targetPageHeight)
|
|
496
529
|
: image.extract_area(left, top, width, height);
|
|
530
|
+
if (baton->keepGainMap) {
|
|
531
|
+
gainMap = gainMap.extract_area(left / gainMapScaleFactor, top / gainMapScaleFactor,
|
|
532
|
+
width / gainMapScaleFactor, height / gainMapScaleFactor);
|
|
533
|
+
}
|
|
497
534
|
} else {
|
|
498
535
|
int attention_x;
|
|
499
536
|
int attention_y;
|
|
500
537
|
|
|
501
538
|
// Attention-based or Entropy-based crop
|
|
502
539
|
MultiPageUnsupported(nPages, "Resize strategy");
|
|
540
|
+
KeepGainMapUnsupported(baton->keepGainMap, "Resize strategy");
|
|
503
541
|
image = sharp::StaySequential(image);
|
|
504
542
|
image = image.smartcrop(baton->width, baton->height, VImage::option()
|
|
505
543
|
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
|
|
@@ -523,6 +561,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
523
561
|
std::vector<double> background;
|
|
524
562
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha);
|
|
525
563
|
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
|
564
|
+
if (baton->keepGainMap) {
|
|
565
|
+
gainMap = gainMap.rotate(baton->rotationAngle, VImage::option()->set("background", 0));
|
|
566
|
+
}
|
|
526
567
|
}
|
|
527
568
|
|
|
528
569
|
// Post extraction
|
|
@@ -531,18 +572,23 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
531
572
|
image = sharp::CropMultiPage(image,
|
|
532
573
|
baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost,
|
|
533
574
|
nPages, &targetPageHeight);
|
|
534
|
-
|
|
535
575
|
// heightPost is used in the info object, so update to reflect the number of pages
|
|
536
576
|
baton->heightPost *= nPages;
|
|
537
577
|
} else {
|
|
538
578
|
image = image.extract_area(
|
|
539
579
|
baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost);
|
|
580
|
+
if (baton->keepGainMap) {
|
|
581
|
+
gainMap = gainMap.extract_area(baton->leftOffsetPost / gainMapScaleFactor,
|
|
582
|
+
baton->topOffsetPost / gainMapScaleFactor, baton->widthPost / gainMapScaleFactor,
|
|
583
|
+
baton->heightPost / gainMapScaleFactor);
|
|
584
|
+
}
|
|
540
585
|
}
|
|
541
586
|
}
|
|
542
587
|
|
|
543
588
|
// Affine transform
|
|
544
589
|
if (!baton->affineMatrix.empty()) {
|
|
545
590
|
MultiPageUnsupported(nPages, "Affine");
|
|
591
|
+
KeepGainMapUnsupported(baton->keepGainMap, "Affine");
|
|
546
592
|
image = sharp::StaySequential(image);
|
|
547
593
|
std::vector<double> background;
|
|
548
594
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
|
|
@@ -573,6 +619,12 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
573
619
|
baton->extendWith, background, nPages, &targetPageHeight)
|
|
574
620
|
: image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height,
|
|
575
621
|
VImage::option()->set("extend", baton->extendWith)->set("background", background));
|
|
622
|
+
if (baton->keepGainMap) {
|
|
623
|
+
gainMap = gainMap.embed(baton->extendLeft / gainMapScaleFactor, baton->extendTop / gainMapScaleFactor,
|
|
624
|
+
baton->width / gainMapScaleFactor, baton->height / gainMapScaleFactor, VImage::option()
|
|
625
|
+
->set("extend", baton->extendWith)
|
|
626
|
+
->set("background", 0));
|
|
627
|
+
}
|
|
576
628
|
} else {
|
|
577
629
|
std::vector<double> ignoredBackground(1);
|
|
578
630
|
image = sharp::StaySequential(image);
|
|
@@ -582,6 +634,12 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
582
634
|
baton->extendWith, ignoredBackground, nPages, &targetPageHeight)
|
|
583
635
|
: image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height,
|
|
584
636
|
VImage::option()->set("extend", baton->extendWith));
|
|
637
|
+
if (baton->keepGainMap) {
|
|
638
|
+
gainMap = gainMap.embed(baton->extendLeft / gainMapScaleFactor, baton->extendTop / gainMapScaleFactor,
|
|
639
|
+
baton->width / gainMapScaleFactor, baton->height / gainMapScaleFactor, VImage::option()
|
|
640
|
+
->set("extend", baton->extendWith)
|
|
641
|
+
->set("background", 0));
|
|
642
|
+
}
|
|
585
643
|
}
|
|
586
644
|
}
|
|
587
645
|
// Median - must happen before blurring, due to the utility of blurring after thresholding
|
|
@@ -608,6 +666,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
608
666
|
// Blur
|
|
609
667
|
if (shouldBlur) {
|
|
610
668
|
image = sharp::Blur(image, baton->blurSigma, baton->precision, baton->minAmpl);
|
|
669
|
+
if (baton->keepGainMap) {
|
|
670
|
+
gainMap = sharp::Blur(gainMap, baton->blurSigma, baton->precision, baton->minAmpl);
|
|
671
|
+
}
|
|
611
672
|
}
|
|
612
673
|
|
|
613
674
|
// Unflatten the image
|
|
@@ -617,6 +678,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
617
678
|
|
|
618
679
|
// Convolve
|
|
619
680
|
if (shouldConv) {
|
|
681
|
+
KeepGainMapUnsupported(baton->keepGainMap, "Convolve");
|
|
620
682
|
image = sharp::Convolve(image,
|
|
621
683
|
baton->convKernelWidth, baton->convKernelHeight,
|
|
622
684
|
baton->convKernelScale, baton->convKernelOffset,
|
|
@@ -625,11 +687,13 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
625
687
|
|
|
626
688
|
// Recomb
|
|
627
689
|
if (!baton->recombMatrix.empty()) {
|
|
690
|
+
KeepGainMapUnsupported(baton->keepGainMap, "Recomb");
|
|
628
691
|
image = sharp::Recomb(image, baton->recombMatrix);
|
|
629
692
|
}
|
|
630
693
|
|
|
631
694
|
// Modulate
|
|
632
695
|
if (baton->brightness != 1.0 || baton->saturation != 1.0 || baton->hue != 0.0 || baton->lightness != 0.0) {
|
|
696
|
+
KeepGainMapUnsupported(baton->keepGainMap, "Modulate");
|
|
633
697
|
image = sharp::Modulate(image, baton->brightness, baton->saturation, baton->hue, baton->lightness);
|
|
634
698
|
}
|
|
635
699
|
|
|
@@ -647,6 +711,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
647
711
|
|
|
648
712
|
// Composite
|
|
649
713
|
if (shouldComposite) {
|
|
714
|
+
KeepGainMapUnsupported(baton->keepGainMap, "Composite");
|
|
650
715
|
std::vector<VImage> images = { image };
|
|
651
716
|
std::vector<int> modes, xs, ys;
|
|
652
717
|
for (Composite *composite : baton->composite) {
|
|
@@ -675,7 +740,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
675
740
|
|
|
676
741
|
// Verify within current dimensions
|
|
677
742
|
if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
|
|
678
|
-
throw
|
|
743
|
+
throw std::runtime_error("Image to composite must have same dimensions or smaller");
|
|
679
744
|
}
|
|
680
745
|
// Check if overlay is tiled
|
|
681
746
|
if (composite->tile) {
|
|
@@ -759,18 +824,21 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
759
824
|
|
|
760
825
|
// Apply normalisation - stretch luminance to cover full dynamic range
|
|
761
826
|
if (baton->normalise) {
|
|
827
|
+
KeepGainMapUnsupported(baton->keepGainMap, "Normalise");
|
|
762
828
|
image = sharp::StaySequential(image);
|
|
763
829
|
image = sharp::Normalise(image, baton->normaliseLower, baton->normaliseUpper);
|
|
764
830
|
}
|
|
765
831
|
|
|
766
832
|
// Apply contrast limiting adaptive histogram equalization (CLAHE)
|
|
767
833
|
if (baton->claheWidth != 0 && baton->claheHeight != 0) {
|
|
834
|
+
KeepGainMapUnsupported(baton->keepGainMap, "Clahe");
|
|
768
835
|
image = sharp::StaySequential(image);
|
|
769
836
|
image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
|
|
770
837
|
}
|
|
771
838
|
|
|
772
839
|
// Apply bitwise boolean operation between images
|
|
773
840
|
if (baton->boolean != nullptr) {
|
|
841
|
+
KeepGainMapUnsupported(baton->keepGainMap, "Boolean");
|
|
774
842
|
VImage booleanImage;
|
|
775
843
|
sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
|
|
776
844
|
baton->boolean->access = access;
|
|
@@ -813,6 +881,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
813
881
|
|
|
814
882
|
// Extract channel
|
|
815
883
|
if (baton->extractChannel > -1) {
|
|
884
|
+
KeepGainMapUnsupported(baton->keepGainMap, "Extract channel");
|
|
816
885
|
if (baton->extractChannel >= image.bands()) {
|
|
817
886
|
if (baton->extractChannel == 3 && image.has_alpha()) {
|
|
818
887
|
baton->extractChannel = image.bands() - 1;
|
|
@@ -847,6 +916,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
847
916
|
// Negate the colours in the image
|
|
848
917
|
if (baton->negate) {
|
|
849
918
|
image = sharp::Negate(image, baton->negateAlpha);
|
|
919
|
+
if (baton->keepGainMap) {
|
|
920
|
+
gainMap = sharp::Negate(gainMap, false);
|
|
921
|
+
}
|
|
850
922
|
}
|
|
851
923
|
|
|
852
924
|
// Override EXIF Orientation tag
|
|
@@ -893,18 +965,27 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
893
965
|
if (baton->formatOut == "jpeg" || (baton->formatOut == "input" && inputImageType == sharp::ImageType::JPEG)) {
|
|
894
966
|
// Write JPEG to buffer
|
|
895
967
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
|
|
896
|
-
VipsArea *area
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
968
|
+
VipsArea *area;
|
|
969
|
+
if (baton->keepGainMap) {
|
|
970
|
+
image = ReattachGainMap(image, gainMap, baton);
|
|
971
|
+
area = reinterpret_cast<VipsArea*>(image.uhdrsave_buffer(VImage::option()
|
|
972
|
+
->set("keep", baton->keepMetadata)
|
|
973
|
+
->set("Q", baton->jpegQuality)
|
|
974
|
+
->set("gainmap_scale_factor", gainMapScaleFactor)));
|
|
975
|
+
} else {
|
|
976
|
+
area = reinterpret_cast<VipsArea*>(image.jpegsave_buffer(VImage::option()
|
|
977
|
+
->set("keep", baton->keepMetadata)
|
|
978
|
+
->set("Q", baton->jpegQuality)
|
|
979
|
+
->set("interlace", baton->jpegProgressive)
|
|
980
|
+
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
|
981
|
+
? VIPS_FOREIGN_SUBSAMPLE_OFF
|
|
982
|
+
: VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
983
|
+
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
|
984
|
+
->set("quant_table", baton->jpegQuantisationTable)
|
|
985
|
+
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
|
986
|
+
->set("optimize_scans", baton->jpegOptimiseScans)
|
|
987
|
+
->set("optimize_coding", baton->jpegOptimiseCoding)));
|
|
988
|
+
}
|
|
908
989
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
909
990
|
baton->bufferOutLength = area->length;
|
|
910
991
|
area->free_fn = nullptr;
|
|
@@ -1086,20 +1167,19 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1086
1167
|
// Get raw image data
|
|
1087
1168
|
baton->bufferOut = static_cast<char*>(image.write_to_memory(&baton->bufferOutLength));
|
|
1088
1169
|
if (baton->bufferOut == nullptr) {
|
|
1089
|
-
(
|
|
1090
|
-
return Error();
|
|
1170
|
+
throw std::runtime_error("Could not allocate enough memory for raw output");
|
|
1091
1171
|
}
|
|
1092
1172
|
baton->formatOut = "raw";
|
|
1093
1173
|
} else {
|
|
1094
1174
|
// Unsupported output format
|
|
1095
|
-
(
|
|
1175
|
+
auto unsupported = std::string("Unsupported output format ");
|
|
1096
1176
|
if (baton->formatOut == "input") {
|
|
1097
|
-
|
|
1098
|
-
|
|
1177
|
+
unsupported.append("when trying to match input format of ");
|
|
1178
|
+
unsupported.append(ImageTypeId(inputImageType));
|
|
1099
1179
|
} else {
|
|
1100
|
-
|
|
1180
|
+
unsupported.append(baton->formatOut);
|
|
1101
1181
|
}
|
|
1102
|
-
|
|
1182
|
+
throw std::runtime_error(unsupported);
|
|
1103
1183
|
}
|
|
1104
1184
|
} else {
|
|
1105
1185
|
// File output
|
|
@@ -1122,18 +1202,26 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1122
1202
|
(willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
|
|
1123
1203
|
// Write JPEG to file
|
|
1124
1204
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
->
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
->
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1205
|
+
if (baton->keepGainMap) {
|
|
1206
|
+
image = ReattachGainMap(image, gainMap, baton);
|
|
1207
|
+
image.uhdrsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1208
|
+
->set("keep", baton->keepMetadata)
|
|
1209
|
+
->set("Q", baton->jpegQuality)
|
|
1210
|
+
->set("gainmap_scale_factor", gainMapScaleFactor));
|
|
1211
|
+
} else {
|
|
1212
|
+
image.jpegsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1213
|
+
->set("keep", baton->keepMetadata)
|
|
1214
|
+
->set("Q", baton->jpegQuality)
|
|
1215
|
+
->set("interlace", baton->jpegProgressive)
|
|
1216
|
+
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
|
1217
|
+
? VIPS_FOREIGN_SUBSAMPLE_OFF
|
|
1218
|
+
: VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
1219
|
+
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
|
1220
|
+
->set("quant_table", baton->jpegQuantisationTable)
|
|
1221
|
+
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
|
1222
|
+
->set("optimize_scans", baton->jpegOptimiseScans)
|
|
1223
|
+
->set("optimize_coding", baton->jpegOptimiseCoding));
|
|
1224
|
+
}
|
|
1137
1225
|
baton->formatOut = "jpeg";
|
|
1138
1226
|
baton->channels = std::min(baton->channels, 3);
|
|
1139
1227
|
} else if (baton->formatOut == "jp2" || (mightMatchInput && isJp2) ||
|
|
@@ -1274,7 +1362,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1274
1362
|
return Error();
|
|
1275
1363
|
}
|
|
1276
1364
|
}
|
|
1277
|
-
} catch (
|
|
1365
|
+
} catch (std::runtime_error const &err) {
|
|
1278
1366
|
char const *what = err.what();
|
|
1279
1367
|
if (what && what[0]) {
|
|
1280
1368
|
(baton->err).append(what);
|
|
@@ -1302,11 +1390,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1302
1390
|
if (baton->errUseWarning) {
|
|
1303
1391
|
(baton->err).append("\n").append(warning);
|
|
1304
1392
|
} else {
|
|
1305
|
-
debuglog.
|
|
1393
|
+
debuglog.SHARP_CALLBACK_FN_NAME(Receiver().Value(), { Napi::String::New(env, warning) });
|
|
1306
1394
|
}
|
|
1307
1395
|
warning = sharp::VipsWarningPop();
|
|
1308
1396
|
}
|
|
1309
|
-
|
|
1310
1397
|
if (baton->err.empty()) {
|
|
1311
1398
|
int width = baton->width;
|
|
1312
1399
|
int height = baton->height;
|
|
@@ -1352,17 +1439,15 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1352
1439
|
info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
|
|
1353
1440
|
if (baton->typedArrayOut) {
|
|
1354
1441
|
// ECMAScript ArrayBuffer with Uint8Array view
|
|
1355
|
-
Napi::
|
|
1356
|
-
|
|
1442
|
+
Napi::TypedArrayOf<uint8_t> data = Napi::Buffer<char>::Copy(env,
|
|
1443
|
+
static_cast<char*>(baton->bufferOut), baton->bufferOutLength);
|
|
1357
1444
|
sharp::FreeCallback(static_cast<char*>(baton->bufferOut), nullptr);
|
|
1358
|
-
|
|
1359
|
-
baton->bufferOutLength, ab, 0, napi_uint8_array);
|
|
1360
|
-
Callback().Call(Receiver().Value(), { env.Null(), data, info });
|
|
1445
|
+
Callback().SHARP_CALLBACK_FN_NAME(Receiver().Value(), { env.Null(), data, info });
|
|
1361
1446
|
} else {
|
|
1362
1447
|
// Node.js Buffer
|
|
1363
1448
|
Napi::Buffer<char> data = Napi::Buffer<char>::NewOrCopy(env, static_cast<char*>(baton->bufferOut),
|
|
1364
1449
|
baton->bufferOutLength, sharp::FreeCallback);
|
|
1365
|
-
Callback().
|
|
1450
|
+
Callback().SHARP_CALLBACK_FN_NAME(Receiver().Value(), { env.Null(), data, info });
|
|
1366
1451
|
}
|
|
1367
1452
|
} else {
|
|
1368
1453
|
// Add file size to info
|
|
@@ -1373,10 +1458,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1373
1458
|
info.Set("size", size);
|
|
1374
1459
|
} catch (...) {}
|
|
1375
1460
|
}
|
|
1376
|
-
Callback().
|
|
1461
|
+
Callback().SHARP_CALLBACK_FN_NAME(Receiver().Value(), { env.Null(), info });
|
|
1377
1462
|
}
|
|
1378
1463
|
} else {
|
|
1379
|
-
Callback().
|
|
1464
|
+
Callback().SHARP_CALLBACK_FN_NAME(Receiver().Value(),
|
|
1465
|
+
{ Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
|
|
1380
1466
|
}
|
|
1381
1467
|
|
|
1382
1468
|
// Delete baton
|
|
@@ -1397,7 +1483,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1397
1483
|
// Decrement processing task counter
|
|
1398
1484
|
sharp::counterProcess--;
|
|
1399
1485
|
Napi::Number queueLength = Napi::Number::New(env, static_cast<int>(sharp::counterQueue));
|
|
1400
|
-
queueListener.
|
|
1486
|
+
queueListener.SHARP_CALLBACK_FN_NAME(Receiver().Value(), { queueLength });
|
|
1401
1487
|
}
|
|
1402
1488
|
|
|
1403
1489
|
private:
|
|
@@ -1407,7 +1493,13 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1407
1493
|
|
|
1408
1494
|
void MultiPageUnsupported(int const pages, std::string op) {
|
|
1409
1495
|
if (pages > 1) {
|
|
1410
|
-
throw
|
|
1496
|
+
throw std::runtime_error(op + " is not supported for multi-page images");
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
void KeepGainMapUnsupported(bool const keepGainMap, std::string op) {
|
|
1501
|
+
if (keepGainMap) {
|
|
1502
|
+
throw std::runtime_error(op + " is not supported when keeping gain maps");
|
|
1411
1503
|
}
|
|
1412
1504
|
}
|
|
1413
1505
|
|
|
@@ -1529,6 +1621,21 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1529
1621
|
return options;
|
|
1530
1622
|
}
|
|
1531
1623
|
|
|
1624
|
+
VImage ReattachGainMap(VImage image, VImage gainMap, PipelineBaton *baton) {
|
|
1625
|
+
VipsArea *gainMapJpeg = reinterpret_cast<VipsArea*>(gainMap.jpegsave_buffer(VImage::option()
|
|
1626
|
+
->set("keep", FALSE)
|
|
1627
|
+
->set("Q", baton->jpegQuality)
|
|
1628
|
+
->set("subsample_mode", VIPS_FOREIGN_SUBSAMPLE_OFF)
|
|
1629
|
+
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
|
1630
|
+
->set("quant_table", baton->jpegQuantisationTable)
|
|
1631
|
+
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
|
1632
|
+
->set("optimize_coding", baton->jpegOptimiseCoding)));
|
|
1633
|
+
image = image.copy();
|
|
1634
|
+
image.set("gainmap-data", reinterpret_cast<VipsCallbackFn>(vips_area_free_cb),
|
|
1635
|
+
gainMapJpeg->data, gainMapJpeg->length);
|
|
1636
|
+
return image;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1532
1639
|
/*
|
|
1533
1640
|
Clear all thread-local data.
|
|
1534
1641
|
*/
|
|
@@ -1730,6 +1837,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1730
1837
|
}
|
|
1731
1838
|
baton->withExifMerge = sharp::AttrAsBool(options, "withExifMerge");
|
|
1732
1839
|
baton->withXmp = sharp::AttrAsStr(options, "withXmp");
|
|
1840
|
+
baton->keepGainMap = sharp::AttrAsBool(options, "keepGainMap");
|
|
1733
1841
|
baton->withGainMap = sharp::AttrAsBool(options, "withGainMap");
|
|
1734
1842
|
baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
|
|
1735
1843
|
baton->loop = sharp::AttrAsUint32(options, "loop");
|
|
@@ -1835,7 +1943,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1835
1943
|
|
|
1836
1944
|
// Increment queued task counter
|
|
1837
1945
|
Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<int>(++sharp::counterQueue));
|
|
1838
|
-
queueListener.
|
|
1946
|
+
queueListener.SHARP_CALLBACK_FN_NAME(info.This(), { queueLength });
|
|
1839
1947
|
|
|
1840
1948
|
return info.Env().Undefined();
|
|
1841
1949
|
}
|
package/src/pipeline.h
CHANGED
|
@@ -213,6 +213,7 @@ struct PipelineBaton {
|
|
|
213
213
|
bool withExifMerge;
|
|
214
214
|
std::string withXmp;
|
|
215
215
|
bool withGainMap;
|
|
216
|
+
bool keepGainMap;
|
|
216
217
|
int timeoutSeconds;
|
|
217
218
|
std::vector<double> convKernel;
|
|
218
219
|
int convKernelWidth;
|
|
@@ -365,7 +366,7 @@ struct PipelineBaton {
|
|
|
365
366
|
tiffBigtiff(false),
|
|
366
367
|
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
|
|
367
368
|
tiffPyramid(false),
|
|
368
|
-
tiffBitdepth(
|
|
369
|
+
tiffBitdepth(0),
|
|
369
370
|
tiffMiniswhite(false),
|
|
370
371
|
tiffTile(false),
|
|
371
372
|
tiffTileHeight(256),
|
|
@@ -391,6 +392,7 @@ struct PipelineBaton {
|
|
|
391
392
|
withMetadataDensity(0.0),
|
|
392
393
|
withExifMerge(true),
|
|
393
394
|
withGainMap(false),
|
|
395
|
+
keepGainMap(false),
|
|
394
396
|
timeoutSeconds(0),
|
|
395
397
|
convKernelWidth(0),
|
|
396
398
|
convKernelHeight(0),
|
package/src/stats.cc
CHANGED
|
@@ -39,7 +39,7 @@ class StatsWorker : public Napi::AsyncWorker {
|
|
|
39
39
|
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
|
|
40
40
|
try {
|
|
41
41
|
std::tie(image, imageType) = OpenInput(baton->input);
|
|
42
|
-
} catch (
|
|
42
|
+
} catch (std::runtime_error const &err) {
|
|
43
43
|
(baton->err).append(err.what());
|
|
44
44
|
}
|
|
45
45
|
if (imageType != sharp::ImageType::UNKNOWN) {
|
|
@@ -92,7 +92,7 @@ class StatsWorker : public Napi::AsyncWorker {
|
|
|
92
92
|
baton->dominantRed = dx * 16 + 8;
|
|
93
93
|
baton->dominantGreen = dy * 16 + 8;
|
|
94
94
|
baton->dominantBlue = dz * 16 + 8;
|
|
95
|
-
} catch (
|
|
95
|
+
} catch (std::runtime_error const &err) {
|
|
96
96
|
(baton->err).append(err.what());
|
|
97
97
|
}
|
|
98
98
|
}
|
|
@@ -109,10 +109,9 @@ class StatsWorker : public Napi::AsyncWorker {
|
|
|
109
109
|
// Handle warnings
|
|
110
110
|
std::string warning = sharp::VipsWarningPop();
|
|
111
111
|
while (!warning.empty()) {
|
|
112
|
-
debuglog.
|
|
112
|
+
debuglog.SHARP_CALLBACK_FN_NAME(Receiver().Value(), { Napi::String::New(env, warning) });
|
|
113
113
|
warning = sharp::VipsWarningPop();
|
|
114
114
|
}
|
|
115
|
-
|
|
116
115
|
if (baton->err.empty()) {
|
|
117
116
|
// Stats Object
|
|
118
117
|
Napi::Object info = Napi::Object::New(env);
|
|
@@ -144,9 +143,10 @@ class StatsWorker : public Napi::AsyncWorker {
|
|
|
144
143
|
dominant.Set("g", baton->dominantGreen);
|
|
145
144
|
dominant.Set("b", baton->dominantBlue);
|
|
146
145
|
info.Set("dominant", dominant);
|
|
147
|
-
Callback().
|
|
146
|
+
Callback().SHARP_CALLBACK_FN_NAME(Receiver().Value(), { env.Null(), info });
|
|
148
147
|
} else {
|
|
149
|
-
Callback().
|
|
148
|
+
Callback().SHARP_CALLBACK_FN_NAME(Receiver().Value(),
|
|
149
|
+
{ Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
delete baton->input;
|
package/src/utilities.cc
CHANGED
|
@@ -244,7 +244,7 @@ Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {
|
|
|
244
244
|
}
|
|
245
245
|
// Calculate colour distance
|
|
246
246
|
maxColourDistance = image1.dE00(image2).max();
|
|
247
|
-
} catch (
|
|
247
|
+
} catch (std::runtime_error const &err) {
|
|
248
248
|
throw Napi::Error::New(env, err.what());
|
|
249
249
|
}
|
|
250
250
|
|