Skip to content

Commit aa54f04

Browse files
committed
jpeg2000: valid_file implementation, much faster than trying to open
Primary cost of detecting whether a given file is valid jpeg2000 file is the thread pool initialization and shutdown that Jpeg2000Input::open does (the actual thread pool part is inside OpenJpeg code). So add a dedicated valid_file() implementation that only needs to check 12 bytes of the header. While at it, I changed already existing isJp2File() to is_jp2_header() to better match naming conventions used elsewhere, and instead of trying to handle both little and big endian cases by manual repetition of two sets of magic integers, let's do just byte comparisons with memcmp instead. On my PC (Ryzen 5950X, SSD, Windows VS2022), doing ImageInput::create() on 1138 files where they are not images at all (so OIIO in turns tries all the input plugins on them): - Before: 3.4 seconds spent in Jpeg2000Input::open (1.9s opj_thread_pool_create, 1.3s opj_thread_pool_destroy) - After: 33 milliseconds spent in Jpeg2000Input::valid_file Signed-off-by: Aras Pranckevicius <[email protected]>
1 parent 9ee71c4 commit aa54f04

File tree

1 file changed

+30
-20
lines changed

1 file changed

+30
-20
lines changed

src/jpeg2000.imageio/jpeg2000input.cpp

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class Jpeg2000Input final : public ImageInput {
8080
return feature == "ioproxy";
8181
// FIXME: we should support Exif/IPTC, but currently don't.
8282
}
83+
bool valid_file(Filesystem::IOProxy* ioproxy) const override;
8384
bool open(const std::string& name, ImageSpec& spec) override;
8485
bool open(const std::string& name, ImageSpec& newspec,
8586
const ImageSpec& config) override;
@@ -97,7 +98,8 @@ class Jpeg2000Input final : public ImageInput {
9798

9899
void init(void);
99100

100-
bool isJp2File(const int* const p_magicTable) const;
101+
static bool is_jp2_header(const uint8_t header[12]);
102+
static bool is_j2k_header(const uint8_t header[5]);
101103

102104
opj_codec_t* create_decompressor();
103105
void destroy_decompressor();
@@ -208,7 +210,19 @@ Jpeg2000Input::init(void)
208210
ioproxy_clear();
209211
}
210212

213+
bool
214+
Jpeg2000Input::valid_file(Filesystem::IOProxy* ioproxy) const
215+
{
216+
if (!ioproxy || ioproxy->mode() != Filesystem::IOProxy::Mode::Read)
217+
return false;
211218

219+
uint8_t header[12];
220+
auto r = ioproxy->pread(header, sizeof(header), 0);
221+
if (r != sizeof(header)) {
222+
return false;
223+
}
224+
return is_jp2_header(header) || is_j2k_header(header);
225+
}
212226

213227
bool
214228
Jpeg2000Input::open(const std::string& name, ImageSpec& p_spec)
@@ -409,36 +423,32 @@ Jpeg2000Input::close(void)
409423
return true;
410424
}
411425

412-
413426
bool
414-
Jpeg2000Input::isJp2File(const int* const p_magicTable) const
427+
Jpeg2000Input::is_jp2_header(const uint8_t header[12])
415428
{
416-
const int32_t JP2_MAGIC = 0x0000000C, JP2_MAGIC2 = 0x0C000000;
417-
if (p_magicTable[0] == JP2_MAGIC || p_magicTable[0] == JP2_MAGIC2) {
418-
const int32_t JP2_SIG1_MAGIC = 0x6A502020, JP2_SIG1_MAGIC2 = 0x2020506A;
419-
const int32_t JP2_SIG2_MAGIC = 0x0D0A870A, JP2_SIG2_MAGIC2 = 0x0A870A0D;
420-
if ((p_magicTable[1] == JP2_SIG1_MAGIC
421-
|| p_magicTable[1] == JP2_SIG1_MAGIC2)
422-
&& (p_magicTable[2] == JP2_SIG2_MAGIC
423-
|| p_magicTable[2] == JP2_SIG2_MAGIC2)) {
424-
return true;
425-
}
426-
}
427-
return false;
429+
const uint8_t jp2_header[] = { 0x0, 0x0, 0x0, 0x0C, 0x6A, 0x50,
430+
0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A };
431+
return memcmp(header, jp2_header, sizeof(jp2_header)) == 0;
428432
}
429433

434+
bool
435+
Jpeg2000Input::is_j2k_header(const uint8_t header[5])
436+
{
437+
const uint8_t j2k_header[] = { 0xFF, 0x4F, 0xFF, 0x51, 0x00 };
438+
return memcmp(header, j2k_header, sizeof(j2k_header)) == 0;
439+
}
430440

431441
opj_codec_t*
432442
Jpeg2000Input::create_decompressor()
433443
{
434-
int magic[3];
435-
auto r = ioproxy()->pread(magic, sizeof(magic), 0);
436-
if (r != 3 * sizeof(int)) {
444+
uint8_t header[12];
445+
auto r = ioproxy()->pread(header, sizeof(header), 0);
446+
if (r != sizeof(header)) {
437447
errorfmt("Empty file \"{}\"", m_filename);
438448
return nullptr;
439449
}
440-
return opj_create_decompress(isJp2File(magic) ? OPJ_CODEC_JP2
441-
: OPJ_CODEC_J2K);
450+
return opj_create_decompress(is_jp2_header(header) ? OPJ_CODEC_JP2
451+
: OPJ_CODEC_J2K);
442452
}
443453

444454

0 commit comments

Comments
 (0)