8000 tiff: add thread-safe warning/error handlers, requires libtiff 4.5.0+ by lovell · Pull Request #4313 · libvips/libvips · GitHub
[go: up one dir, main page]

Skip to content

tiff: add thread-safe warning/error handlers, requires libtiff 4.5.0+ #4313

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
Dec 11, 2024
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 @@ -2,6 +2,7 @@

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

8.16.1

Expand Down
58 changes: 56 additions & 2 deletions libvips/foreign/tiff.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Some shared TIFF utilities.

Check notice on line 1 in libvips/foreign/tiff.c

View workflow job for this annotation

GitHub Actions / cpp-linter

Run clang-format on libvips/foreign/tiff.c

File libvips/foreign/tiff.c does not conform to Custom style guidelines. (lines 64, 199)
*
* 14/10/16
* - from vips2tiff.c
Expand Down Expand Up @@ -59,6 +59,11 @@

#include "tiff.h"

#ifdef HAVE_TIFF_OPEN_OPTIONS
void
vips__tiff_init(void) {}
#else

/* Handle TIFF errors here. Shared with vips2tiff.c. These can be called from
* more than one thread.
*/
Expand Down Expand Up @@ -89,6 +94,7 @@
TIFFSetErrorHandler(vips__thandler_error);
TIFFSetWarningHandler(vips__thandler_warning);
}
#endif /*HAVE_TIFF_OPEN_OPTIONS*/

/* TIFF input from a vips source.
*/
Expand Down Expand Up @@ -157,7 +163,8 @@
}

TIFF *
vips__tiff_openin_source(VipsSource *source)
vips__tiff_openin_source(VipsSource *source, VipsTiffErrorHandler error_fn,
VipsTiffErrorHandler warning_fn, void *user_data)
{
TIFF *tiff;

Expand All @@ -175,6 +182,28 @@
* chopped into c. 8kb chunks. This can reduce peak memory use for
* this type of file.
*/

#ifdef HAVE_TIFF_OPEN_OPTIONS
TIFFOpenOptions *opts = TIFFOpenOptionsAlloc();
TIFFOpenOptionsSetErrorHandlerExtR(opts, error_fn, user_data);
TIFFOpenOptionsSetWarningHandlerExtR(opts, warning_fn, user_data);
if (!(tiff = TIFFClientOpenExt("source input", "rmC",
(thandle_t) source,
openin_source_read,
openin_source_write,
openin_source_seek,
openin_source_close,
openin_source_length,
openin_source_map,
openin_source_unmap,
opts))) {
TIFFOpenOptionsFree(opts);
vips_error("vips__tiff_openin_source", "%s",
_("unable to open source for input"));
return NULL;
}
TIFFOpenOptionsFree(opts);
#else
if (!(tiff = TIFFClientOpen("source input", "rmC",
(thandle_t) source,
openin_source_read,
Expand All @@ -188,6 +217,7 @@
_("unable to open source for input"));
return NULL;
}
#endif /*HAVE_TIFF_OPEN_OPTIONS*/

/* Unreffed on close(), see above.
*/
Expand Down Expand Up @@ -264,7 +294,9 @@
}

TIFF *
vips__tiff_openout_target(VipsTarget *target, gboolean bigtiff)
vips__tiff_openout_target(VipsTarget *target, gboolean bigtiff,
VipsTiffErrorHandler error_fn, VipsTiffErrorHandler warning_fn,
void *user_data)
{
const char *mode = bigtiff ? "w8" : "w";

Expand All @@ -274,6 +306,27 @@
printf("vips__tiff_openout_buffer:\n");
#endif /*DEBUG*/

#ifdef HAVE_TIFF_OPEN_OPTIONS
TIFFOpenOptions *opts = TIFFOpenOptionsAlloc();
TIFFOpenOptionsSetErrorHandlerExtR(opts, error_fn, user_data);
TIFFOpenOptionsSetWarningHandlerExtR(opts, warning_fn, user_data);
if (!(tiff = TIFFClientOpenExt("target output", mode,
(thandle_t) target,
openout_target_read,
openout_target_write,
openout_target_seek,
openout_target_close,
openout_target_length,
openout_target_map,
openout_target_unmap,
opts))) {
TIFFOpenOptionsFree(opts);
vips_error("vips__tiff_openout_target", "%s",
_("unable to open target for output"));
return NULL;
}
TIFFOpenOptionsFree(opts);
#else
if (!(tiff = TIFFClientOpen("target output", mode,
(thandle_t) target,
openout_target_read,
Expand All @@ -287,6 +340,7 @@
_("unable to open target for output"));
return NULL;
}
#endif /*HAVE_TIFF_OPEN_OPTIONS*/

return tiff;
}
Expand Down
11 changes: 9 additions & 2 deletions libvips/foreign/tiff.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* common defs for tiff read/write

Check notice on line 1 in libvips/foreign/tiff.h

View workflow job for this annotation

GitHub Actions / cpp-linter

Run clang-format on libvips/foreign/tiff.h

File libvips/foreign/tiff.h does not conform to Custom style guidelines. (lines 40)
*/

/*
Expand Down Expand Up @@ -37,10 +37,17 @@
extern "C" {
#endif /*__cplusplus*/

TIFF *vips__tiff_openin_source(VipsSource *source);
typedef int (*VipsTiffErrorHandler)(TIFF *tiff, void* user_data,
const char *module, const char *fmt, va_list ap);

TIFF *vips__tiff_openin_source(VipsSource *source,
VipsTiffErrorHandler error_fn, VipsTiffErrorHandler warning_fn,
void *user_data);

TIFF *vips__tiff_openout(const char *path, gboolean bigtiff);
TIFF *vips__tiff_openout_target(VipsTarget *target, gboolean bigtiff);
TIFF *vips__tiff_openout_target(VipsTarget *target, gboolean bigtiff,
VipsTiffErrorHandler error_fn, VipsTiffErrorHandler warning_fn,
void *user_data);

#ifdef __cplusplus
}
Expand Down
43 changes: 41 additions & 2 deletions libvips/foreign/tiff2vips.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* TIFF parts: Copyright (c) 1988, 1990 by Sam Leffler.

Check notice on line 1 in libvips/foreign/tiff2vips.c

View workflow job for this annotation

GitHub Actions / cpp-linter

Run clang-format on libvips/foreign/tiff2vips.c

File libvips/foreign/tiff2vips.c does not conform to Custom style guidelines. (lines 622, 630, 634, 694, 3437)
* All rights reserved.
*
* This file is provided for unrestricted use provided that this
Expand Down Expand Up @@ -428,6 +428,10 @@
/* The Y we are reading at. Used to verify strip read is sequential.
*/
int y_pos;

/* Stop processing due to an error or warning.
*/
gboolean failed;
} Rtiff;

/* Convert IEEE 754-2008 16-bit float to 32-bit float
Expand Down Expand Up @@ -614,6 +618,28 @@
vips_source_minimise(rtiff->source);
}

static int
rtiff_handler_error(TIFF *tiff, void* user_data,
const char *module, const char *fmt, va_list ap)
{
vips_verror("tiff2vips", fmt, ap);
return 1;
}

static int
rtiff_handler_warning(TIFF *tiff, void* user_data,
const char *module, const char *fmt, va_list ap)
{
if (user_data) {
Rtiff *rtiff = (Rtiff*) user_data;
if (rtiff->fail_on >= VIPS_FAIL_ON_WARNING) {
rtiff->failed = TRUE;
}
}
g_logv("tiff2vips", G_LOG_LEVEL_WARNING, fmt, ap);
return 1;
}

static Rtiff *
rtiff_new(VipsSource *source, VipsImage *out,
int page, int n, gboolean autorotate, int subifd, VipsFailOn fail_on)
Expand Down Expand Up @@ -641,6 +667,7 @@
rtiff->plane_buf = NULL;
rtiff->contig_buf = NULL;
rtiff->y_pos = 0;
rtiff->failed = FALSE;

g_signal_connect(out, "close",
G_CALLBACK(rtiff_close_cb), rtiff);
Expand All @@ -664,7 +691,8 @@
return NULL;
}

if (!(rtiff->tiff = vips__tiff_openin_source(source)))
if (!(rtiff->tiff = vips__tiff_openin_source(source,
rtiff_handler_error, rtiff_handler_warning, rtiff)))
return NULL;

return rtiff;
Expand All @@ -690,6 +718,11 @@
return -1;
}

if (rtiff->failed) {
vips_foreign_load_invalidate(rtiff->out);
return -1;
}

return 0;
}

Expand Down Expand Up @@ -729,6 +762,11 @@

TIFFRGBAImageEnd(&img);

if (rtiff->failed) {
vips_foreign_load_invalidate(rtiff->out);
return -1;
}

return 0;
}

Expand Down Expand Up @@ -3396,7 +3434,8 @@

vips__tiff_init();

if (!(tif = vips__tiff_openin_source(source))) {
if (!(tif = vips__tiff_openin_source(source, rtiff_handler_error,
rtiff_handler_warning, NULL))) {
vips_error_clear();
return FALSE;
}
Expand Down
7 changes: 7 additions & 0 deletions libvips/foreign/tiffload.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ vips_foreign_load_tiff_class_init(VipsForeignLoadTiffClass *class)
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET(VipsForeignLoadTiff, subifd),
-1, 100000, -1);

}

static void
Expand Down Expand Up @@ -470,6 +471,7 @@ vips_foreign_load_tiff_buffer_init(VipsForeignLoadTiffBuffer *buffer)
* * @autorotate: %gboolean, use orientation tag to rotate the image
* during load
* * @subifd: %gint, select this subifd index
* * @fail_on: #VipsFailOn, types of read error to fail on
*
* Read a TIFF file into a VIPS image. It is a full baseline TIFF 6 reader,
* with extensions for tiled images, multipage images, XYZ and LAB colour
Expand Down Expand Up @@ -502,6 +504,9 @@ vips_foreign_load_tiff_buffer_init(VipsForeignLoadTiffBuffer *buffer)
* selected. This can be used to read lower resolution layers from
* bioformats-style image pyramids.
*
* Use @fail_on to set the type of error that will cause load to fail. By
* default, loaders are permissive, that is, #VIPS_FAIL_ON_NONE.
*
* Any ICC profile is read and attached to the VIPS image as
* #VIPS_META_ICC_NAME. Any XMP metadata is read and attached to the image
* as #VIPS_META_XMP_NAME. Any IPTC is attached as #VIPS_META_IPTC_NAME. The
Expand Down Expand Up @@ -540,6 +545,7 @@ vips_tiffload(const char *filename, VipsImage **out, ...)
* * @autorotate: %gboolean, use orientation tag to rotate the image
* during load
* * @subifd: %gint, select this subifd index
* * @fail_on: #VipsFailOn, types of read error to fail on
*
* Read a TIFF-formatted memory block into a VIPS image. Exactly as
* vips_tiffload(), but read from a memory source.
Expand Down Expand Up @@ -584,6 +590,7 @@ vips_tiffload_buffer(void *buf, size_t len, VipsImage **out, ...)
* * @autorotate: %gboolean, use orientation tag to rotate the image
* during load
* * @subifd: %gint, select this subifd index
* * @fail_on: #VipsFailOn, types of read error to fail on
*
* Exactly as vips_tiffload(), but read from a source.
*
Expand Down
22 changes: 20 additions & 2 deletions libvips/foreign/vips2tiff.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* TIFF PARTS:

Check notice on line 1 in libvips/foreign/vips2tiff.c

View workflow job for this annotation

GitHub Actions / cpp-linter

Run clang-format on libvips/foreign/vips2tiff.c

File libvips/foreign/vips2tiff.c does not conform to Custom style guidelines. (lines 445, 453, 2421)
* Copyright (c) 1988, 1990 by Sam Leffler.
* All rights reserved.
*
Expand Down Expand Up @@ -441,6 +441,22 @@
return 0;
}

static int
wtiff_handler_error(TIFF *tiff, void* user_data,
const char *module, const char *fmt, va_list ap)
{
vips_verror("vips2tiff", fmt, ap);
return 1;
}

static int
wtiff_handler_warning(TIFF *tiff, void* user_data,
const char *module, const char *fmt, va_list ap)
{
g_logv("vips2tiff", G_LOG_LEVEL_WARNING, fmt, ap);
return 1;
}

static void
wtiff_layer_init(Wtiff *wtiff, Layer **layer, Layer *above,
int width, int height)
Expand Down Expand Up @@ -1146,7 +1162,8 @@
vips__region_no_ownership(layer->copy);

layer->tif = vips__tiff_openout_target(layer->target,
wtiff->bigtiff);
wtiff->bigtiff, wtiff_handler_error,
wtiff_handler_warning, wtiff);
if (!layer->tif)
return -1;
}
Expand Down Expand Up @@ -2401,7 +2418,8 @@
if (!(source = vips_source_new_from_target(layer->target)))
return -1;

if (!(in = vips__tiff_openin_source(source))) {
if (!(in = vips__tiff_openin_source(source, wtiff_handler_error,
wtiff_handler_warning, NULL))) {
VIPS_UNREF(source);
return -1;
}
Expand Down
4 changes: 4 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,10 @@ if libtiff_dep.found()
if cc.get_define('COMPRESSION_WEBP', prefix: '#include <tiff.h>', dependencies: libtiff_dep) != ''
cfg_var.set('HAVE_TIFF_COMPRESSION_WEBP', '1')
endif
# TIFFOpenOptions added in libtiff 4.5.0
if cc.has_function('TIFFOpenOptionsAlloc', prefix: '#include <tiffio.h>', dependencies: libtiff_dep)
cfg_var.set('HAVE_TIFF_OPEN_OPTIONS', '1')
endif
endif

# 2.40.3 so we get the UNLIMITED open flag
Expand Down
9 changes: 9 additions & 0 deletions test/test-suite/test_foreign.py
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,15 @@ def tiff_subsampled_valid(im):
assert x1.xres == 100
assert x1.yres == 200

if sys.platform == "darwin":
with open(TIF2_FILE, 'rb') as f:
buf = bytearray(f.read())
buf = buf[:-4]
source = pyvips.Source.new_from_memory(buf)
im = pyvips.Image.tiffload_source(source, fail_on="warning")
with pytest.raises(Exception) as e_info:
im.avg() > 0

# OME support in 8.5
x = pyvips.Image.new_from_file(OME_FILE)
assert x.width == 439
Expand Down
Loading
0