Skip to content

Commit c966232

Browse files
mvanhornlgritz
andauthored
api: Add OIIO_NODISCARD to ImageInput create/open/read methods (#5111)
## Summary Adds `OIIO_NODISCARD` to `ImageInput::create()`, `open()`, `read_image()`, and `read_scanlines()` methods. Callers ignoring return values from these methods now get a compiler warning. ## Why this matters As noted in #4781, it's easy to accidentally ignore the bool return from `read_image()` or `open()`. These methods return false on failure, and silently proceeding with bad data leads to hard-to-debug issues. `create()` returns a unique_ptr that should never be discarded. ## Changes - `src/include/OpenImageIO/imageio.h`: Added `OIIO_NODISCARD` to 14 method declarations in the `ImageInput` class, covering `create()` (2 overloads), `open()` (4 overloads), `read_image()` (4 overloads), and `read_scanlines()` (4 overloads). Scoped to `ImageInput` only per @lgritz's guidance to start with one class and iterate. ## Testing - Header compiles (syntax validated) - No behavioral changes - annotation only Partial fix for #4781 Assisted-by: Claude Code / Claude Opus 4.6 (1M context) Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Signed-off-by: Larry Gritz <lg@larrygritz.com> Co-authored-by: Larry Gritz <lg@larrygritz.com>
1 parent f518355 commit c966232

File tree

6 files changed

+121
-41
lines changed

6 files changed

+121
-41
lines changed

src/include/OpenImageIO/imageio.h

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,10 +1180,11 @@ class OIIO_API ImageInput {
11801180
///
11811181
/// @returns
11821182
/// `true` if the file was found and opened successfully.
1183-
virtual bool open (const std::string& name, ImageSpec &newspec) = 0;
1183+
OIIO_NODISCARD_ERROR virtual bool open (const std::string& name,
1184+
ImageSpec &newspec) = 0;
11841185

11851186
/// Open the ImageInput using a UTF-16 encoded wstring filename.
1186-
bool open (const std::wstring& name, ImageSpec &newspec) {
1187+
OIIO_NODISCARD_ERROR bool open (const std::wstring& name, ImageSpec &newspec) {
11871188
return open(Strutil::utf16_to_utf8(name), newspec);
11881189
}
11891190

@@ -1207,13 +1208,14 @@ class OIIO_API ImageInput {
12071208
///
12081209
/// @returns
12091210
/// `true` if the file was found and opened successfully.
1210-
virtual bool open (const std::string& name, ImageSpec &newspec,
1211-
const ImageSpec& config OIIO_MAYBE_UNUSED) {
1211+
OIIO_NODISCARD_ERROR virtual bool open (const std::string& name,
1212+
ImageSpec &newspec,
1213+
const ImageSpec& config OIIO_MAYBE_UNUSED) {
12121214
return open(name,newspec);
12131215
}
12141216
/// Open the ImageInput using a UTF-16 encoded wstring filename.
1215-
bool open (const std::wstring& name, ImageSpec &newspec,
1216-
const ImageSpec& config OIIO_MAYBE_UNUSED) {
1217+
OIIO_NODISCARD_ERROR bool open (const std::wstring& name, ImageSpec &newspec,
1218+
const ImageSpec& config OIIO_MAYBE_UNUSED) {
12171219
return open(name,newspec);
12181220
}
12191221

@@ -1411,15 +1413,17 @@ class OIIO_API ImageInput {
14111413
/// y, and z).
14121414
/// @returns `true` upon success, or `false` upon failure.
14131415
///
1414-
virtual bool read_image(int subimage, int miplevel, int chbegin, int chend,
1415-
TypeDesc format, const image_span<std::byte>& data);
1416+
OIIO_NODISCARD_ERROR virtual bool
1417+
read_image(int subimage, int miplevel, int chbegin, int chend,
1418+
TypeDesc format, const image_span<std::byte>& data);
14161419

14171420
/// A version of `read_image()` taking an `image_span<T>`, where the type
14181421
/// of the underlying data is `T`. This is a convenience wrapper around
14191422
/// the `read_image()` that takes an `image_span<std::byte>`.
14201423
template<typename T>
1421-
bool read_image(int subimage, int miplevel, int chbegin, int chend,
1422-
const image_span<T>& data)
1424+
OIIO_NODISCARD_ERROR bool read_image(int subimage, int miplevel,
1425+
int chbegin, int chend,
1426+
const image_span<T>& data)
14231427
{
14241428
static_assert(!std::is_const_v<T>,
14251429
"read_image() does not accept image_span<const T>");
@@ -1432,8 +1436,8 @@ class OIIO_API ImageInput {
14321436
/// contiguous strides in all dimensions. This is a convenience wrapper
14331437
/// around the `read_image()` that takes an `image_span<T>`.
14341438
template<typename T>
1435-
bool read_image(int subimage, int miplevel, int chbegin, int chend,
1436-
span<T> data)
1439+
OIIO_NODISCARD_ERROR bool read_image(int subimage, int miplevel,
1440+
int chbegin, int chend, span<T> data)
14371441
{
14381442
static_assert(!std::is_const_v<T>,
14391443
"read_image() does not accept span<const T>");
@@ -1480,17 +1484,18 @@ class OIIO_API ImageInput {
14801484
/// Added in OIIO 3.1, this is the "safe" preferred alternative to
14811485
/// the version of read_scanlines that takes raw pointers.
14821486
///
1483-
virtual bool read_scanlines(int subimage, int miplevel, int ybegin,
1484-
int yend, int chbegin, int chend,
1485-
TypeDesc format,
1486-
const image_span<std::byte>& data);
1487+
OIIO_NODISCARD_ERROR virtual bool
1488+
read_scanlines(int subimage, int miplevel, int ybegin, int yend,
1489+
int chbegin, int chend, TypeDesc format,
1490+
const image_span<std::byte>& data);
14871491

14881492
/// A version of `read_scanlines()` taking an `image_span<T>`, where the
14891493
/// type of the underlying data is `T`. This is a convenience wrapper
14901494
/// around the `read_scanlines()` that takes an `image_span<std::byte>`.
14911495
template<typename T>
1492-
bool read_scanlines(int subimage, int miplevel, int ybegin, int yend,
1493-
int chbegin, int chend, const image_span<T>& data)
1496+
OIIO_NODISCARD_ERROR bool
1497+
read_scanlines(int subimage, int miplevel, int ybegin, int yend,
1498+
int chbegin, int chend, const image_span<T>& data)
14941499
{
14951500
static_assert(!std::is_const_v<T>,
14961501
"read_scanlines() does not accept span<const T>");
@@ -1504,8 +1509,9 @@ class OIIO_API ImageInput {
15041509
/// contiguous strides in all dimensions. This is a convenience wrapper
15051510
/// around the `read_scanlines()` that takes an `image_span<T>`.
15061511
template<typename T>
1507-
bool read_scanlines(int subimage, int miplevel, int ybegin, int yend,
1508-
int chbegin, int chend, span<T> data)
1512+
OIIO_NODISCARD_ERROR bool read_scanlines(int subimage, int miplevel,
1513+
int ybegin, int yend, int chbegin,
1514+
int chend, span<T> data)
15091515
{
15101516
static_assert(!std::is_const_v<T>,
15111517
"read_scanlines() does not accept span<const T>");
@@ -1773,12 +1779,12 @@ class OIIO_API ImageInput {
17731779
///
17741780
/// @note This call was changed for OpenImageIO 2.0 to include the
17751781
/// explicit subimage and miplevel parameters.
1776-
virtual bool read_scanlines (int subimage, int miplevel,
1777-
int ybegin, int yend, int z,
1778-
int chbegin, int chend,
1779-
TypeDesc format, void *data,
1780-
stride_t xstride=AutoStride,
1781-
stride_t ystride=AutoStride);
1782+
OIIO_NODISCARD_ERROR virtual bool read_scanlines (int subimage, int miplevel,
1783+
int ybegin, int yend, int z,
1784+
int chbegin, int chend,
1785+
TypeDesc format, void *data,
1786+
stride_t xstride=AutoStride,
1787+
stride_t ystride=AutoStride);
17821788

17831789
/// Read the tile whose upper-left origin is (x,y,z) into `data[]`,
17841790
/// converting if necessary from the native data format of the file into
@@ -1904,14 +1910,14 @@ class OIIO_API ImageInput {
19041910
/// @param progress_callback/progress_callback_data
19051911
/// Optional progress callback.
19061912
/// @returns `true` upon success, or `false` upon failure.
1907-
virtual bool read_image (int subimage, int miplevel,
1908-
int chbegin, int chend,
1909-
TypeDesc format, void *data,
1910-
stride_t xstride=AutoStride,
1911-
stride_t ystride=AutoStride,
1912-
stride_t zstride=AutoStride,
1913-
ProgressCallback progress_callback=NULL,
1914-
void *progress_callback_data=NULL);
1913+
OIIO_NODISCARD_ERROR virtual bool read_image (int subimage, int miplevel,
1914+
int chbegin, int chend,
1915+
TypeDesc format, void *data,
1916+
stride_t xstride=AutoStride,
1917+
stride_t ystride=AutoStride,
1918+
stride_t zstride=AutoStride,
1919+
ProgressCallback progress_callback=NULL,
1920+
void *progress_callback_data=NULL);
19151921

19161922
/// @}
19171923

src/include/OpenImageIO/platform.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,28 @@
466466
# define OIIO_NODISCARD
467467
#endif
468468

469+
// OIIO_NODISCARD_ERROR is for functions returning error status (bool) where
470+
// ignoring the return is a bad practice but not always catastrophic.
471+
//
472+
// OIIO_NODISCARD_ERROR_ENABLE, if nonzero, enables OIIO_NODISCARD_ERROR to
473+
// take effect. The default is to disable it for OIIO < 3.3, and enable it for
474+
// OIIO >= 3.3. But `-DOIIO_NODISCARD_ERROR_ENABLE=...` can be used to
475+
// override the default, for example to flag discarded errors in older
476+
// versions of OIIO, or to disable the warnings in future versions of OIIO.
477+
#ifndef OIIO_NODISCARD_ERROR_ENABLE
478+
# if OIIO_VERSION_LESS(3, 3, 0)
479+
# define OIIO_NODISCARD_ERROR_ENABLE 0 /* disable for now */
480+
# else
481+
# define OIIO_NODISCARD_ERROR_ENABLE 1 /* enable for OIIO >= 3.3 */
482+
# endif
483+
#endif
484+
485+
#if OIIO_NODISCARD_ERROR_ENABLE
486+
# define OIIO_NODISCARD_ERROR OIIO_NODISCARD
487+
#else
488+
# define OIIO_NODISCARD_ERROR /* nothing */
489+
#endif
490+
469491

470492
// OIIO_NO_SANITIZE_ADDRESS can be used to mark a function that you don't
471493
// want address sanitizer to catch. Only use this if you know there are

src/jpeg.imageio/jpegoutput.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -573,24 +573,25 @@ JpgOutput::copy_image(ImageInput* in)
573573
ImageSpec in_spec;
574574
ImageSpec config_spec;
575575
config_spec.attribute("_jpeg:raw", 1);
576-
in->open(in_name, in_spec, config_spec);
576+
if (!in->open(in_name, in_spec, config_spec)) {
577+
errorfmt("{}", in->geterror());
578+
return false;
579+
}
577580

578581
// Re-open the output
579582
std::string out_name = m_filename;
580583
ImageSpec orig_out_spec = spec();
581584
close();
582585
m_copy_coeffs = (jvirt_barray_ptr*)jpg_in->coeffs();
583586
m_copy_decompressor = &jpg_in->m_cinfo;
584-
open(out_name, orig_out_spec);
585-
587+
bool ok = open(out_name, orig_out_spec);
586588
// Strangeness -- the write_coefficients somehow sets things up
587589
// so that certain writes only happen in close(), which MUST
588590
// happen while the input file is still open. So we go ahead
589591
// and close() now, so that the caller of copy_image() doesn't
590592
// close the input file first and then wonder why they crashed.
591593
close();
592-
593-
return true;
594+
return ok;
594595
}
595596

596597
return ImageOutput::copy_image(in);

src/libOpenImageIO/imageioplugin.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ pvt::catalog_all_plugins(std::string searchpath)
504504
// ImageInput::create will take a lock of imageio_mutex
505505
auto inp = ImageInput::create(f.first);
506506
lock.lock();
507-
if (inp->supports("procedural"))
507+
if (inp && inp->supports("procedural"))
508508
procedural_plugins.insert(f.first);
509509
}
510510
}

src/python/py_imagebuf.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ ImageBuf_repr_png(const ImageBuf& self)
270270

271271
std::unique_ptr<ImageOutput> out = ImageOutput::create("temp.png",
272272
&file_vec);
273-
out->open("temp.png", altered_spec);
273+
if (!out || !out->open("temp.png", altered_spec))
274+
return py::bytes();
274275
self.write(out.get());
275276
out->close();
276277

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
Reading src/corrupt-exif.jpg
2+
src/corrupt-exif.jpg : 256 x 256, 3 channel, uint8 jpeg
3+
SHA-1: 3EA7BAEB0871E9B9BAADF286521423199ACA2401
4+
channel list: R, G, B
5+
Orientation: 1 (normal)
6+
ResolutionUnit: "in"
7+
XResolution: 300
8+
YResolution: 300
9+
jpeg:subsampling: "4:2:0"
10+
oiio:ColorSpace: "srgb_rec709_scene"
11+
Reading src/corrupt-exif-1626.jpg
12+
src/corrupt-exif-1626.jpg : 256 x 256, 3 channel, uint8 jpeg
13+
SHA-1: 6CBB7DB602A67DB300AB28842F20F6DCA02B82B1
14+
channel list: R, G, B
15+
Orientation: 1 (normal)
16+
ResolutionUnit: "in"
17+
XResolution: 300
18+
YResolution: 300
19+
jpeg:subsampling: "4:2:0"
20+
oiio:ColorSpace: "srgb_rec709_scene"
21+
corrupt-icc-4551.jpg
22+
iconvert ERROR copying "src/corrupt-icc-4551.jpg" to "out-4551.jpg" :
23+
JPEG error: Corrupt JPEG data: bad Huffman code ("src/corrupt-icc-4551.jpg")
24+
jpeg image resolution must be at least 1x1, but the file specified 0x0x1, 3 chans. Possible corrupt input?
25+
Reading src/corrupt-icc-4552.jpg
26+
src/corrupt-icc-4552.jpg : 1500 x 1000, 3 channel, uint8 jpeg
27+
SHA-1: 58BC613FDC7513B4F3854D8D2910040B60FF7A7F
28+
channel list: R, G, B
29+
ICCProfile: 0, 0, 12, 72, 76, 105, 110, 111, 2, 16, 0, 0, 109, 110, 116, 114, ... [3144 x uint8]
30+
ResolutionUnit: "in"
31+
XResolution: 72
32+
YResolution: 72
33+
ICCProfile:attributes: "Transparency , Glossy, Positive, Color"
34+
ICCProfile:cmm_type: 1281977967
35+
ICCProfile:color_space: "RGB"
36+
ICCProfile:creation_date: "1998:02:09 06:49:00"
37+
ICCProfile:creator_signature: "48502020"
38+
ICCProfile:device_class: "Display device profile"
39+
ICCProfile:flags: "Not Embedded, Independent"
40+
ICCProfile:manufacturer: "49454320"
41+
ICCProfile:model: "73524742"
42+
ICCProfile:platform_signature: "Microsoft Corporation"
43+
ICCProfile:profile_connection_space: "XYZ"
44+
ICCProfile:profile_size: 3144
45+
ICCProfile:profile_version: "2.1.0"
46+
ICCProfile:rendering_intent: "Unknown"
47+
jpeg:subsampling: "4:2:0"
48+
oiio:ColorSpace: "srgb_rec709_scene"
49+
iinfo ERROR: "src/corrupt-iptc-8011.jpg" : JPEG error: Corrupt JPEG data: 256 extraneous bytes before marker 0xdb ("src/corrupt-iptc-8011.jpg")
50+
Corrupted IPTC data

0 commit comments

Comments
 (0)