8000 tiff: add thread-safe warning/error handlers, requires libtiff 4.5.0+ · libvips/libvips@c87515f · GitHub
[go: up one dir, main page]

Skip to content

Commit c87515f

Browse files
committed
tiff: add thread-safe warning/error handlers, requires libtiff 4.5.0+
1 parent 9b5c724 commit c87515f

File tree

8 files changed

+147
-8
lines changed

8 files changed

+147
-8
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- add `keep_duplicate_frames` option to GIF save [dloebl]
44
- add Magic Kernel support [akimon658]
5+
- tiff: add threadsafe warning/error handlers (requires libtiff 4.5.0+) [lovell]
56

67
8.16.1
78

libvips/foreign/tiff.c

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@
5959

6060
#include "tiff.h"
6161

62+
#ifdef HAVE_TIFF_OPEN_OPTIONS
63+
void
64+
vips__tiff_init(void) {}
65+
#else
66+
6267
/* Handle TIFF errors here. Shared with vips2tiff.c. These can be called from
6368
* more than one thread.
6469
*/
@@ -89,6 +94,7 @@ vips__tiff_init(void)
8994
TIFFSetErrorHandler(vips__thandler_error);
9095
TIFFSetWarningHandler(vips__thandler_warning);
9196
}
97+
#endif /*HAVE_TIFF_OPEN_OPTIONS*/
9298

9399
/* TIFF input from a vips source.
94100
*/
@@ -157,7 +163,8 @@ openin_source_unmap(thandle_t st, tdata_t start, toff_t len)
157163
}
158164

159165
TIFF *
160-
vips__tiff_openin_source(VipsSource *source)
166+
vips__tiff_openin_source(VipsSource *source, VipsTiffErrorHandler error_fn,
167+
VipsTiffErrorHandler warning_fn, void *user_data)
161168
{
162169
TIFF *tiff;
163170

@@ -175,6 +182,28 @@ vips__tiff_openin_source(VipsSource *source)
175182
* chopped into c. 8kb chunks. This can reduce peak memory use for
176183
* this type of file.
177184
*/
185+
186+
#ifdef HAVE_TIFF_OPEN_OPTIONS
187+
TIFFOpenOptions *opts = TIFFOpenOptionsAlloc();
188+
TIFFOpenOptionsSetErrorHandlerExtR(opts, error_fn, user_data);
189+
TIFFOpenOptionsSetWarningHandlerExtR(opts, warning_fn, user_data);
190+
if (!(tiff = TIFFClientOpenExt("source input", "rmC",
191+
(thandle_t) source,
192+
openin_source_read,
193+
openin_source_write,
194+
openin_source_seek,
195+
openin_source_close,
196+
openin_source_length,
197+
openin_source_map,
198+
openin_source_unmap,
199+
opts))) {
200+
TIFFOpenOptionsFree(opts);
201+
vips_error("vips__tiff_openin_source", "%s",
202+
_("unable to open source for input"));
203+
return NULL;
204+
}
205+
TIFFOpenOptionsFree(opts);
206+
#else
178207
if (!(tiff = TIFFClientOpen("source input", "rmC",
179208
(thandle_t) source,
180209
openin_source_read,
@@ -188,6 +217,7 @@ vips__tiff_openin_source(VipsSource *source)
188217
_("unable to open source for input"));
189218
return NULL;
190219
}
220+
#endif /*HAVE_TIFF_OPEN_OPTIONS*/
191221

192222
/* Unreffed on close(), see above.
193223
*/
@@ -264,7 +294,9 @@ openout_target_unmap(thandle_t st, tdata_t start, toff_t len)
264294
}
265295

266296
TIFF *
267-
vips__tiff_openout_target(VipsTarget *target, gboolean bigtiff)
297+
vips__tiff_openout_target(VipsTarget *target, gboolean bigtiff,
298+
VipsTiffErrorHandler error_fn, VipsTiffErrorHandler warning_fn,
299+
void *user_data)
268300
{
269301
const char *mode = bigtiff ? "w8" : "w";
270302

@@ -274,6 +306,27 @@ vips__tiff_openout_target(VipsTarget *target, gboolean bigtiff)
274306
printf("vips__tiff_openout_buffer:\n");
275307
#endif /*DEBUG*/
276308

309+
#ifdef HAVE_TIFF_OPEN_OPTIONS
310+
TIFFOpenOptions *opts = TIFFOpenOptionsAlloc();
311+
TIFFOpenOptionsSetErrorHandlerExtR(opts, error_fn, user_data);
312+
TIFFOpenOptionsSetWarningHandlerExtR(opts, warning_fn, user_data);
313+
if (!(tiff = TIFFClientOpenExt("target output", mode,
314+
(thandle_t) target,
315+
openout_target_read,
316+
openout_target_write,
317+
openout_target_seek,
318+
openout_target_close,
319+
openout_target_length,
320+
openout_target_map,
321+
openout_target_unmap,
322+
opts))) {
323+
TIFFOpenOptionsFree(opts);
324+
vips_error("vips__tiff_openout_target", "%s",
325+
_("unable to open target for output"));
326+
return NULL;
327+
}
328+
TIFFOpenOptionsFree(opts);
329+
#else
277330
if (!(tiff = TIFFClientOpen("target output", mode,
278331
(thandle_t) target,
279332
openout_target_read,
@@ -287,6 +340,7 @@ vips__tiff_openout_target(VipsTarget *target, gboolean bigtiff)
287340
_("unable to open target for output"));
288341
return NULL;
289342
}
343+
#endif /*HAVE_TIFF_OPEN_OPTIONS*/
290344

291345
return tiff;
292346
}

libvips/foreign/tiff.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,17 @@
3737
extern "C" {
3838
#endif /*__cplusplus*/
3939

40-
TIFF *vips__tiff_openin_source(VipsSource *source);
40+
typedef int (*VipsTiffErrorHandler)(TIFF *tiff, void* user_data,
41+
const char *module, const char *fmt, va_list ap);
42+
43+
TIFF *vips__tiff_openin_source(VipsSource *source,
44+
VipsTiffErrorHandler error_fn, VipsTiffErrorHandler warning_fn,
45+
void *user_data);
4146

4247
TIFF *vips__tiff_openout(const char *path, gboolean bigtiff);
43-
TIFF *vips__tiff_openout_target(VipsTarget *target, gboolean bigtiff);
48+
TIFF *vips__tiff_openout_target(VipsTarget *target, gboolean bigtiff,
49+
VipsTiffErrorHandler error_fn, VipsTiffErrorHandler warning_fn,
50+
void *user_data);
4451

4552
#ifdef __cplusplus
4653
}

libvips/foreign/tiff2vips.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,10 @@ typedef struct _Rtiff {
428428
/* The Y we are reading at. Used to verify strip read is sequential.
429429
*/
430430
int y_pos;
431+
432+
/* Stop processing due to an error or warning.
433+
*/
434+
gboolean failed;
431435
} Rtiff;
432436

433437
/* Convert IEEE 754-2008 16-bit float to 32-bit float
@@ -614,6 +618,28 @@ rtiff_minimise_cb(VipsImage *image, Rtiff *rtiff)
614618
vips_source_minimise(rtiff->source);
615619
}
616620

621+
static int
622+
rtiff_handler_error(TIFF *tiff, void* user_data,
623+
const char *module, const char *fmt, va_list ap)
624+
{
625+
vips_verror("tiff2vips", fmt, ap);
626+
return 1;
627+
}
628+
629+
static int
630+
rtiff_handler_warning(TIFF *tiff, void* user_data,
631+
const char *module, const char *fmt, va_list ap)
632+
{
633+
if (user_data) {
634+
Rtiff *rtiff = (Rtiff*) user_data;
635+
if (rtiff->fail_on >= VIPS_FAIL_ON_WARNING) {
636+
rtiff->failed = TRUE;
637+
}
638+
}
639+
g_logv("tiff2vips", G_LOG_LEVEL_WARNING, fmt, ap);
640+
return 1;
641+
}
642+
617643
static Rtiff *
618644
rtiff_new(VipsSource *source, VipsImage *out,
619645
int page, int n, gboolean autorotate, int subifd, VipsFailOn fail_on)
@@ -641,6 +667,7 @@ rtiff_new(VipsSource *source, VipsImage *out,
641667
rtiff->plane_buf = NULL;
642668
rtiff->contig_buf = NULL;
643669
rtiff->y_pos = 0;
670+
rtiff->failed = FALSE;
644671

645672
g_signal_connect(out, "close",
646673
G_CALLBACK(rtiff_close_cb), rtiff);
@@ -664,7 +691,8 @@ rtiff_new(VipsSource *source, VipsImage *out,
664691
return NULL;
665692
}
666693

667-
if (!(rtiff->tiff = vips__tiff_openin_source(source)))
694+
if (!(rtiff->tiff = vips__tiff_openin_source(source,
695+
rtiff_handler_error, rtiff_handler_warning, rtiff)))
668696
return NULL;
669697

670698
return rtiff;
@@ -690,6 +718,11 @@ rtiff_strip_read(Rtiff *rtiff, int strip, tdata_t buf)
690718
return -1;
691719
}
692720

721+
if (rtiff->failed) {
722+
vips_foreign_load_invalidate(rtiff->out);
723+
return -1;
724+
}
725+
693726
return 0;
694727
}
695728

@@ -729,6 +762,11 @@ rtiff_rgba_strip_read(Rtiff *rtiff, int strip, tdata_t buf)
729762

730763
TIFFRGBAImageEnd(&img);
731764

765+
if (rtiff->failed) {
766+
vips_foreign_load_invalidate(rtiff->out);
767+
return -1;
768+
}
769+
732770
return 0;
733771
}
734772

@@ -3396,7 +3434,8 @@ vips__testtiff_source(VipsSource *source, TiffPropertyFn fn)
33963434

33973435
vips__tiff_init();
33983436

3399-
if (!(tif = vips__tiff_openin_source(source))) {
3437+
if (!(tif = vips__tiff_openin_source(source, rtiff_handler_error,
3438+
rtiff_handler_warning, NULL))) {
34003439
vips_error_clear();
34013440
return FALSE;
34023441
}

libvips/foreign/tiffload.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ vips_foreign_load_tiff_class_init(VipsForeignLoadTiffClass *class)
215215
VIPS_ARGUMENT_OPTIONAL_INPUT,
216216
G_STRUCT_OFFSET(VipsForeignLoadTiff, subifd),
217217
-1, 100000, -1);
218+
218219
}
219220

220221
static void
@@ -470,6 +471,7 @@ vips_foreign_load_tiff_buffer_init(VipsForeignLoadTiffBuffer *buffer)
470471
* * @autorotate: %gboolean, use orientation tag to rotate the image
471472
* during load
472473
* * @subifd: %gint, select this subifd index
474+
* * @fail_on: #VipsFailOn, types of read error to fail on
473475
*
474476
* Read a TIFF file into a VIPS image. It is a full baseline TIFF 6 reader,
475477
* with extensions for tiled images, multipage images, XYZ and LAB colour
@@ -502,6 +504,9 @@ vips_foreign_load_tiff_buffer_init(VipsForeignLoadTiffBuffer *buffer)
502504
* selected. This can be used to read lower resolution layers from
503505
* bioformats-style image pyramids.
504506
*
507+
* Use @fail_on to set the type of error that will cause load to fail. By
508+
* default, loaders are permissive, that is, #VIPS_FAIL_ON_NONE.
509+
*
505510
* Any ICC profile is read and attached to the VIPS image as
506511
* #VIPS_META_ICC_NAME. Any XMP metadata is read and attached to the image
507512
* as #VIPS_META_XMP_NAME. Any IPTC is attached as #VIPS_META_IPTC_NAME. The
@@ -540,6 +545,7 @@ vips_tiffload(const char *filename, VipsImage **out, ...)
540545
* * @autorotate: %gboolean, use orientation tag to rotate the image
541546
* during load
542547
* * @subifd: %gint, select this subifd index
548+
* * @fail_on: #VipsFailOn, types of read error to fail on
543549
*
544550
* Read a TIFF-formatted memory block into a VIPS image. Exactly as
545551
* vips_tiffload(), but read from a memory source.
@@ -584,6 +590,7 @@ vips_tiffload_buffer(void *buf, size_t len, VipsImage **out, ...)
584590
* * @autorotate: %gboolean, use orientation tag to rotate the image
585591
* during load
586592
* * @subifd: %gint, select this subifd index
593+
* * @fail_on: #VipsFailOn, types of read error to fail on
587594
*
588595
* Exactly as vips_tiffload(), but read from a source.
589596
*

libvips/foreign/vips2tiff.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,22 @@ embed_profile_meta(TIFF *tif, VipsImage *im)
441441
return 0;
442442
}
443443

444+
static int
445+
wtiff_handler_error(TIFF *tiff, void* user_data,
446+
const char *module, const char *fmt, va_list ap)
447+
{
448+
vips_verror("vips2tiff", fmt, ap);
449+
return 1;
450+
}
451+
452+
static int
453+
wtiff_handler_warning(TIFF *tiff, void* user_data,
454+
const char *module, const char *fmt, va_list ap)
455+
{
456+
g_logv("vips2tiff", G_LOG_LEVEL_WARNING, fmt, ap);
457+
return 1;
458+
}
459+
444460
static void
445461
wtiff_layer_init(Wtiff *wtiff, Layer **layer, Layer *above,
446462
int width, int height)
@@ -1146,7 +1162,8 @@ wtiff_allocate_layers(Wtiff *wtiff)
11461162
vips__region_no_ownership(layer->copy);
11471163

11481164
layer->tif = vips__tiff_openout_target(layer->target,
1149-
wtiff->bigtiff);
1165+
wtiff->bigtiff, wtiff_handler_error,
1166+
wtiff_handler_warning, wtiff);
11501167
if (!layer->tif)
11511168
return -1;
11521169
}
@@ -2401,7 +2418,8 @@ wtiff_gather(Wtiff *wtiff)
24012418
if (!(source = vips_source_new_from_target(layer->target)))
24022419
return -1;
24032420

2404-
if (!(in = vips__tiff_openin_source(source))) {
2421+
if (!(in = vips__tiff_openin_source(source, wtiff_handler_error,
2422+
wtiff_handler_warning, NULL))) {
24052423
VIPS_UNREF(source);
24062424
return -1;
24072425
}

meson.build

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,10 @@ if libtiff_dep.found()
414414
if cc.get_define('COMPRESSION_WEBP', prefix: '#include <tiff.h>', dependencies: libtiff_dep) != ''
415415
cfg_var.set('HAVE_TIFF_COMPRESSION_WEBP', '1')
416416
endif
417+
# TIFFOpenOptions added in libtiff 4.5.0
418+
if cc.has_function('TIFFOpenOptionsAlloc', prefix: '#include <tiffio.h>', dependencies: libtiff_dep)
419+
cfg_var.set('HAVE_TIFF_OPEN_OPTIONS', '1')
420+
endif
417421
endif
418422

419423
# 2.40.3 so we get the UNLIMITED open flag

test/test-suite/test_foreign.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,15 @@ def tiff_subsampled_valid(im):
662662
assert x1.xres == 100
663663
assert x1.yres == 200
664664

665+
if sys.platform == "darwin":
666+
with open(TIF2_FILE, 'rb') as f:
667+
buf = bytearray(f.read())
668+
buf = buf[:-4]
669+
source = pyvips.Source.new_from_memory(buf)
670+
im = pyvips.Image.tiffload_source(source, fail_on="warning")
671+
with pytest.raises(Exception) as e_info:
672+
im.avg() > 0
673+
665674
# OME support in 8.5
666675
x = pyvips.Image.new_from_file(OME_FILE)
667676
assert x.width == 439

0 commit comments

Comments
 (0)
0