Browse Source

Fix float TIFF opacity and linear BW TIFF colors

DarthSim 1 year ago
parent
commit
4e1d556458
4 changed files with 118 additions and 0 deletions
  1. 1 0
      CHANGELOG.md
  2. 107 0
      vips/vips.c
  3. 8 0
      vips/vips.go
  4. 2 0
      vips/vips.h

+ 1 - 0
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

+ 107 - 0
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;

+ 8 - 0
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
 }
 

+ 2 - 0
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);