Skip to content

Commit 7538c43

Browse files
lgritzCopilot
andcommitted
build: several OpenEXR and OpenJPH build related fixes (#4875)
Original changes and goal: * Bump our "latest releases" CI tests to OpenEXR 3.4.0 * cleanup: dead guard removal for openexr -- our minimum OpenEXR release supports float vectors, so we don't need any `#if` guards for it anymore. * Fix warnings related to mixing Core and C++ library constants for H2J2K compression enums within a switch statement (only noticed on ARM Linux with Clang 18). But it seems that OpenEXR 3.4's auto-building of OpenJPH (to support exr's new h2j2k compression methods) revealed that our CI wasn't building and testing against OpenJPG as well as we thought. So: * Clean up some openjph API use and type mismatches * Suppress warnings on clang and recent gcc versions had with the OpenJPH headers and our use of them. * Fix our test scripts and reference output for the htj2k tests; I think maybe they weren't running at all before, and nad some incorrect file paths. Finally, * When no OpenEXR is found, update the auto-build version to 3.3.5. I wish I could go all the way to 3.4.0, but the OpenEXR 3.4.0 currently auto-builds OpenJPH in a way that (I think incorrectly) installs the OpenJPH components in ways that can overwrite or break existing installations. * Remove FindOpenJPH.cmake, the nomenclature clashes with the lowercase "openjph" used by the new OpenJPH exported cmake configs. * But those are only generated for OpenJPH >= 0.21.2, so make that our new minimum. (This only affects OIIO 3.1 and higher, so it's safe to do this.) --------- Signed-off-by: Larry Gritz <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 8132aa4 commit 7538c43

File tree

12 files changed

+502
-27
lines changed

12 files changed

+502
-27
lines changed

.github/workflows/ci.yml

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ jobs:
423423
cxx_std: 20
424424
fmt_ver: 11.2.0
425425
opencolorio_ver: v2.4.2
426-
openexr_ver: v3.4-alpha
426+
openexr_ver: v3.4.0
427427
pybind11_ver: v3.0.0
428428
python_ver: "3.12"
429429
simd: avx2,f16c
@@ -514,44 +514,44 @@ jobs:
514514
depcmds: |
515515
sudo rm -rf /usr/local/include/OpenEXR
516516
sudo rm -rf /usr/local/lib64/cmake/{IlmBase,OpenEXR}
517-
- desc: Linux ARM latest releases gcc14 C++20 py3.12 exr3.3 ocio2.4
518-
nametag: linux-latest-releases
517+
- desc: Linux ARM latest releases gcc14 C++20 py3.12 exr3.4 ocio2.4
518+
nametag: linux-arm-latest-releases
519519
runner: ubuntu-24.04-arm
520520
cc_compiler: gcc-14
521521
cxx_compiler: g++-14
522522
cxx_std: 20
523-
fmt_ver: 11.1.4
523+
fmt_ver: 11.2.0
524524
opencolorio_ver: v2.4.2
525-
openexr_ver: v3.3.3
525+
openexr_ver: v3.4.0
526526
pybind11_ver: v3.0.0
527527
python_ver: "3.12"
528-
setenvs: export LIBJPEGTURBO_VERSION=3.1.0
529-
LIBRAW_VERSION=0.21.3
528+
setenvs: export LIBJPEGTURBO_VERSION=3.1.1
529+
LIBRAW_VERSION=0.21.4
530530
LIBTIFF_VERSION=v4.7.0
531531
OPENJPEG_VERSION=v2.5.3
532532
PTEX_VERSION=v2.4.3
533533
PUGIXML_VERSION=v1.15
534-
WEBP_VERSION=v1.5.0
534+
WEBP_VERSION=v1.6.0
535535
FREETYPE_VERSION=VER-2-13-3
536536
USE_OPENVDB=0
537-
- desc: Linux ARM latest releases clang18 C++20 py3.12 exr3.3 ocio2.4
538-
nametag: linux-latest-releases
537+
- desc: Linux ARM latest releases clang18 C++20 py3.12 exr3.4 ocio2.4
538+
nametag: linux-arm-latest-releases-clang
539539
runner: ubuntu-24.04-arm
540540
cc_compiler: clang-18
541541
cxx_compiler: clang++-18
542542
cxx_std: 20
543-
fmt_ver: 11.1.4
543+
fmt_ver: 11.2.0
544544
opencolorio_ver: v2.4.2
545-
openexr_ver: v3.3.3
545+
openexr_ver: v3.4.0
546546
pybind11_ver: v3.0.0
547547
python_ver: "3.12"
548548
setenvs: export LIBJPEGTURBO_VERSION=3.1.0
549-
LIBRAW_VERSION=0.21.3
549+
LIBRAW_VERSION=0.21.4
550550
LIBTIFF_VERSION=v4.7.0
551551
OPENJPEG_VERSION=v2.5.3
552552
PTEX_VERSION=v2.4.3
553553
PUGIXML_VERSION=v1.15
554-
WEBP_VERSION=v1.5.0
554+
WEBP_VERSION=v1.6.0
555555
FREETYPE_VERSION=VER-2-13-3
556556
USE_OPENVDB=0
557557

INSTALL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ NEW or CHANGED MINIMUM dependencies since the last major release are **bold**.
8080
* libjxl >= 0.10.1 (tested through 0.11.1)
8181
* If you want support for "Ultra HDR" inside JPEG images:
8282
* libuhdr >= 1.3 (tested through 1.4)
83+
* If you want support for j2c files:
84+
* OpenJPH >= 0.21.2 (tested through 0.22)
8385
* We use PugiXML for XML parsing. There is a version embedded in the OIIO
8486
tree, but if you want to use an external, system-installed version (as
8587
may be required by some software distributions with policies against

src/build-scripts/build_openexr.bash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ set -ex
1111

1212
# Which OpenEXR to retrieve, how to build it
1313
OPENEXR_REPO=${OPENEXR_REPO:=https://github.com/AcademySoftwareFoundation/openexr.git}
14-
OPENEXR_VERSION=${OPENEXR_VERSION:=v3.2.4}
14+
OPENEXR_VERSION=${OPENEXR_VERSION:=v3.3.5}
1515

1616
# Where to install the final results
1717
LOCAL_DEPS_DIR=${LOCAL_DEPS_DIR:=${PWD}/ext}

src/cmake/build_OpenEXR.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# https://github.com/AcademySoftwareFoundation/OpenImageIO
44

55

6-
set_cache (OpenEXR_BUILD_VERSION 3.2.4 "OpenEXR version for local builds")
6+
set_cache (OpenEXR_BUILD_VERSION 3.3.5 "OpenEXR version for local builds")
77
set (OpenEXR_GIT_REPOSITORY "https://github.com/AcademySoftwareFoundation/OpenEXR")
88
set (OpenEXR_GIT_TAG "v${OpenEXR_BUILD_VERSION}")
99
set_cache (OpenEXR_BUILD_SHARED_LIBS ${LOCAL_BUILD_SHARED_LIBS_DEFAULT}

src/cmake/externalpackages.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ checked_find_package (OpenJPEG VERSION_MIN 2.0
176176
# Note: Recent OpenJPEG versions have exported cmake configs, but we don't
177177
# find them reliable at all, so we stick to our FindOpenJPEG.cmake module.
178178

179+
checked_find_package (openjph VERSION_MIN 0.21.2)
180+
179181
checked_find_package (OpenVDB
180182
VERSION_MIN 9.0
181183
DEPS TBB

src/jpeg2000.imageio/jpeg2000input.cpp

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@
1515
#include <OpenImageIO/sysutil.h>
1616
#include <OpenImageIO/tiffutils.h>
1717

18+
#ifdef USE_OPENJPH
19+
# include <openjph/ojph_codestream.h>
20+
# include <openjph/ojph_file.h>
21+
OIIO_PRAGMA_WARNING_PUSH
22+
OIIO_GCC_PRAGMA(GCC diagnostic ignored "-Wdelete-incomplete")
23+
# include <openjph/ojph_mem.h>
24+
OIIO_PRAGMA_WARNING_POP
25+
# include <openjph/ojph_message.h>
26+
# include <openjph/ojph_params.h>
27+
#endif
28+
1829
#ifndef OIIO_OPJ_VERSION
1930
# if defined(OPJ_VERSION_MAJOR)
2031
// OpenJPEG >= 2.1 defines these symbols
@@ -225,13 +236,256 @@ Jpeg2000Input::valid_file(Filesystem::IOProxy* ioproxy) const
225236
return is_jp2_header(header) || is_j2k_header(header);
226237
}
227238

239+
#ifdef USE_OPENJPH
240+
// A wrapper for ojph::infile_base to use OIIO's IOProxy
241+
class jph_infile : public ojph::infile_base {
242+
private:
243+
Filesystem::IOProxy* ioproxy;
244+
245+
public:
246+
jph_infile(Filesystem::IOProxy* iop) { ioproxy = iop; }
247+
~jph_infile()
248+
{
249+
// if (ioproxy != NULL)
250+
// ioproxy->close();
251+
}
252+
253+
//read reads size bytes, returns the number of bytes read
254+
size_t read(void* ptr, size_t size) { return ioproxy->read(ptr, size); }
255+
//seek returns 0 on success
256+
int seek(ojph::si64 offset, enum infile_base::seek origin)
257+
{
258+
return ioproxy->seek(offset, origin);
259+
}
260+
ojph::si64 tell() { return ioproxy->tell(); };
261+
bool eof()
262+
{
263+
int64_t pos = ioproxy->tell();
264+
if (pos < 0)
265+
return false; // Error condition, not EOF
266+
return pos == static_cast<int64_t>(ioproxy->size());
267+
}
268+
void close()
269+
{
270+
ioproxy->close();
271+
ioproxy = NULL;
272+
};
273+
};
274+
275+
276+
277+
// Convert a 32-bit signed integer to a 16-bit signed integer, with special
278+
// handling for special numbers (NaN, Infinity, etc.) if requested.
279+
ojph::si16
280+
convert_si32_to_si16(const ojph::si32 si32_value,
281+
bool convert_special_numbers_to_finite_numbers = false)
282+
{
283+
if (si32_value > INT16_MAX)
284+
return INT16_MAX;
285+
else if (si32_value < INT16_MIN)
286+
return INT16_MIN;
287+
else if (true == convert_special_numbers_to_finite_numbers) {
288+
const ojph::si16 si16_value = (ojph::si16)si32_value;
289+
half half_value;
290+
half_value.setBits(si16_value);
291+
if (half_value.isFinite())
292+
return si16_value;
293+
294+
// handle non-real number to real-number mapping
295+
if (half_value.isNan())
296+
half_value = 0.0f;
297+
else if (half_value.isInfinity() && !half_value.isNegative())
298+
half_value = HALF_MAX;
299+
else if (half_value.isInfinity() && half_value.isNegative())
300+
half_value = -1.0f * HALF_MAX;
301+
302+
return half_value.bits();
303+
} else
304+
return (ojph::si16)si32_value;
305+
}
306+
307+
308+
309+
bool
310+
Jpeg2000Input::ojph_read_header()
311+
{
312+
ojph::param_siz siz = codestream.access_siz();
313+
int ch = siz.get_num_components();
314+
const int w = siz.get_recon_width(0);
315+
const int h = siz.get_recon_height(0);
316+
TypeDesc dtype;
317+
318+
if (ch > 4)
319+
ch = 4; // Only do the first 4 channels.
320+
m_bpp.resize(ch);
321+
322+
for (int c = 0; c < ch; c++) {
323+
switch (siz.get_bit_depth(c)) {
324+
case 8:
325+
dtype = TypeDesc::UCHAR;
326+
m_bpp[c] = 1;
327+
break;
328+
case 10:
329+
case 12:
330+
case 16:
331+
m_bpp[c] = 2;
332+
dtype = TypeDesc::USHORT;
333+
break;
334+
case 32:
335+
m_bpp[c] = 4;
336+
dtype = TypeDesc::UINT;
337+
break;
338+
default:
339+
errorfmt("Unsupported bit depth {} for channel {}",
340+
siz.get_bit_depth(c), c);
341+
close();
342+
return false;
343+
}
344+
if (m_bpp[c] != m_bpp[0]) {
345+
errorfmt("All channels need to be the same bitdepth");
346+
close();
347+
return false;
348+
}
349+
}
350+
351+
m_spec = ImageSpec(w, h, ch, dtype);
352+
m_spec.default_channel_names();
353+
m_spec.attribute("oiio:BitsPerSample", siz.get_bit_depth(0));
354+
m_spec.set_colorspace("srgb_rec709_scene");
355+
356+
return true;
357+
}
358+
359+
360+
361+
bool
362+
Jpeg2000Input::ojph_read_image()
363+
{
364+
buffer_bpp = m_bpp[0];
365+
int w = m_spec.width;
366+
int h = m_spec.height;
367+
int ch = m_spec.nchannels;
368+
ojph::param_siz siz = codestream.access_siz();
369+
370+
const int bufsize = w * h * ch * buffer_bpp;
371+
m_buf.resize(bufsize);
372+
codestream.create();
373+
374+
int file_bit_depth = siz.get_bit_depth(0); // Assuming RGBA are the same.
375+
376+
// We are going to read the whole image into the buffer, since with openjph
377+
// its hard to easily grab part of the image.
378+
if (codestream.is_planar()) {
379+
for (int c = 0; c < ch; ++c)
380+
for (int i = 0; i < h; ++i) {
381+
ojph::ui32 comp_num;
382+
ojph::line_buf* line = codestream.pull(comp_num);
383+
const ojph::si32* sp = line->i32;
384+
OIIO_DASSERT(int(comp_num) == c);
385+
if (m_spec.format == TypeDesc::UCHAR) {
386+
unsigned char* dout = &m_buf[i * w * ch];
387+
dout += c;
388+
for (int j = w; j > 0; j--, dout += ch) {
389+
*dout = *sp++;
390+
}
391+
}
392+
if (m_spec.format == TypeDesc::USHORT) {
393+
unsigned short* dout
394+
= (unsigned short*)&m_buf[buffer_bpp * (i * w * ch)];
395+
dout += c;
396+
for (int j = w; j > 0; j--, dout += ch) {
397+
*dout = bit_range_convert(*sp++, file_bit_depth,
398+
buffer_bpp * 8);
399+
}
400+
}
401+
}
402+
} else {
403+
for (int i = 0; i < h; ++i) {
404+
for (int c = 0; c < ch; ++c) {
405+
ojph::ui32 comp_num;
406+
ojph::line_buf* line = codestream.pull(comp_num);
407+
const ojph::si32* sp = line->i32;
408+
OIIO_DASSERT(int(comp_num) == c);
409+
if (m_spec.format == TypeDesc::UCHAR) {
410+
unsigned char* dout = &m_buf[i * w * ch];
411+
dout += c;
412+
for (int j = w; j > 0; j--, dout += ch) {
413+
*dout = *sp++;
414+
}
415+
}
416+
if (m_spec.format == TypeDesc::USHORT) {
417+
unsigned short* dout
418+
= (unsigned short*)&m_buf[buffer_bpp * (i * w * ch)];
419+
dout += c;
420+
for (int j = w; j > 0; j--, dout += ch) {
421+
*dout = bit_range_convert(*sp++, file_bit_depth,
422+
buffer_bpp * 8);
423+
}
424+
}
425+
}
426+
}
427+
}
428+
429+
ojph_image_read = true;
430+
return true;
431+
}
432+
433+
434+
435+
class Oiio_Reader_Error_handler : public ojph::message_error {
436+
// This is a special error handler, since in this case, if we get the error-code for not a J2K file, we dont
437+
// want to print out anything. If not, we fall through to the regular error handler.
438+
ojph::message_error* default_error;
439+
440+
public:
441+
Oiio_Reader_Error_handler(ojph::message_error* error)
442+
{
443+
default_error = error;
444+
}
445+
virtual void operator()(int error_code, const char* file_name, int line_num,
446+
const char* fmt, ...)
447+
{
448+
if (error_code == 0x00050044) {
449+
throw std::runtime_error("ojph error: not HTJ2K file");
450+
}
451+
va_list args;
452+
va_start(args, fmt);
453+
default_error[0](error_code, file_name, line_num, fmt, args);
454+
va_end(args);
455+
}
456+
};
457+
458+
#endif // USE_OPENJPH
459+
228460
bool
229461
Jpeg2000Input::open(const std::string& name, ImageSpec& p_spec)
230462
{
231463
m_filename = name;
232464

233465
if (!ioproxy_use_or_open(name))
234466
return false;
467+
468+
#ifdef USE_OPENJPH
469+
jph_infile* jphinfile = new jph_infile(ioproxy());
470+
ojph_reader = true;
471+
ojph::message_error* default_error = ojph::get_error();
472+
// Disable the default OpenJPH error stream to prevent unwanted error output.
473+
// Errors will be handled by the custom error handler (Oiio_Reader_Error_handler) configured below.
474+
ojph::set_error_stream(nullptr);
475+
476+
try {
477+
Oiio_Reader_Error_handler error_handler(default_error);
478+
ojph::configure_error(&error_handler);
479+
codestream.read_headers(jphinfile);
480+
return ojph_read_header();
481+
} catch (const std::runtime_error& e) {
482+
ojph::configure_error(default_error);
483+
ojph_reader = false;
484+
}
485+
delete jphinfile;
486+
487+
#endif // USE_OPENJPH
488+
235489
ioseek(0);
236490

237491
m_codec = create_decompressor();

0 commit comments

Comments
 (0)