Skip to content

Commit a1d8dbc

Browse files
authored
api(ImageInput)!: Add an ImageInput::valid_file(IOProxy) overload (#3826)
ABI-BREAKING CHANGE! Every format that has an existing `valid_file(std::string)` implementation, and which supports an ioproxy, now also has a `valid_file(IOProxy)` implementation for completeness. The existing std::string overloads have been implemented in terms of the IOProxy overload where possible to cut down on the boilerplate. This is done at the base of the hierarchy in ImageInput. The remaining formats that support ioproxy, but do not currently have a `valid_file` method, will be submitted in a follow-up PR as the additional logic required for those will require closer review.
1 parent 23819a8 commit a1d8dbc

File tree

10 files changed

+106
-108
lines changed

10 files changed

+106
-108
lines changed

src/bmp.imageio/bmpinput.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class BmpInput final : public ImageInput {
2323
{
2424
return feature == "ioproxy";
2525
}
26-
bool valid_file(const std::string& filename) const override;
26+
bool valid_file(Filesystem::IOProxy* ioproxy) const override;
2727
bool open(const std::string& name, ImageSpec& newspec) override;
2828
bool open(const std::string& name, ImageSpec& newspec,
2929
const ImageSpec& config) override;
@@ -94,11 +94,13 @@ OIIO_PLUGIN_EXPORTS_END
9494

9595

9696
bool
97-
BmpInput::valid_file(const std::string& filename) const
97+
BmpInput::valid_file(Filesystem::IOProxy* ioproxy) const
9898
{
99+
if (!ioproxy || ioproxy->mode() != Filesystem::IOProxy::Mode::Read)
100+
return false;
101+
99102
bmp_pvt::BmpFileHeader header;
100-
Filesystem::IOFile file(filename, Filesystem::IOProxy::Read);
101-
return file.opened() && header.read_header(&file) && header.isBmp();
103+
return header.read_header(ioproxy) && header.isBmp();
102104
}
103105

104106

src/dpx.imageio/dpxinput.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class DPXInput final : public ImageInput {
2929
{
3030
return (feature == "ioproxy");
3131
}
32-
bool valid_file(const std::string& filename) const override;
32+
bool valid_file(Filesystem::IOProxy* ioproxy) const override;
3333
bool open(const std::string& name, ImageSpec& newspec) override;
3434
bool open(const std::string& name, ImageSpec& newspec,
3535
const ImageSpec& config) override;
@@ -107,21 +107,18 @@ OIIO_PLUGIN_EXPORTS_END
107107

108108

109109
bool
110-
DPXInput::valid_file(const std::string& filename) const
110+
DPXInput::valid_file(Filesystem::IOProxy* ioproxy) const
111111
{
112-
Filesystem::IOProxy* io
113-
= new Filesystem::IOFile(filename, Filesystem::IOProxy::Mode::Read);
114-
std::unique_ptr<Filesystem::IOProxy> io_uptr(io);
115-
if (!io || io->mode() != Filesystem::IOProxy::Mode::Read)
112+
if (!ioproxy || ioproxy->mode() != Filesystem::IOProxy::Mode::Read)
116113
return false;
117114

118-
std::unique_ptr<InStream> stream_uptr(new InStream(io));
115+
std::unique_ptr<InStream> stream_uptr(new InStream(ioproxy));
119116
if (!stream_uptr)
120117
return false;
121118

122119
dpx::Reader dpx;
123120
dpx.SetInStream(stream_uptr.get());
124-
return dpx.ReadHeader(); // IOFile is automatically closed when destructed
121+
return dpx.ReadHeader();
125122
}
126123

127124

src/include/OpenImageIO/imageio.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1025,11 +1025,24 @@ class OIIO_API ImageInput {
10251025
/// `true` upon success, or `false` upon failure.
10261026
virtual bool valid_file (const std::string& filename) const;
10271027

1028-
/// Check valid vile using a UTF-16 encoded wstring filename.
1028+
/// Check valid file using a UTF-16 encoded wstring filename.
10291029
bool valid_file (const std::wstring& filename) const {
10301030
return valid_file(Strutil::utf16_to_utf8(filename));
10311031
}
10321032

1033+
/// Return true if the `ioproxy` represents a file of the type for this
1034+
/// ImageInput. The implementation will try to determine this as
1035+
/// efficiently as possible, in most cases much less expensively than
1036+
/// doing a full `open()`. Note that there can be false positives: a
1037+
/// file can appear to be of the right type (i.e., `valid_file()`
1038+
/// returning `true`) but still fail a subsequent call to `open()`, such
1039+
/// as if the contents of the file are truncated, nonsensical, or
1040+
/// otherwise corrupted.
1041+
///
1042+
/// @returns
1043+
/// `true` upon success, or `false` upon failure.
1044+
virtual bool valid_file (Filesystem::IOProxy* ioproxy) const;
1045+
10331046
/// Opens the file with given name and seek to the first subimage in the
10341047
/// file. Various file attributes are put in `newspec` and a copy
10351048
/// is also saved internally to the `ImageInput` (retrievable via

src/jpeg.imageio/jpeg_pvt.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,8 @@ class JpgInput final : public ImageInput {
6262
{
6363
return (feature == "exif" || feature == "iptc" || feature == "ioproxy");
6464
}
65-
bool valid_file(const std::string& filename) const override
66-
{
67-
return valid_file(filename, nullptr);
68-
}
65+
bool valid_file(Filesystem::IOProxy* ioproxy) const override;
66+
6967
bool open(const std::string& name, ImageSpec& spec) override;
7068
bool open(const std::string& name, ImageSpec& spec,
7169
const ImageSpec& config) override;
@@ -119,8 +117,6 @@ class JpgInput final : public ImageInput {
119117

120118
bool read_icc_profile(j_decompress_ptr cinfo, ImageSpec& spec);
121119

122-
bool valid_file(const std::string& filename, Filesystem::IOProxy* io) const;
123-
124120
void close_file() { init(); }
125121

126122
friend class JpgOutput;

src/jpeg.imageio/jpeginput.cpp

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -138,26 +138,16 @@ JpgInput::jpegerror(my_error_ptr /*myerr*/, bool fatal)
138138

139139

140140
bool
141-
JpgInput::valid_file(const std::string& filename, Filesystem::IOProxy* io) const
141+
JpgInput::valid_file(Filesystem::IOProxy* ioproxy) const
142142
{
143143
// Check magic number to assure this is a JPEG file
144-
uint8_t magic[2] = { 0, 0 };
145-
bool ok = true;
146-
147-
if (io) {
148-
ok = (io->pread(magic, sizeof(magic), 0) == sizeof(magic));
149-
} else {
150-
FILE* fd = Filesystem::fopen(filename, "rb");
151-
if (!fd)
152-
return false;
153-
ok = (::fread(magic, sizeof(magic), 1, fd) == 1);
154-
fclose(fd);
155-
}
144+
if (!ioproxy || ioproxy->mode() != Filesystem::IOProxy::Read)
145+
return false;
156146

157-
if (magic[0] != JPEG_MAGIC1 || magic[1] != JPEG_MAGIC2) {
158-
ok = false;
159-
}
160-
return ok;
147+
uint8_t magic[2] {};
148+
const size_t numRead = ioproxy->pread(magic, sizeof(magic), 0);
149+
return numRead == sizeof(magic) && magic[0] == JPEG_MAGIC1
150+
&& magic[1] == JPEG_MAGIC2;
161151
}
162152

163153

src/libOpenImageIO/imageinput.cpp

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,39 @@ ImageInput::~ImageInput() {}
9393
bool
9494
ImageInput::valid_file(const std::string& filename) const
9595
{
96-
ImageSpec tmpspec;
97-
bool ok = const_cast<ImageInput*>(this)->open(filename, tmpspec);
98-
if (ok)
99-
const_cast<ImageInput*>(this)->close();
96+
ImageInput* self = const_cast<ImageInput*>(this);
97+
98+
if (self->supports("ioproxy")) {
99+
Filesystem::IOFile io(filename, Filesystem::IOProxy::Read);
100+
return valid_file(&io);
101+
} else {
102+
ImageSpec tmpspec;
103+
bool ok = self->open(filename, tmpspec);
104+
if (ok)
105+
self->close();
106+
(void)geterror(); // Clear any errors
107+
return ok;
108+
}
109+
}
110+
111+
112+
113+
bool
114+
ImageInput::valid_file(Filesystem::IOProxy* ioproxy) const
115+
{
116+
ImageInput* self = const_cast<ImageInput*>(this);
117+
118+
/* Open using the ioproxy if possible. */
119+
if (!self->set_ioproxy(ioproxy))
120+
return false;
121+
122+
ImageSpec config, tmpspec;
123+
bool ok = self->open("", tmpspec, config);
124+
if (ok) {
125+
self->close();
126+
}
127+
self->ioproxy_clear();
128+
100129
(void)geterror(); // Clear any errors
101130
return ok;
102131
}

src/openexr.imageio/exrinput.cpp

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ class OpenEXRInput final : public ImageInput {
133133
|| feature == "iptc" // Because of arbitrary_metadata
134134
|| feature == "ioproxy");
135135
}
136-
bool valid_file(const std::string& filename) const override;
136+
bool valid_file(Filesystem::IOProxy* ioproxy) const override;
137137
bool open(const std::string& name, ImageSpec& newspec,
138138
const ImageSpec& config) override;
139139
bool open(const std::string& name, ImageSpec& newspec) override
@@ -245,8 +245,6 @@ class OpenEXRInput final : public ImageInput {
245245
m_missingcolor.clear();
246246
}
247247

248-
bool valid_file(const std::string& filename, Filesystem::IOProxy* io) const;
249-
250248
bool read_native_tiles_individually(int subimage, int miplevel, int xbegin,
251249
int xend, int ybegin, int yend,
252250
int zbegin, int zend, int chbegin,
@@ -338,24 +336,13 @@ OpenEXRInput::OpenEXRInput() { init(); }
338336

339337

340338
bool
341-
OpenEXRInput::valid_file(const std::string& filename) const
339+
OpenEXRInput::valid_file(Filesystem::IOProxy* ioproxy) const
342340
{
343-
return valid_file(filename, nullptr);
344-
}
345-
346-
341+
if (!ioproxy || ioproxy->mode() != Filesystem::IOProxy::Mode::Read)
342+
return false;
347343

348-
bool
349-
OpenEXRInput::valid_file(const std::string& filename,
350-
Filesystem::IOProxy* io) const
351-
{
352344
try {
353-
std::unique_ptr<Filesystem::IOProxy> local_io;
354-
if (!io) {
355-
io = new Filesystem::IOFile(filename, Filesystem::IOProxy::Read);
356-
local_io.reset(io);
357-
}
358-
OpenEXRInputStream IStream(filename.c_str(), io);
345+
OpenEXRInputStream IStream("", ioproxy);
359346
return Imf::isOpenExrFile(IStream);
360347
} catch (const std::exception& e) {
361348
return false;
@@ -381,7 +368,16 @@ OpenEXRInput::open(const std::string& name, ImageSpec& newspec,
381368
errorf("Could not open file \"%s\"", name);
382369
return false;
383370
}
384-
if (!valid_file(name, m_io)) {
371+
372+
// If we weren't given an IOProxy, create one now that just reads from
373+
// the file.
374+
if (!m_io) {
375+
m_io = new Filesystem::IOFile(name, Filesystem::IOProxy::Read);
376+
m_local_io.reset(m_io);
377+
}
378+
OIIO_ASSERT(m_io);
379+
380+
if (!valid_file(m_io)) {
385381
errorf("\"%s\" is not an OpenEXR file", name);
386382
return false;
387383
}
@@ -416,14 +412,8 @@ OpenEXRInput::open(const std::string& name, ImageSpec& newspec,
416412
// Clear the spec with default constructor
417413
m_spec = ImageSpec();
418414

419-
// Establish an input stream. If we weren't given an IOProxy, create one
420-
// now that just reads from the file.
415+
// Establish an input stream.
421416
try {
422-
if (!m_io) {
423-
m_io = new Filesystem::IOFile(name, Filesystem::IOProxy::Read);
424-
m_local_io.reset(m_io);
425-
}
426-
OIIO_ASSERT(m_io);
427417
if (m_io->mode() != Filesystem::IOProxy::Read) {
428418
// If the proxy couldn't be opened in write mode, try to
429419
// return an error.

src/png.imageio/pnginput.cpp

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class PNGInput final : public ImageInput {
2020
{
2121
return (feature == "ioproxy" || feature == "exif");
2222
}
23-
bool valid_file(const std::string& filename) const override;
23+
bool valid_file(Filesystem::IOProxy* ioproxy) const override;
2424
bool open(const std::string& name, ImageSpec& newspec) override;
2525
bool open(const std::string& name, ImageSpec& newspec,
2626
const ImageSpec& config) override;
@@ -110,16 +110,14 @@ OIIO_PLUGIN_EXPORTS_END
110110

111111

112112
bool
113-
PNGInput::valid_file(const std::string& filename) const
113+
PNGInput::valid_file(Filesystem::IOProxy* ioproxy) const
114114
{
115-
FILE* fd = Filesystem::fopen(filename, "rb");
116-
if (!fd)
115+
if (!ioproxy || ioproxy->mode() != Filesystem::IOProxy::Mode::Read)
117116
return false;
118-
unsigned char sig[8];
119-
bool ok = (fread(sig, 1, sizeof(sig), fd) == sizeof(sig)
120-
&& png_sig_cmp(sig, 0, 7) == 0);
121-
fclose(fd);
122-
return ok;
117+
118+
unsigned char sig[8] {};
119+
const size_t numRead = ioproxy->pread(sig, sizeof(sig), 0);
120+
return numRead == sizeof(sig) && png_sig_cmp(sig, 0, 8) == 0;
123121
}
124122

125123

@@ -136,7 +134,7 @@ PNGInput::open(const std::string& name, ImageSpec& newspec)
136134

137135
unsigned char sig[8];
138136
if (ioproxy()->pread(sig, sizeof(sig), 0) != sizeof(sig)
139-
|| png_sig_cmp(sig, 0, 7)) {
137+
|| png_sig_cmp(sig, 0, 8)) {
140138
if (!has_error())
141139
errorf("Not a PNG file");
142140
return false; // Read failed

src/sgi.imageio/sgiinput.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class SgiInput final : public ImageInput {
1818
{
1919
return feature == "ioproxy";
2020
}
21-
bool valid_file(const std::string& filename) const override;
21+
bool valid_file(Filesystem::IOProxy* ioproxy) const override;
2222
bool open(const std::string& name, ImageSpec& spec) override;
2323
bool open(const std::string& name, ImageSpec& newspec,
2424
const ImageSpec& config) override;
@@ -82,16 +82,14 @@ OIIO_PLUGIN_EXPORTS_END
8282

8383

8484
bool
85-
SgiInput::valid_file(const std::string& filename) const
85+
SgiInput::valid_file(Filesystem::IOProxy* ioproxy) const
8686
{
87-
FILE* fd = Filesystem::fopen(filename, "rb");
88-
if (!fd)
87+
if (!ioproxy || ioproxy->mode() != Filesystem::IOProxy::Read)
8988
return false;
90-
int16_t magic;
91-
bool ok = (::fread(&magic, sizeof(magic), 1, fd) == 1)
92-
&& (magic == sgi_pvt::SGI_MAGIC);
93-
fclose(fd);
94-
return ok;
89+
90+
int16_t magic {};
91+
const size_t numRead = ioproxy->pread(&magic, sizeof(magic), 0);
92+
return numRead == sizeof(magic) && magic == sgi_pvt::SGI_MAGIC;
9593
}
9694

9795

0 commit comments

Comments
 (0)