Skip to content

colour: use suggested rendering intent as fallback #4347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- gifsave: add support for eval callback, ensure correct return code [lovell]
- tiffsave: honor disc threshold during pyramid save [kleisauke]
- fill_nearest: fix a leak
- colour: use suggested rendering intent as fallback [kleisauke]

10/10/24 8.16.0

Expand Down
38 changes: 26 additions & 12 deletions libvips/colour/icc_transform.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ typedef struct _VipsIcc {
int depth;
gboolean black_point_compensation;

VipsIntent selected_intent;

VipsBlob *in_blob;
cmsHPROFILE in_profile;
VipsBlob *out_blob;
Expand Down Expand Up @@ -446,7 +448,7 @@ vips_icc_build(VipsObject *object)
if (!(icc->trans = cmsCreateTransform(
icc->in_profile, icc->in_icc_format,
icc->out_profile, icc->out_icc_format,
icc->intent, flags)))
icc->selected_intent, flags)))
return -1;

if (VIPS_OBJECT_CLASS(vips_icc_parent_class)->build(object))
Expand Down Expand Up @@ -596,8 +598,8 @@ vips_image_is_profile_compatible(VipsImage *image, int profile_bands)
* Don't set any errors since this is used to test compatibility.
*/
static cmsHPROFILE
vips_icc_load_profile_blob(VipsBlob *blob,
VipsImage *image, VipsIntent intent, int direction)
vips_icc_load_profile_blob(VipsIcc *icc, VipsBlob *blob,
VipsImage *image, int direction)
{
const void *data;
size_t size;
Expand All @@ -607,7 +609,7 @@ vips_icc_load_profile_blob(VipsBlob *blob,
#ifdef DEBUG
printf("loading %s profile, intent %s, from blob %p\n",
direction == LCMS_USED_AS_INPUT ? _("input") : _("output"),
vips_enum_nick(VIPS_TYPE_INTENT, intent),
vips_enum_nick(VIPS_TYPE_INTENT, icc->intent),
blob);
#endif /*DEBUG*/

Expand All @@ -617,6 +619,18 @@ vips_icc_load_profile_blob(VipsBlob *blob,
return NULL;
}

icc->selected_intent = icc->intent;
if (!cmsIsIntentSupported(profile, icc->intent, direction)) {
icc->selected_intent = (VipsIntent) cmsGetHeaderRenderingIntent(
profile);

g_warning(_("fallback to suggested %s intent, as profile "
"does not support %s %s intent"),
vips_enum_nick(VIPS_TYPE_INTENT, icc->selected_intent),
vips_enum_nick(VIPS_TYPE_INTENT, icc->intent),
direction == LCMS_USED_AS_INPUT ? _("input") : _("output"));
}

#ifdef DEBUG
vips_icc_print_profile("loaded from blob to make", profile);
#endif /*DEBUG*/
Expand All @@ -634,10 +648,10 @@ vips_icc_load_profile_blob(VipsBlob *blob,
return NULL;
}

if (!cmsIsIntentSupported(profile, intent, direction)) {
if (!cmsIsIntentSupported(profile, icc->selected_intent, direction)) {
VIPS_FREEF(cmsCloseProfile, profile);
g_warning(_("profile does not support %s %s intent"),
vips_enum_nick(VIPS_TYPE_INTENT, intent),
vips_enum_nick(VIPS_TYPE_INTENT, icc->selected_intent),
direction == LCMS_USED_AS_INPUT ? _("input") : _("output"));
return NULL;
}
Expand All @@ -654,8 +668,8 @@ vips_icc_verify_blob(VipsIcc *icc, VipsBlob **blob)
{
if (*blob) {
VipsColourCode *code = (VipsColourCode *) icc;
cmsHPROFILE profile = vips_icc_load_profile_blob(*blob,
code->in, icc->intent, LCMS_USED_AS_INPUT);
cmsHPROFILE profile = vips_icc_load_profile_blob(icc, *blob,
code->in, LCMS_USED_AS_INPUT);

if (!profile) {
vips_area_unref((VipsArea *) *blob);
Expand Down Expand Up @@ -1024,8 +1038,8 @@ vips_icc_export_build(VipsObject *object)
}

if (icc->out_blob &&
!(icc->out_profile = vips_icc_load_profile_blob(icc->out_blob,
NULL, icc->intent, LCMS_USED_AS_OUTPUT))) {
!(icc->out_profile = vips_icc_load_profile_blob(icc, icc->out_blob,
NULL, LCMS_USED_AS_OUTPUT))) {
vips_error(class->nickname, "%s", _("no output profile"));
return -1;
}
Expand Down Expand Up @@ -1188,8 +1202,8 @@ vips_icc_transform_build(VipsObject *object)
}

if (icc->out_blob)
icc->out_profile = vips_icc_load_profile_blob(icc->out_blob,
NULL, icc->intent, LCMS_USED_AS_OUTPUT);
icc->out_profile = vips_icc_load_profile_blob(icc, icc->out_blob,
NULL, LCMS_USED_AS_OUTPUT);

if (!icc->out_profile) {
vips_error(class->nickname, "%s", _("no output profile"));
Expand Down
2 changes: 2 additions & 0 deletions libvips/include/vips/colour.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ extern "C" {
#define VIPS_D3250_Y0 (100.0)
#define VIPS_D3250_Z0 (45.8501)

/* Note: constants align with those defined in lcms2.h.
*/
typedef enum {
VIPS_INTENT_PERCEPTUAL = 0,
VIPS_INTENT_RELATIVE,
Expand Down
2 changes: 1 addition & 1 deletion test/test-suite/test_resample.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ def test_thumbnail(self):
@pytest.mark.skipif(not pyvips.at_least_libvips(8, 5),
reason="requires libvips >= 8.5")
def test_thumbnail_icc(self):
im = pyvips.Image.thumbnail(JPEG_FILE_XYB, 442, export_profile="srgb", intent="perceptual")
im = pyvips.Image.thumbnail(JPEG_FILE_XYB, 442, export_profile="srgb")

assert im.width == 290
assert im.height == 442
Expand Down
Loading