Skip to content

Commit

Permalink
Ensure embedded profile, if any, is always used
Browse files Browse the repository at this point in the history
Perform sRGB conversion at end of pipe only

withMetadata exports profile, should not convert

Convert one fixture to sRGB to help test

Discovered while investigating #125
  • Loading branch information
lovell committed Nov 25, 2014
1 parent 02b6016 commit f57a0e3
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 38 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

The typical use case for this high speed Node.js module is to convert large images of many formats to smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.

This module supports reading and writing JPEG, PNG and WebP images to and from Streams, Buffer objects and the filesystem. It also supports reading images of many other types from the filesystem via libmagick++ or libgraphicsmagick++ if present.
This module supports reading and writing JPEG, PNG and WebP images to and from Streams, Buffer objects and the filesystem.
It also supports reading images of many other types from the filesystem via libmagick++ or libgraphicsmagick++ if present.
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.

Only small regions of uncompressed image data are held in memory and processed at a time, taking full advantage of multiple CPU cores and L1/L2/L3 cache. Resizing an image is typically 4x faster than using the quickest ImageMagick and GraphicsMagick settings.

Expand Down Expand Up @@ -401,7 +403,9 @@ Use progressive (interlace) scan for JPEG and PNG output. This typically reduces

#### withMetadata()

Include all metadata (ICC, EXIF, XMP) from the input image in the output image. The default behaviour is to strip all metadata.
Include all metadata (ICC, EXIF, XMP) from the input image in the output image.

The default behaviour is to strip all metadata and convert to the device-independent sRGB colour space.

#### compressionLevel(compressionLevel)

Expand Down
7 changes: 7 additions & 0 deletions src/common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ namespace sharp {
return image;
}

/*
Does this image have an embedded profile?
*/
bool HasProfile(VipsImage *image) {
return (vips_image_get_typeof(image, VIPS_META_ICC_NAME) > 0) ? TRUE : FALSE;
}

/*
Does this image have an alpha channel?
Uses colour space interpretation with number of channels to guess this.
Expand Down
5 changes: 5 additions & 0 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ namespace sharp {
*/
VipsImage* InitImage(ImageType imageType, char const *file, VipsAccess const access);

/*
Does this image have an embedded profile?
*/
bool HasProfile(VipsImage *image);

/*
Does this image have an alpha channel?
Uses colour space interpretation with number of channels to guess this.
Expand Down
2 changes: 1 addition & 1 deletion src/metadata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class MetadataWorker : public NanAsyncWorker {
baton->height = image->Ysize;
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type);
baton->channels = image->Bands;
baton->hasProfile = (vips_image_get_typeof(image, VIPS_META_ICC_NAME) > 0) ? TRUE : FALSE;
baton->hasProfile = HasProfile(image);
// Derived attributes
baton->hasAlpha = HasAlpha(image);
baton->orientation = ExifOrientation(image);
Expand Down
58 changes: 28 additions & 30 deletions src/resize.cc
Original file line number Diff line number Diff line change
Expand Up @@ -269,33 +269,23 @@ class ResizeWorker : public NanAsyncWorker {
image = shrunkOnLoad;
}

// Handle colour profile, if any, for non sRGB images
if (image->Type != VIPS_INTERPRETATION_sRGB) {
// Get the input colour profile
if (vips_image_get_typeof(image, VIPS_META_ICC_NAME)) {
VipsImage *profile;
// Use embedded profile
if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "embedded", TRUE, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, profile);
image = profile;
} else if (image->Type == VIPS_INTERPRETATION_CMYK) {
VipsImage *profile;
// CMYK with no embedded profile
if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "input_profile", (baton->iccProfileCmyk).c_str(), NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, profile);
image = profile;
// Ensure we're using a device-independent colour space
if (HasProfile(image)) {
// Convert to CIELAB using embedded profile
VipsImage *profile;
if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "embedded", TRUE, NULL)) {
return Error(baton, hook);
}
// Attempt to convert to sRGB colour space
VipsImage *colourspaced;
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
vips_object_local(hook, profile);
image = profile;
} else if (image->Type == VIPS_INTERPRETATION_CMYK) {
// Convert to CIELAB using default "USWebCoatedSWOP" CMYK profile
VipsImage *profile;
if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "input_profile", (baton->iccProfileCmyk).c_str(), NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, colourspaced);
image = colourspaced;
vips_object_local(hook, profile);
image = profile;
}

// Flatten image to remove alpha channel
Expand Down Expand Up @@ -578,14 +568,22 @@ class ResizeWorker : public NanAsyncWorker {
image = gammaDecoded;
}

// Convert to sRGB colour space, if not already
// Convert colour space to either sRGB or RGB-with-profile, if not already
if (image->Type != VIPS_INTERPRETATION_sRGB) {
VipsImage *colourspaced;
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
return Error(baton, hook);
VipsImage *rgb;
if (baton->withMetadata && HasProfile(image)) {
// Convert to device-dependent RGB using embedded profile of input
if (vips_icc_export(image, &rgb, NULL)) {
return Error(baton, hook);
}
} else {
// Convert to device-independent sRGB
if (vips_colourspace(image, &rgb, VIPS_INTERPRETATION_sRGB, NULL)) {
return Error(baton, hook);
}
}
vips_object_local(hook, colourspaced);
image = colourspaced;
vips_object_local(hook, rgb);
image = rgb;
}

#if !(VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40 && VIPS_MINOR_VERSION >= 5)
Expand Down
Binary file modified test/fixtures/2569067123_aca715a2ee_o.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions test/unit/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('Image metadata', function() {
assert.strictEqual(2225, metadata.height);
assert.strictEqual('srgb', metadata.space);
assert.strictEqual(3, metadata.channels);
assert.strictEqual(true, metadata.hasProfile);
assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha);
assert.strictEqual('undefined', typeof metadata.orientation);
done();
Expand Down Expand Up @@ -116,7 +116,7 @@ describe('Image metadata', function() {
assert.strictEqual(2225, metadata.height);
assert.strictEqual('srgb', metadata.space);
assert.strictEqual(3, metadata.channels);
assert.strictEqual(true, metadata.hasProfile);
assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha);
done();
});
Expand All @@ -131,7 +131,7 @@ describe('Image metadata', function() {
assert.strictEqual(2225, metadata.height);
assert.strictEqual('srgb', metadata.space);
assert.strictEqual(3, metadata.channels);
assert.strictEqual(true, metadata.hasProfile);
assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha);
done();
}).catch(function(err) {
Expand All @@ -149,7 +149,7 @@ describe('Image metadata', function() {
assert.strictEqual(2225, metadata.height);
assert.strictEqual('srgb', metadata.space);
assert.strictEqual(3, metadata.channels);
assert.strictEqual(true, metadata.hasProfile);
assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha);
done();
});
Expand All @@ -165,7 +165,7 @@ describe('Image metadata', function() {
assert.strictEqual(2225, metadata.height);
assert.strictEqual('srgb', metadata.space);
assert.strictEqual(3, metadata.channels);
assert.strictEqual(true, metadata.hasProfile);
assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha);
image.resize(metadata.width / 2).toBuffer(function(err, data, info) {
if (err) throw err;
Expand Down

0 comments on commit f57a0e3

Please sign in to comment.