@revizly/sharp 0.35.0-revizly52 → 0.35.0-revizly53

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/dist/index.d.cts CHANGED
@@ -775,7 +775,7 @@ declare namespace sharp {
775
775
  * Use these JPEG-XL (JXL) options for output image.
776
776
  * This feature is experimental, please do not use in production systems.
777
777
  * Requires libvips compiled with support for libjxl.
778
- * The prebuilt binaries do not include this.
778
+ * The `@revizly` prebuilt binaries include this.
779
779
  * Image metadata (EXIF, XMP) is unsupported.
780
780
  * @param options Output options.
781
781
  * @throws {Error} Invalid options
package/dist/index.d.mts CHANGED
@@ -769,7 +769,7 @@ export interface Sharp extends Duplex {
769
769
  * Use these JPEG-XL (JXL) options for output image.
770
770
  * This feature is experimental, please do not use in production systems.
771
771
  * Requires libvips compiled with support for libjxl.
772
- * The prebuilt binaries do not include this.
772
+ * The `@revizly` prebuilt binaries include this.
773
773
  * Image metadata (EXIF, XMP) is unsupported.
774
774
  * @param options Output options.
775
775
  * @throws {Error} Invalid options
package/dist/output.cjs CHANGED
@@ -1326,8 +1326,7 @@ function heif (options) {
1326
1326
  * This feature is experimental, please do not use in production systems.
1327
1327
  *
1328
1328
  * Requires libvips compiled with support for libjxl.
1329
- * The prebuilt binaries do not include this - see
1330
- * {@link /install/#custom-libvips installing a custom libvips}.
1329
+ * The `@revizly` prebuilt binaries include this.
1331
1330
  *
1332
1331
  * @since 0.31.3
1333
1332
  *
package/dist/output.mjs CHANGED
@@ -1326,8 +1326,7 @@ function heif (options) {
1326
1326
  * This feature is experimental, please do not use in production systems.
1327
1327
  *
1328
1328
  * Requires libvips compiled with support for libjxl.
1329
- * The prebuilt binaries do not include this - see
1330
- * {@link /install/#custom-libvips installing a custom libvips}.
1329
+ * The `@revizly` prebuilt binaries include this.
1331
1330
  *
1332
1331
  * @since 0.31.3
1333
1332
  *
package/lib/index.d.ts CHANGED
@@ -775,7 +775,7 @@ declare namespace sharp {
775
775
  * Use these JPEG-XL (JXL) options for output image.
776
776
  * This feature is experimental, please do not use in production systems.
777
777
  * Requires libvips compiled with support for libjxl.
778
- * The prebuilt binaries do not include this.
778
+ * The `@revizly` prebuilt binaries include this.
779
779
  * Image metadata (EXIF, XMP) is unsupported.
780
780
  * @param options Output options.
781
781
  * @throws {Error} Invalid options
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@revizly/sharp",
3
3
  "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
4
- "version": "0.35.0-revizly52",
4
+ "version": "0.35.0-revizly53",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://sharp.pixelplumbing.com",
7
7
  "contributors": [
@@ -163,10 +163,10 @@
163
163
  "semver": "^7.8.5"
164
164
  },
165
165
  "optionalDependencies": {
166
- "@revizly/sharp-libvips-linux-arm64": "1.0.53",
167
- "@revizly/sharp-libvips-linux-x64": "1.0.53",
168
- "@revizly/sharp-linux-arm64": "0.35.0-revizly52",
169
- "@revizly/sharp-linux-x64": "0.35.0-revizly52"
166
+ "@revizly/sharp-libvips-linux-arm64": "1.0.54",
167
+ "@revizly/sharp-libvips-linux-x64": "1.0.54",
168
+ "@revizly/sharp-linux-arm64": "0.35.0-revizly53",
169
+ "@revizly/sharp-linux-x64": "0.35.0-revizly53"
170
170
  },
171
171
  "peerDependenciesMeta": {
172
172
  "@types/node": {
@@ -177,7 +177,7 @@
177
177
  "@biomejs/biome": "^2.5.14",
178
178
  "@cpplint/cli": "^0.1.0",
179
179
  "@emnapi/runtime": "^1.11.3",
180
- "@revizly/sharp-libvips-dev": "1.0.53",
180
+ "@revizly/sharp-libvips-dev": "1.0.54",
181
181
  "@types/node": "*",
182
182
  "emnapi": "^1.11.3",
183
183
  "exif-reader": "^2.0.3",
package/src/pipeline.cc CHANGED
@@ -78,7 +78,20 @@ class PipelineWorker : public Napi::AsyncWorker {
78
78
  }
79
79
  }
80
80
  VipsAccess access = baton->input->access;
81
- image = sharp::EnsureColourspace(image, baton->colourspacePipeline);
81
+ // A device-independent colourspace is reached through the ICC profile connection space, either Lab or XYZ
82
+ bool const labPcs =
83
+ baton->colourspacePipeline == VIPS_INTERPRETATION_LAB ||
84
+ baton->colourspacePipeline == VIPS_INTERPRETATION_LABS ||
85
+ baton->colourspacePipeline == VIPS_INTERPRETATION_LCH;
86
+ bool const xyzPcs =
87
+ baton->colourspacePipeline == VIPS_INTERPRETATION_scRGB ||
88
+ baton->colourspacePipeline == VIPS_INTERPRETATION_XYZ ||
89
+ baton->colourspacePipeline == VIPS_INTERPRETATION_YXY;
90
+ // lcms needs 8/16-bit device values, so the conversion must wait for the import
91
+ bool const deferColourspace = sharp::HasProfile(image) && (labPcs || xyzPcs);
92
+ if (!deferColourspace) {
93
+ image = sharp::EnsureColourspace(image, baton->colourspacePipeline);
94
+ }
82
95
 
83
96
  int nPages = baton->input->pages;
84
97
  if (nPages == -1) {
@@ -372,6 +385,7 @@ class PipelineWorker : public Napi::AsyncWorker {
372
385
  baton->input->ignoreIcc = true;
373
386
  }
374
387
  char const *processingProfile = image.interpretation() == VIPS_INTERPRETATION_RGB16 ? "p3" : "srgb";
388
+ bool importedToPcs = false;
375
389
  if (
376
390
  sharp::HasProfile(image) &&
377
391
  image.interpretation() != VIPS_INTERPRETATION_LABS &&
@@ -381,10 +395,20 @@ class PipelineWorker : public Napi::AsyncWorker {
381
395
  ) {
382
396
  // Convert to sRGB/P3 using embedded profile
383
397
  try {
384
- image = image.icc_transform(processingProfile, VImage::option()
385
- ->set("embedded", true)
386
- ->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
387
- ->set("intent", VIPS_INTENT_PERCEPTUAL));
398
+ if (deferColourspace &&
399
+ (image.format() == VIPS_FORMAT_UCHAR || image.format() == VIPS_FORMAT_USHORT)) {
400
+ // XYZ PCS is linear and device-independent, so colour management and linearisation are a single operation
401
+ image = image.icc_import(VImage::option()
402
+ ->set("embedded", true)
403
+ ->set("pcs", labPcs ? VIPS_PCS_LAB : VIPS_PCS_XYZ)
404
+ ->set("intent", VIPS_INTENT_PERCEPTUAL));
405
+ importedToPcs = true;
406
+ } else {
407
+ image = image.icc_transform(processingProfile, VImage::option()
408
+ ->set("embedded", true)
409
+ ->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
410
+ ->set("intent", VIPS_INTENT_PERCEPTUAL));
411
+ }
388
412
  } catch(...) {
389
413
  sharp::VipsWarningCallback(nullptr, G_LOG_LEVEL_WARNING, "Invalid embedded profile", nullptr);
390
414
  }
@@ -397,6 +421,10 @@ class PipelineWorker : public Napi::AsyncWorker {
397
421
  ->set("intent", VIPS_INTENT_PERCEPTUAL));
398
422
  }
399
423
 
424
+ if (deferColourspace) {
425
+ image = sharp::EnsureColourspace(image, baton->colourspacePipeline);
426
+ }
427
+
400
428
  // Flatten image to remove alpha channel
401
429
  if (baton->flatten && image.has_alpha()) {
402
430
  image = sharp::Flatten(image, baton->flattenBackground);
@@ -882,6 +910,19 @@ class PipelineWorker : public Napi::AsyncWorker {
882
910
  image = sharp::EnsureAlpha(image, baton->ensureAlpha);
883
911
  }
884
912
 
913
+ // Leave PCS before the output colourspace is applied, which would gamut-clip
914
+ if (importedToPcs) {
915
+ try {
916
+ image = image.colourspace(VIPS_INTERPRETATION_XYZ).icc_export(VImage::option()
917
+ ->set("output_profile", baton->withIccProfile.empty()
918
+ ? processingProfile
919
+ : const_cast<char*>(baton->withIccProfile.data()))
920
+ ->set("intent", VIPS_INTENT_PERCEPTUAL));
921
+ } catch(...) {
922
+ sharp::VipsWarningCallback(nullptr, G_LOG_LEVEL_WARNING, "Invalid profile", nullptr);
923
+ }
924
+ }
925
+
885
926
  // Ensure output colour space
886
927
  if (sharp::Is16Bit(image.interpretation())) {
887
928
  image = image.cast(VIPS_FORMAT_USHORT);
@@ -919,7 +960,7 @@ class PipelineWorker : public Napi::AsyncWorker {
919
960
  }
920
961
 
921
962
  // Apply output ICC profile
922
- if (!baton->withIccProfile.empty()) {
963
+ if (!importedToPcs && !baton->withIccProfile.empty()) {
923
964
  try {
924
965
  image = image.icc_transform(const_cast<char*>(baton->withIccProfile.data()), VImage::option()
925
966
  ->set("input_profile", processingProfile)