sharp 0.33.5 → 0.35.3

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.
Files changed (49) hide show
  1. package/README.md +12 -18
  2. package/dist/channel.cjs +177 -0
  3. package/{lib/channel.js → dist/channel.mjs} +11 -8
  4. package/dist/colour.cjs +199 -0
  5. package/{lib/colour.js → dist/colour.mjs} +42 -23
  6. package/dist/composite.cjs +213 -0
  7. package/{lib/composite.js → dist/composite.mjs} +14 -11
  8. package/{lib/constructor.js → dist/constructor.cjs} +102 -43
  9. package/dist/constructor.mjs +511 -0
  10. package/dist/index.cjs +25 -0
  11. package/dist/index.d.cts +1999 -0
  12. package/dist/index.d.mts +2046 -0
  13. package/dist/index.mjs +25 -0
  14. package/dist/input.cjs +814 -0
  15. package/{lib/input.js → dist/input.mjs} +232 -76
  16. package/{lib/is.js → dist/is.cjs} +19 -45
  17. package/dist/is.mjs +143 -0
  18. package/{lib/libvips.js → dist/libvips.cjs} +52 -43
  19. package/dist/libvips.mjs +212 -0
  20. package/dist/operation.cjs +998 -0
  21. package/{lib/operation.js → dist/operation.mjs} +131 -91
  22. package/{lib/output.js → dist/output.cjs} +262 -61
  23. package/dist/output.mjs +1788 -0
  24. package/dist/resize.cjs +617 -0
  25. package/{lib/resize.js → dist/resize.mjs} +65 -35
  26. package/dist/sharp.cjs +174 -0
  27. package/dist/sharp.mjs +174 -0
  28. package/{lib/utility.js → dist/utility.cjs} +30 -25
  29. package/dist/utility.mjs +301 -0
  30. package/install/build.js +38 -0
  31. package/lib/index.d.ts +387 -142
  32. package/package.json +90 -85
  33. package/src/binding.gyp +49 -26
  34. package/src/common.cc +203 -111
  35. package/src/common.h +68 -40
  36. package/src/metadata.cc +106 -22
  37. package/src/metadata.h +15 -5
  38. package/src/operations.cc +62 -21
  39. package/src/operations.h +15 -3
  40. package/src/pipeline.cc +391 -201
  41. package/src/pipeline.h +36 -7
  42. package/src/sharp.cc +11 -8
  43. package/src/stats.cc +16 -13
  44. package/src/stats.h +6 -3
  45. package/src/utilities.cc +35 -15
  46. package/src/utilities.h +5 -2
  47. package/install/check.js +0 -41
  48. package/lib/index.js +0 -16
  49. package/lib/sharp.js +0 -114
package/src/common.cc CHANGED
@@ -1,18 +1,22 @@
1
- // Copyright 2013 Lovell Fuller and others.
2
- // SPDX-License-Identifier: Apache-2.0
1
+ /*!
2
+ Copyright 2013 Lovell Fuller and others.
3
+ SPDX-License-Identifier: Apache-2.0
4
+ */
3
5
 
6
+ #include <algorithm>
4
7
  #include <cstdlib>
8
+ #include <map>
9
+ #include <mutex>
10
+ #include <queue>
5
11
  #include <string>
6
- #include <string.h>
12
+ #include <tuple>
13
+ #include <utility>
7
14
  #include <vector>
8
- #include <queue>
9
- #include <map>
10
- #include <mutex> // NOLINT(build/c++11)
11
15
 
12
16
  #include <napi.h>
13
17
  #include <vips/vips8>
14
18
 
15
- #include "common.h"
19
+ #include "./common.h"
16
20
 
17
21
  using vips::VImage;
18
22
 
@@ -93,6 +97,7 @@ namespace sharp {
93
97
  descriptor->rawWidth = AttrAsUint32(input, "rawWidth");
94
98
  descriptor->rawHeight = AttrAsUint32(input, "rawHeight");
95
99
  descriptor->rawPremultiplied = AttrAsBool(input, "rawPremultiplied");
100
+ descriptor->rawPageHeight = AttrAsUint32(input, "rawPageHeight");
96
101
  }
97
102
  // Multi-page input (GIF, TIFF, PDF)
98
103
  if (HasAttr(input, "pages")) {
@@ -101,19 +106,35 @@ namespace sharp {
101
106
  if (HasAttr(input, "page")) {
102
107
  descriptor->page = AttrAsUint32(input, "page");
103
108
  }
109
+ // SVG
110
+ if (HasAttr(input, "svgStylesheet")) {
111
+ descriptor->svgStylesheet = AttrAsStr(input, "svgStylesheet");
112
+ }
113
+ if (HasAttr(input, "svgHighBitdepth")) {
114
+ descriptor->svgHighBitdepth = AttrAsBool(input, "svgHighBitdepth");
115
+ }
104
116
  // Multi-level input (OpenSlide)
105
- if (HasAttr(input, "level")) {
106
- descriptor->level = AttrAsUint32(input, "level");
117
+ if (HasAttr(input, "openSlideLevel")) {
118
+ descriptor->openSlideLevel = AttrAsUint32(input, "openSlideLevel");
107
119
  }
108
120
  // subIFD (OME-TIFF)
109
121
  if (HasAttr(input, "subifd")) {
110
- descriptor->subifd = AttrAsInt32(input, "subifd");
122
+ descriptor->tiffSubifd = AttrAsInt32(input, "tiffSubifd");
123
+ }
124
+ // // PDF background color
125
+ if (HasAttr(input, "pdfBackground")) {
126
+ descriptor->pdfBackground = AttrAsVectorOfDouble(input, "pdfBackground");
127
+ }
128
+ // Use JPEG 2000 oneshot mode?
129
+ if (HasAttr(input, "jp2Oneshot")) {
130
+ descriptor->jp2Oneshot = AttrAsBool(input, "jp2Oneshot");
111
131
  }
112
132
  // Create new image
113
133
  if (HasAttr(input, "createChannels")) {
114
134
  descriptor->createChannels = AttrAsUint32(input, "createChannels");
115
135
  descriptor->createWidth = AttrAsUint32(input, "createWidth");
116
136
  descriptor->createHeight = AttrAsUint32(input, "createHeight");
137
+ descriptor->createPageHeight = AttrAsUint32(input, "createPageHeight");
117
138
  if (HasAttr(input, "createNoiseType")) {
118
139
  descriptor->createNoiseType = AttrAsStr(input, "createNoiseType");
119
140
  descriptor->createNoiseMean = AttrAsDouble(input, "createNoiseMean");
@@ -156,12 +177,34 @@ namespace sharp {
156
177
  descriptor->textWrap = AttrAsEnum<VipsTextWrap>(input, "textWrap", VIPS_TYPE_TEXT_WRAP);
157
178
  }
158
179
  }
159
- // Limit input images to a given number of pixels, where pixels = width * height
180
+ // Join images together
181
+ if (HasAttr(input, "joinAnimated")) {
182
+ descriptor->joinAnimated = AttrAsBool(input, "joinAnimated");
183
+ }
184
+ if (HasAttr(input, "joinAcross")) {
185
+ descriptor->joinAcross = AttrAsUint32(input, "joinAcross");
186
+ }
187
+ if (HasAttr(input, "joinShim")) {
188
+ descriptor->joinShim = AttrAsUint32(input, "joinShim");
189
+ }
190
+ if (HasAttr(input, "joinBackground")) {
191
+ descriptor->joinBackground = AttrAsVectorOfDouble(input, "joinBackground");
192
+ }
193
+ if (HasAttr(input, "joinHalign")) {
194
+ descriptor->joinHalign = AttrAsEnum<VipsAlign>(input, "joinHalign", VIPS_TYPE_ALIGN);
195
+ }
196
+ if (HasAttr(input, "joinValign")) {
197
+ descriptor->joinValign = AttrAsEnum<VipsAlign>(input, "joinValign", VIPS_TYPE_ALIGN);
198
+ }
160
199
  descriptor->limitInputPixels = static_cast<uint64_t>(AttrAsInt64(input, "limitInputPixels"));
161
- // Allow switch from random to sequential access
162
- descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
200
+ descriptor->limitInputChannels = static_cast<uint64_t>(AttrAsInt64(input, "limitInputChannels"));
201
+ if (HasAttr(input, "access")) {
202
+ descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
203
+ }
163
204
  // Remove safety features and allow unlimited input
164
205
  descriptor->unlimited = AttrAsBool(input, "unlimited");
206
+ // Use the EXIF orientation to auto orient the image
207
+ descriptor->autoOrient = AttrAsBool(input, "autoOrient");
165
208
  return descriptor;
166
209
  }
167
210
 
@@ -244,6 +287,9 @@ namespace sharp {
244
287
  case ImageType::FITS: id = "fits"; break;
245
288
  case ImageType::EXR: id = "exr"; break;
246
289
  case ImageType::JXL: id = "jxl"; break;
290
+ case ImageType::RAD: id = "rad"; break;
291
+ case ImageType::DCRAW: id = "dcraw"; break;
292
+ case ImageType::UHDR: id = "uhdr"; break;
247
293
  case ImageType::VIPS: id = "vips"; break;
248
294
  case ImageType::RAW: id = "raw"; break;
249
295
  case ImageType::UNKNOWN: id = "unknown"; break;
@@ -290,6 +336,13 @@ namespace sharp {
290
336
  { "VipsForeignLoadOpenexr", ImageType::EXR },
291
337
  { "VipsForeignLoadJxlFile", ImageType::JXL },
292
338
  { "VipsForeignLoadJxlBuffer", ImageType::JXL },
339
+ { "VipsForeignLoadRadFile", ImageType::RAD },
340
+ { "VipsForeignLoadRadBuffer", ImageType::RAD },
341
+ { "VipsForeignLoadDcRawFile", ImageType::DCRAW },
342
+ { "VipsForeignLoadDcRawBuffer", ImageType::DCRAW },
343
+ { "VipsForeignLoadUhdr", ImageType::UHDR },
344
+ { "VipsForeignLoadUhdrFile", ImageType::UHDR },
345
+ { "VipsForeignLoadUhdrBuffer", ImageType::UHDR },
293
346
  { "VipsForeignLoadVips", ImageType::VIPS },
294
347
  { "VipsForeignLoadVipsFile", ImageType::VIPS },
295
348
  { "VipsForeignLoadRaw", ImageType::RAW }
@@ -307,6 +360,9 @@ namespace sharp {
307
360
  imageType = it->second;
308
361
  }
309
362
  }
363
+ if (imageType == ImageType::UHDR) {
364
+ imageType = ImageType::JPEG;
365
+ }
310
366
  return imageType;
311
367
  }
312
368
 
@@ -326,6 +382,9 @@ namespace sharp {
326
382
  imageType = ImageType::MISSING;
327
383
  }
328
384
  }
385
+ if (imageType == ImageType::UHDR) {
386
+ imageType = ImageType::JPEG;
387
+ }
329
388
  return imageType;
330
389
  }
331
390
 
@@ -351,9 +410,68 @@ namespace sharp {
351
410
  imageType == ImageType::JPEG ||
352
411
  imageType == ImageType::PNG ||
353
412
  imageType == ImageType::SVG ||
413
+ imageType == ImageType::TIFF ||
354
414
  imageType == ImageType::HEIF;
355
415
  }
356
416
 
417
+ /*
418
+ Format-specific options builder
419
+ */
420
+ vips::VOption* GetOptionsForImageType(ImageType imageType, InputDescriptor *descriptor) {
421
+ vips::VOption *option = VImage::option()
422
+ ->set("access", descriptor->access)
423
+ ->set("fail_on", descriptor->failOn);
424
+ if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
425
+ option->set("unlimited", true);
426
+ }
427
+ if (ImageTypeSupportsPage(imageType)) {
428
+ option->set("n", descriptor->pages);
429
+ option->set("page", std::max(0, descriptor->page));
430
+ }
431
+ switch (imageType) {
432
+ case ImageType::SVG:
433
+ option->set("dpi", descriptor->density)
434
+ ->set("stylesheet", descriptor->svgStylesheet.data())
435
+ ->set("high_bitdepth", descriptor->svgHighBitdepth);
436
+ break;
437
+ case ImageType::TIFF:
438
+ option->set("subifd", descriptor->tiffSubifd);
439
+ break;
440
+ case ImageType::PDF:
441
+ option->set("dpi", descriptor->density)
442
+ ->set("background", descriptor->pdfBackground);
443
+ break;
444
+ case ImageType::OPENSLIDE:
445
+ option->set("level", descriptor->openSlideLevel);
446
+ break;
447
+ case ImageType::JP2:
448
+ option->set("oneshot", descriptor->jp2Oneshot);
449
+ break;
450
+ case ImageType::MAGICK:
451
+ option->set("density", std::to_string(descriptor->density).data());
452
+ break;
453
+ default:
454
+ break;
455
+ }
456
+ return option;
457
+ }
458
+
459
+ /*
460
+ Should HEIF image be re-opened using the primary item?
461
+ */
462
+ static bool HeifPrimaryPageReopen(VImage image, InputDescriptor *descriptor) {
463
+ if (image.get_typeof(VIPS_META_N_PAGES) == G_TYPE_INT && image.get_typeof("heif-primary") == G_TYPE_INT) {
464
+ if (image.get_int(VIPS_META_N_PAGES) > 1 && descriptor->pages == 1 && descriptor->page == -1) {
465
+ int const pagePrimary = image.get_int("heif-primary");
466
+ if (pagePrimary != 0) {
467
+ descriptor->page = pagePrimary;
468
+ return true;
469
+ }
470
+ }
471
+ }
472
+ return false;
473
+ }
474
+
357
475
  /*
358
476
  Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
359
477
  */
@@ -371,6 +489,10 @@ namespace sharp {
371
489
  } else {
372
490
  image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_RGB16;
373
491
  }
492
+ if (descriptor->rawPageHeight > 0) {
493
+ image.set(VIPS_META_PAGE_HEIGHT, descriptor->rawPageHeight);
494
+ image.set(VIPS_META_N_PAGES, static_cast<int>(descriptor->rawHeight / descriptor->rawPageHeight));
495
+ }
374
496
  if (descriptor->rawPremultiplied) {
375
497
  image = image.unpremultiply();
376
498
  }
@@ -380,37 +502,19 @@ namespace sharp {
380
502
  imageType = DetermineImageType(descriptor->buffer, descriptor->bufferLength);
381
503
  if (imageType != ImageType::UNKNOWN) {
382
504
  try {
383
- vips::VOption *option = VImage::option()
384
- ->set("access", descriptor->access)
385
- ->set("fail_on", descriptor->failOn);
386
- if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
387
- option->set("unlimited", true);
388
- }
389
- if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
390
- option->set("dpi", descriptor->density);
391
- }
392
- if (imageType == ImageType::MAGICK) {
393
- option->set("density", std::to_string(descriptor->density).data());
394
- }
395
- if (ImageTypeSupportsPage(imageType)) {
396
- option->set("n", descriptor->pages);
397
- option->set("page", descriptor->page);
398
- }
399
- if (imageType == ImageType::OPENSLIDE) {
400
- option->set("level", descriptor->level);
401
- }
402
- if (imageType == ImageType::TIFF) {
403
- option->set("subifd", descriptor->subifd);
404
- }
505
+ vips::VOption *option = GetOptionsForImageType(imageType, descriptor);
405
506
  image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
406
507
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
407
508
  image = SetDensity(image, descriptor->density);
509
+ } else if (imageType == ImageType::HEIF && HeifPrimaryPageReopen(image, descriptor)) {
510
+ option = GetOptionsForImageType(imageType, descriptor);
511
+ image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
408
512
  }
409
- } catch (vips::VError const &err) {
410
- throw vips::VError(std::string("Input buffer has corrupt header: ") + err.what());
513
+ } catch (std::runtime_error const &err) {
514
+ throw std::runtime_error(std::string("Input buffer has corrupt header: ") + err.what());
411
515
  }
412
516
  } else {
413
- throw vips::VError("Input buffer contains unsupported image format");
517
+ throw std::runtime_error("Input buffer contains unsupported image format");
414
518
  }
415
519
  }
416
520
  } else {
@@ -441,6 +545,10 @@ namespace sharp {
441
545
  channels < 3 ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_sRGB))
442
546
  .new_from_image(background);
443
547
  }
548
+ if (descriptor->createPageHeight > 0) {
549
+ image.set(VIPS_META_PAGE_HEIGHT, descriptor->createPageHeight);
550
+ image.set(VIPS_META_N_PAGES, static_cast<int>(descriptor->createHeight / descriptor->createPageHeight));
551
+ }
444
552
  image = image.cast(VIPS_FORMAT_UCHAR);
445
553
  imageType = ImageType::RAW;
446
554
  } else if (descriptor->textValue.length() > 0) {
@@ -477,44 +585,26 @@ namespace sharp {
477
585
  imageType = DetermineImageType(descriptor->file.data());
478
586
  if (imageType == ImageType::MISSING) {
479
587
  if (descriptor->file.find("<svg") != std::string::npos) {
480
- throw vips::VError("Input file is missing, did you mean "
588
+ throw std::runtime_error("Input file is missing, did you mean "
481
589
  "sharp(Buffer.from('" + descriptor->file.substr(0, 8) + "...')?");
482
590
  }
483
- throw vips::VError("Input file is missing: " + descriptor->file);
591
+ throw std::runtime_error("Input file is missing: " + descriptor->file);
484
592
  }
485
593
  if (imageType != ImageType::UNKNOWN) {
486
594
  try {
487
- vips::VOption *option = VImage::option()
488
- ->set("access", descriptor->access)
489
- ->set("fail_on", descriptor->failOn);
490
- if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
491
- option->set("unlimited", true);
492
- }
493
- if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
494
- option->set("dpi", descriptor->density);
495
- }
496
- if (imageType == ImageType::MAGICK) {
497
- option->set("density", std::to_string(descriptor->density).data());
498
- }
499
- if (ImageTypeSupportsPage(imageType)) {
500
- option->set("n", descriptor->pages);
501
- option->set("page", descriptor->page);
502
- }
503
- if (imageType == ImageType::OPENSLIDE) {
504
- option->set("level", descriptor->level);
505
- }
506
- if (imageType == ImageType::TIFF) {
507
- option->set("subifd", descriptor->subifd);
508
- }
595
+ vips::VOption *option = GetOptionsForImageType(imageType, descriptor);
509
596
  image = VImage::new_from_file(descriptor->file.data(), option);
510
597
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
511
598
  image = SetDensity(image, descriptor->density);
599
+ } else if (imageType == ImageType::HEIF && HeifPrimaryPageReopen(image, descriptor)) {
600
+ option = GetOptionsForImageType(imageType, descriptor);
601
+ image = VImage::new_from_file(descriptor->file.data(), option);
512
602
  }
513
- } catch (vips::VError const &err) {
514
- throw vips::VError(std::string("Input file has corrupt header: ") + err.what());
603
+ } catch (std::runtime_error const &err) {
604
+ throw std::runtime_error(std::string("Input file has corrupt header: ") + err.what());
515
605
  }
516
606
  } else {
517
- throw vips::VError("Input file contains unsupported image format");
607
+ throw std::runtime_error("Input file contains unsupported image format");
518
608
  }
519
609
  }
520
610
  }
@@ -522,7 +612,10 @@ namespace sharp {
522
612
  // Limit input images to a given number of pixels, where pixels = width * height
523
613
  if (descriptor->limitInputPixels > 0 &&
524
614
  static_cast<uint64_t>(image.width()) * image.height() > descriptor->limitInputPixels) {
525
- throw vips::VError("Input image exceeds pixel limit");
615
+ throw std::runtime_error("Input image exceeds pixel limit");
616
+ }
617
+ if (descriptor->limitInputChannels > 0 && static_cast<uint64_t>(image.bands()) > descriptor->limitInputChannels) {
618
+ throw std::runtime_error("Input image exceeds channel limit");
526
619
  }
527
620
  return std::make_tuple(image, imageType);
528
621
  }
@@ -542,7 +635,7 @@ namespace sharp {
542
635
  if (HasProfile(image)) {
543
636
  size_t length;
544
637
  const void *data = image.get_blob(VIPS_META_ICC_NAME, &length);
545
- icc.first = static_cast<char*>(g_malloc(length));
638
+ icc.first = static_cast<char*>(vips_malloc(reinterpret_cast<VipsObject*>(image.get_image()), length));
546
639
  icc.second = length;
547
640
  memcpy(icc.first, data, length);
548
641
  }
@@ -555,19 +648,11 @@ namespace sharp {
555
648
  VImage SetProfile(VImage image, std::pair<char*, size_t> icc) {
556
649
  if (icc.first != nullptr) {
557
650
  image = image.copy();
558
- image.set(VIPS_META_ICC_NAME, reinterpret_cast<VipsCallbackFn>(vips_area_free_cb), icc.first, icc.second);
651
+ image.set(VIPS_META_ICC_NAME, nullptr, icc.first, icc.second);
559
652
  }
560
653
  return image;
561
654
  }
562
655
 
563
- /*
564
- Does this image have an alpha channel?
565
- Uses colour space interpretation with number of channels to guess this.
566
- */
567
- bool HasAlpha(VImage image) {
568
- return image.has_alpha();
569
- }
570
-
571
656
  static void* RemoveExifCallback(VipsImage *image, char const *field, GValue *value, void *data) {
572
657
  std::vector<std::string> *fieldNames = static_cast<std::vector<std::string> *>(data);
573
658
  std::string fieldName(field);
@@ -624,22 +709,21 @@ namespace sharp {
624
709
  */
625
710
  VImage SetAnimationProperties(VImage image, int nPages, int pageHeight, std::vector<int> delay, int loop) {
626
711
  bool hasDelay = !delay.empty();
627
-
628
- // Avoid a copy if none of the animation properties are needed.
629
- if (nPages == 1 && !hasDelay && loop == -1) return image;
630
-
631
- if (delay.size() == 1) {
632
- // We have just one delay, repeat that value for all frames.
633
- delay.insert(delay.end(), nPages - 1, delay[0]);
634
- }
635
-
636
- // Attaching metadata, need to copy the image.
637
712
  VImage copy = image.copy();
638
713
 
639
714
  // Only set page-height if we have more than one page, or this could
640
715
  // accidentally turn into an animated image later.
641
716
  if (nPages > 1) copy.set(VIPS_META_PAGE_HEIGHT, pageHeight);
642
- if (hasDelay) copy.set("delay", delay);
717
+ if (hasDelay) {
718
+ if (delay.size() == 1) {
719
+ // We have just one delay, repeat that value for all frames.
720
+ delay.insert(delay.end(), nPages - 1, delay[0]);
721
+ }
722
+ copy.set("delay", delay);
723
+ }
724
+ if (nPages == 1 && !hasDelay && loop == -1) {
725
+ loop = 1;
726
+ }
643
727
  if (loop != -1) copy.set("loop", loop);
644
728
 
645
729
  return copy;
@@ -707,19 +791,19 @@ namespace sharp {
707
791
  : image.height();
708
792
  if (imageType == ImageType::JPEG) {
709
793
  if (image.width() > 65535 || height > 65535) {
710
- throw vips::VError("Processed image is too large for the JPEG format");
794
+ throw std::runtime_error("Processed image is too large for the JPEG format");
711
795
  }
712
796
  } else if (imageType == ImageType::WEBP) {
713
797
  if (image.width() > 16383 || height > 16383) {
714
- throw vips::VError("Processed image is too large for the WebP format");
798
+ throw std::runtime_error("Processed image is too large for the WebP format");
715
799
  }
716
800
  } else if (imageType == ImageType::GIF) {
717
801
  if (image.width() > 65535 || height > 65535) {
718
- throw vips::VError("Processed image is too large for the GIF format");
802
+ throw std::runtime_error("Processed image is too large for the GIF format");
719
803
  }
720
804
  } else if (imageType == ImageType::HEIF) {
721
805
  if (image.width() > 16384 || height > 16384) {
722
- throw vips::VError("Processed image is too large for the HEIF format");
806
+ throw std::runtime_error("Processed image is too large for the HEIF format");
723
807
  }
724
808
  }
725
809
  }
@@ -925,14 +1009,6 @@ namespace sharp {
925
1009
  return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16;
926
1010
  }
927
1011
 
928
- /*
929
- Return the image alpha maximum. Useful for combining alpha bands. scRGB
930
- images are 0 - 1 for image data, but the alpha is 0 - 255.
931
- */
932
- double MaximumImageAlpha(VipsInterpretation const interpretation) {
933
- return Is16Bit(interpretation) ? 65535.0 : 255.0;
934
- }
935
-
936
1012
  /*
937
1013
  Convert RGBA value to another colourspace
938
1014
  */
@@ -975,25 +1051,25 @@ namespace sharp {
975
1051
  0.0722 * colour[2])
976
1052
  };
977
1053
  }
978
- // Add alpha channel to alphaColour colour
979
- if (colour[3] < 255.0 || HasAlpha(image)) {
980
- alphaColour.push_back(colour[3] * multiplier);
1054
+ // Add alpha channel(s) to alphaColour colour
1055
+ if (colour[3] < 255.0 || image.has_alpha()) {
1056
+ int extraBands = image.bands() > 4 ? image.bands() - 3 : 1;
1057
+ alphaColour.insert(alphaColour.end(), extraBands, colour[3] * multiplier);
981
1058
  }
982
1059
  // Ensure alphaColour colour uses correct colourspace
983
1060
  alphaColour = sharp::GetRgbaAsColourspace(alphaColour, image.interpretation(), premultiply);
984
1061
  // Add non-transparent alpha channel, if required
985
- if (colour[3] < 255.0 && !HasAlpha(image)) {
986
- image = image.bandjoin(
987
- VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier).cast(image.format()));
1062
+ if (colour[3] < 255.0 && !image.has_alpha()) {
1063
+ image = image.bandjoin_const({ 255 * multiplier });
988
1064
  }
989
1065
  return std::make_tuple(image, alphaColour);
990
1066
  }
991
1067
 
992
1068
  /*
993
- Removes alpha channel, if any.
1069
+ Removes alpha channels, if any.
994
1070
  */
995
1071
  VImage RemoveAlpha(VImage image) {
996
- if (HasAlpha(image)) {
1072
+ while (image.bands() > 1 && image.has_alpha()) {
997
1073
  image = image.extract_band(0, VImage::option()->set("n", image.bands() - 1));
998
1074
  }
999
1075
  return image;
@@ -1003,10 +1079,8 @@ namespace sharp {
1003
1079
  Ensures alpha channel, if missing.
1004
1080
  */
1005
1081
  VImage EnsureAlpha(VImage image, double const value) {
1006
- if (!HasAlpha(image)) {
1007
- std::vector<double> alpha;
1008
- alpha.push_back(value * sharp::MaximumImageAlpha(image.interpretation()));
1009
- image = image.bandjoin_const(alpha);
1082
+ if (!image.has_alpha()) {
1083
+ image = image.bandjoin_const({ value * vips_interpretation_max_alpha(image.interpretation()) });
1010
1084
  }
1011
1085
  return image;
1012
1086
  }
@@ -1088,4 +1162,22 @@ namespace sharp {
1088
1162
  }
1089
1163
  return image;
1090
1164
  }
1165
+
1166
+ /*
1167
+ Does this image have a gain map?
1168
+ */
1169
+ bool HasGainMap(VImage image) {
1170
+ return image.get_typeof("gainmap") == VIPS_TYPE_BLOB ||
1171
+ image.get_typeof("gainmap-data") == VIPS_TYPE_BLOB;
1172
+ }
1173
+
1174
+ /*
1175
+ Removes gain map, if any.
1176
+ */
1177
+ VImage RemoveGainMap(VImage image) {
1178
+ VImage copy = image.copy();
1179
+ copy.remove("gainmap");
1180
+ copy.remove("gainmap-data");
1181
+ return copy;
1182
+ }
1091
1183
  } // namespace sharp