Skip to content

Commit 4e1d556

Browse files
committed
Fix float TIFF opacity and linear BW TIFF colors
1 parent 46e6346 commit 4e1d556

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
### Fix
55
- Fix parsing of HEIF files with large boxes.
66
- Fix wrong colors when the source image has a linear colorspace.
7+
- Fix wrong colors or opacity when the source image is a TIFF with a float sample format.
78

89
## [3.19.0] - 2023-08-21
910
### Add

vips/vips.c

+107
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
#define VIPS_GIF_RESOLUTION_LIMITED \
1616
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION <= 12)
1717

18+
#define VIPS_SCRGB_ALPHA_FIXED \
19+
(VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 15))
20+
1821
#ifndef VIPS_META_BITS_PER_SAMPLE
1922
#define VIPS_META_BITS_PER_SAMPLE "palette-bit-depth"
2023
#endif
@@ -139,6 +142,110 @@ vips_black_go(VipsImage **out, int width, int height, int bands) {
139142
return res;
140143
}
141144

145+
/* Vips loads linear alpha in the 0.0-1.0 range but uses the 0.0-255.0 range.
146+
* https://github.com/libvips/libvips/pull/3627 fixes this behavior
147+
*/
148+
int
149+
vips_fix_scRGB_alpha_tiff(VipsImage *in, VipsImage **out) {
150+
#if VIPS_SCRGB_ALPHA_FIXED
151+
#warning Revise vips_fix_scRGB_tiff
152+
return vips_copy(in, out, NULL);
153+
#else
154+
VipsImage *base = vips_image_new();
155+
VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 4);
156+
157+
int res =
158+
vips_extract_band(in, &t[0], 0, "n", 3, NULL) ||
159+
vips_extract_band(in, &t[1], 3, "n", in->Bands - 3, NULL) ||
160+
vips_linear1(t[1], &t[2], 255.0, 0, NULL) ||
161+
vips_cast(t[2], &t[3], in->BandFmt, NULL) ||
162+
vips_bandjoin2(t[0], t[3], out, NULL);
163+
164+
clear_image(&base);
165+
166+
return res;
167+
#endif
168+
}
169+
170+
/* Vips loads linear BW TIFFs as VIPS_INTERPRETATION_B_W or VIPS_INTERPRETATION_GREY16
171+
* but these colourspaces are not linear. We should properly convert them to
172+
* VIPS_INTERPRETATION_GREY16
173+
*/
174+
int
175+
vips_fix_BW_float_tiff(VipsImage *in, VipsImage **out) {
176+
VipsImage *base = vips_image_new();
177+
VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 8);
178+
179+
VipsImage *color = in;
180+
VipsImage *alpha = NULL;
181+
182+
/* Extract and fix alpha. Float WB TIFF uses the 0.0-1.0 range but we need
183+
* the 0.0-65535.0 range
184+
*/
185+
if (in->Bands > 1) {
186+
if (
187+
vips_extract_band(in, &t[0], 0, NULL) ||
188+
vips_extract_band(in, &t[1], 1, "n", in->Bands - 1, NULL) ||
189+
vips_linear1(t[1], &t[2], 65535.0, 0, NULL) ||
190+
vips_cast_ushort(t[2], &t[3], NULL) ||
191+
vips_copy(t[3], &t[4], "interpretation", VIPS_INTERPRETATION_GREY16, NULL)
192+
) {
193+
clear_image(&base);
194+
return 1;
195+
}
196+
197+
color = t[0];
198+
alpha = t[4];
199+
}
200+
201+
/* Craft an scRGB image and convert it back to GREY16 to apply a gamma
202+
* correction
203+
*/
204+
VipsImage *rgb[3] = { color, color, color };
205+
if (
206+
vips_bandjoin(rgb, &t[5], 3, NULL) ||
207+
vips_colourspace(t[5], &t[6], VIPS_INTERPRETATION_GREY16,
208+
"source_space", VIPS_INTERPRETATION_scRGB, NULL)
209+
) {
210+
clear_image(&base);
211+
return 1;
212+
}
213+
214+
int res;
215+
216+
if (alpha)
217+
res =
218+
vips_bandjoin2(t[6], alpha, &t[7], NULL) ||
219+
vips_icc_remove(t[7], out);
220+
else
221+
res = vips_icc_remove(t[6], out);
222+
223+
clear_image(&base);
224+
225+
return res;
226+
}
227+
228+
int
229+
vips_fix_float_tiff(VipsImage *in, VipsImage **out) {
230+
/* Vips loads linear alpha in the 0.0-1.0 range but uses the 0.0-255.0 range.
231+
* https://github.com/libvips/libvips/pull/3627 fixes this behavior
232+
*/
233+
if (in->Type == VIPS_INTERPRETATION_scRGB && in->Bands > 3)
234+
return vips_fix_scRGB_alpha_tiff(in, out);
235+
236+
/* Vips loads linear BW TIFFs as VIPS_INTERPRETATION_B_W or VIPS_INTERPRETATION_GREY16
237+
* but these colourspaces are not linear. We should properly convert them to
238+
* VIPS_INTERPRETATION_GREY16
239+
*/
240+
if (
241+
(in->Type == VIPS_INTERPRETATION_B_W || in->Type == VIPS_INTERPRETATION_GREY16) &&
242+
(in->BandFmt == VIPS_FORMAT_FLOAT || in->BandFmt == VIPS_FORMAT_DOUBLE)
243+
)
244+
return vips_fix_BW_float_tiff(in, out);
245+
246+
return vips_copy(in, out);
247+
}
248+
142249
int
143250
vips_get_orientation(VipsImage *image) {
144251
int orientation;

vips/vips.go

+8
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,14 @@ func (img *Image) Load(imgdata *imagedata.ImageData, shrink int, scale float64,
322322

323323
C.swap_and_clear(&img.VipsImage, tmp)
324324

325+
if imgdata.Type == imagetype.TIFF {
326+
if C.vips_fix_float_tiff(img.VipsImage, &tmp) == 0 {
327+
C.swap_and_clear(&img.VipsImage, tmp)
328+
} else {
329+
log.Warnf("Can't fix TIFF: %s", Error())
330+
}
331+
}
332+
325333
return nil
326334
}
327335

vips/vips.h

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ int vips_tiffload_go(void *buf, size_t len, VipsImage **out);
2525

2626
int vips_black_go(VipsImage **out, int width, int height, int bands);
2727

28+
int vips_fix_float_tiff(VipsImage *in, VipsImage **out);
29+
2830
int vips_get_orientation(VipsImage *image);
2931
void vips_strip_meta(VipsImage *image);
3032

0 commit comments

Comments
 (0)