diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bc095a2..23d12ee5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fix - Fix parsing of HEIF files with large boxes. - Fix wrong colors when the source image has a linear colorspace. +- Fix wrong colors or opacity when the source image is a TIFF with a float sample format. ## [3.19.0] - 2023-08-21 ### Add diff --git a/vips/vips.c b/vips/vips.c index c2abf67a..d222739a 100644 --- a/vips/vips.c +++ b/vips/vips.c @@ -15,6 +15,9 @@ #define VIPS_GIF_RESOLUTION_LIMITED \ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION <= 12) +#define VIPS_SCRGB_ALPHA_FIXED \ + (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 15)) + #ifndef VIPS_META_BITS_PER_SAMPLE #define VIPS_META_BITS_PER_SAMPLE "palette-bit-depth" #endif @@ -139,6 +142,110 @@ vips_black_go(VipsImage **out, int width, int height, int bands) { return res; } +/* Vips loads linear alpha in the 0.0-1.0 range but uses the 0.0-255.0 range. + * https://github.com/libvips/libvips/pull/3627 fixes this behavior + */ +int +vips_fix_scRGB_alpha_tiff(VipsImage *in, VipsImage **out) { +#if VIPS_SCRGB_ALPHA_FIXED + #warning Revise vips_fix_scRGB_tiff + return vips_copy(in, out, NULL); +#else + VipsImage *base = vips_image_new(); + VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 4); + + int res = + vips_extract_band(in, &t[0], 0, "n", 3, NULL) || + vips_extract_band(in, &t[1], 3, "n", in->Bands - 3, NULL) || + vips_linear1(t[1], &t[2], 255.0, 0, NULL) || + vips_cast(t[2], &t[3], in->BandFmt, NULL) || + vips_bandjoin2(t[0], t[3], out, NULL); + + clear_image(&base); + + return res; +#endif +} + +/* Vips loads linear BW TIFFs as VIPS_INTERPRETATION_B_W or VIPS_INTERPRETATION_GREY16 + * but these colourspaces are not linear. We should properly convert them to + * VIPS_INTERPRETATION_GREY16 + */ +int +vips_fix_BW_float_tiff(VipsImage *in, VipsImage **out) { + VipsImage *base = vips_image_new(); + VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 8); + + VipsImage *color = in; + VipsImage *alpha = NULL; + + /* Extract and fix alpha. Float WB TIFF uses the 0.0-1.0 range but we need + * the 0.0-65535.0 range + */ + if (in->Bands > 1) { + if ( + vips_extract_band(in, &t[0], 0, NULL) || + vips_extract_band(in, &t[1], 1, "n", in->Bands - 1, NULL) || + vips_linear1(t[1], &t[2], 65535.0, 0, NULL) || + vips_cast_ushort(t[2], &t[3], NULL) || + vips_copy(t[3], &t[4], "interpretation", VIPS_INTERPRETATION_GREY16, NULL) + ) { + clear_image(&base); + return 1; + } + + color = t[0]; + alpha = t[4]; + } + + /* Craft an scRGB image and convert it back to GREY16 to apply a gamma + * correction + */ + VipsImage *rgb[3] = { color, color, color }; + if ( + vips_bandjoin(rgb, &t[5], 3, NULL) || + vips_colourspace(t[5], &t[6], VIPS_INTERPRETATION_GREY16, + "source_space", VIPS_INTERPRETATION_scRGB, NULL) + ) { + clear_image(&base); + return 1; + } + + int res; + + if (alpha) + res = + vips_bandjoin2(t[6], alpha, &t[7], NULL) || + vips_icc_remove(t[7], out); + else + res = vips_icc_remove(t[6], out); + + clear_image(&base); + + return res; +} + +int +vips_fix_float_tiff(VipsImage *in, VipsImage **out) { + /* Vips loads linear alpha in the 0.0-1.0 range but uses the 0.0-255.0 range. + * https://github.com/libvips/libvips/pull/3627 fixes this behavior + */ + if (in->Type == VIPS_INTERPRETATION_scRGB && in->Bands > 3) + return vips_fix_scRGB_alpha_tiff(in, out); + + /* Vips loads linear BW TIFFs as VIPS_INTERPRETATION_B_W or VIPS_INTERPRETATION_GREY16 + * but these colourspaces are not linear. We should properly convert them to + * VIPS_INTERPRETATION_GREY16 + */ + if ( + (in->Type == VIPS_INTERPRETATION_B_W || in->Type == VIPS_INTERPRETATION_GREY16) && + (in->BandFmt == VIPS_FORMAT_FLOAT || in->BandFmt == VIPS_FORMAT_DOUBLE) + ) + return vips_fix_BW_float_tiff(in, out); + + return vips_copy(in, out); +} + int vips_get_orientation(VipsImage *image) { int orientation; diff --git a/vips/vips.go b/vips/vips.go index bb6980e1..938e83fd 100644 --- a/vips/vips.go +++ b/vips/vips.go @@ -322,6 +322,14 @@ func (img *Image) Load(imgdata *imagedata.ImageData, shrink int, scale float64, C.swap_and_clear(&img.VipsImage, tmp) + if imgdata.Type == imagetype.TIFF { + if C.vips_fix_float_tiff(img.VipsImage, &tmp) == 0 { + C.swap_and_clear(&img.VipsImage, tmp) + } else { + log.Warnf("Can't fix TIFF: %s", Error()) + } + } + return nil } diff --git a/vips/vips.h b/vips/vips.h index e06cef09..74637b39 100644 --- a/vips/vips.h +++ b/vips/vips.h @@ -25,6 +25,8 @@ int vips_tiffload_go(void *buf, size_t len, VipsImage **out); int vips_black_go(VipsImage **out, int width, int height, int bands); +int vips_fix_float_tiff(VipsImage *in, VipsImage **out); + int vips_get_orientation(VipsImage *image); void vips_strip_meta(VipsImage *image);