diff --git a/src/include/OpenImageIO/imageio.h b/src/include/OpenImageIO/imageio.h index 8b3149675c..4fb69c8a92 100644 --- a/src/include/OpenImageIO/imageio.h +++ b/src/include/OpenImageIO/imageio.h @@ -390,6 +390,13 @@ class OIIO_API ImageSpec { /// overflow where it's not representable in an `imagesize_t`. imagesize_t scanline_bytes (bool native=false) const noexcept; + /// Returns the number of bytes comprising each scanline, if all channels + /// were of the given type. If `type` is `TypeUnknown`, then it returns + /// the bytes a scanline using each channel's native type. This will + /// return `std::numeric_limits::max()` in the event of an + /// overflow where it's not representable in an `imagesize_t`. + imagesize_t scanline_bytes(TypeDesc type) const noexcept; + /// Return the number of pixels comprising a tile (or 0 if it is not a /// tiled image). This will return /// `std::numeric_limits::max()` in the event of an @@ -404,6 +411,13 @@ class OIIO_API ImageSpec { /// case of per-channel formats). imagesize_t tile_bytes (bool native=false) const noexcept; + /// Returns the number of bytes comprising each tile, if all channels + /// were of the given type. If `type` is `TypeUnknown`, then it returns + /// the bytes a scanline using each channel's native type. This will + /// return `std::numeric_limits::max()` in the event of an + /// overflow where it's not representable in an `imagesize_t`. + imagesize_t tile_bytes(TypeDesc type) const noexcept; + /// Return the number of pixels for an entire image. This will /// return `std::numeric_limits::max()` in the event of /// an overflow where it's not representable in an `imagesize_t`. @@ -420,6 +434,12 @@ class OIIO_API ImageSpec { /// the case of per-channel formats). imagesize_t image_bytes (bool native=false) const noexcept; + /// Returns the number of bytes comprising an entire image of these + /// dimensions, if the values were all of type `datatype`. For the + /// special case of `datatype == `TypeUnknown`, compute the size of + /// the image in the "native" data types for all channels. + imagesize_t image_bytes(TypeDesc datatype) const noexcept; + /// Verify that on this platform, a `size_t` is big enough to hold the /// number of bytes (and pixels) in a scanline, a tile, and the /// whole image. If this returns false, the image is much too big diff --git a/src/libOpenImageIO/formatspec.cpp b/src/libOpenImageIO/formatspec.cpp index 4f7b7eef25..4e16604e9d 100644 --- a/src/libOpenImageIO/formatspec.cpp +++ b/src/libOpenImageIO/formatspec.cpp @@ -274,6 +274,17 @@ ImageSpec::scanline_bytes(bool native) const noexcept +imagesize_t +ImageSpec::scanline_bytes(TypeDesc type) const noexcept +{ + return type == TypeUnknown + ? scanline_bytes(true) + : clamped_mult64(clamped_mult64(size_t(width), size_t(nchannels)), + type.size()); +} + + + imagesize_t ImageSpec::tile_pixels() const noexcept { @@ -296,6 +307,17 @@ ImageSpec::tile_bytes(bool native) const noexcept +imagesize_t +ImageSpec::tile_bytes(TypeDesc type) const noexcept +{ + return type == TypeUnknown + ? tile_bytes(true) + : clamped_mult64(clamped_mult64(tile_pixels(), nchannels), + type.size()); +} + + + imagesize_t ImageSpec::image_pixels() const noexcept { @@ -317,6 +339,17 @@ ImageSpec::image_bytes(bool native) const noexcept +imagesize_t +ImageSpec::image_bytes(TypeDesc datatype) const noexcept +{ + if (datatype == TypeUnknown) + return image_bytes(false); // special case: native size + return clamped_mult64(image_pixels(), + imagesize_t(nchannels) * datatype.size()); +} + + + void ImageSpec::attribute(string_view name, TypeDesc type, const void* value) { diff --git a/src/libOpenImageIO/imagespec_test.cpp b/src/libOpenImageIO/imagespec_test.cpp index 3846faa92f..c701456f15 100644 --- a/src/libOpenImageIO/imagespec_test.cpp +++ b/src/libOpenImageIO/imagespec_test.cpp @@ -81,19 +81,42 @@ test_imagespec_pixels() OIIO_CHECK_EQUAL((size_t)(BYTES_IN_FLOAT * CHANNELS), spec.pixel_bytes()); OIIO_CHECK_EQUAL((imagesize_t)(BYTES_IN_FLOAT * CHANNELS * WIDTH), spec.scanline_bytes()); + OIIO_CHECK_EQUAL((imagesize_t)(sizeof(uint16_t) * CHANNELS * WIDTH), + spec.scanline_bytes(TypeUInt16)); + OIIO_CHECK_EQUAL(spec.scanline_bytes(true), + spec.scanline_bytes(TypeUnknown)); OIIO_CHECK_EQUAL((imagesize_t)(WIDTH * HEIGHT), spec.image_pixels()); // check that the magnitude is right (not clamped) -- should be about > 2^40 - long long expected_bytes = BYTES_IN_FLOAT * CHANNELS * WIDTH * HEIGHT; + uint64_t expected_bytes = BYTES_IN_FLOAT * CHANNELS * WIDTH * HEIGHT; // log (x) / log (2) = log2 (x) // log (2^32) / log (2) = log2 (2^32) = 32 // log (2^32) * M_LOG2E = 32 double log2_result = log((double)expected_bytes) * M_LOG2E; OIIO_CHECK_LT(40, log2_result); - OIIO_CHECK_EQUAL((imagesize_t)expected_bytes, spec.image_bytes()); + OIIO_CHECK_EQUAL(expected_bytes, spec.image_bytes()); std::cout << "expected_bytes = " << expected_bytes << ", log " << log((double)expected_bytes) << std::endl; + + OIIO_CHECK_EQUAL(spec.image_bytes(true), spec.image_bytes(TypeUnknown)); + OIIO_CHECK_EQUAL(spec.image_bytes(false), spec.image_bytes(TypeFloat)); + + // Check tiles -- should be zero + OIIO_CHECK_EQUAL(spec.tile_bytes(), 0); + OIIO_CHECK_EQUAL(spec.tile_bytes(TypeUnknown), 0); + OIIO_CHECK_EQUAL(spec.tile_bytes(TypeUInt16), 0); + // Make apparent tiles and check + spec.tile_width = 7; + spec.tile_height = 5; + spec.tile_depth = 3; + OIIO_CHECK_EQUAL((imagesize_t)(BYTES_IN_FLOAT * spec.nchannels + * spec.tile_pixels()), + spec.tile_bytes()); + OIIO_CHECK_EQUAL((imagesize_t)(sizeof(uint16_t) * spec.nchannels + * spec.tile_pixels()), + spec.tile_bytes(TypeUInt16)); + OIIO_CHECK_EQUAL(spec.tile_bytes(true), spec.tile_bytes(TypeUnknown)); } diff --git a/src/python/py_imagespec.cpp b/src/python/py_imagespec.cpp index 9e03819001..24ecd06b2b 100644 --- a/src/python/py_imagespec.cpp +++ b/src/python/py_imagespec.cpp @@ -112,8 +112,6 @@ declare_imagespec(py::module& m) return spec.channel_bytes(chan, native); }, "channel"_a, "native"_a = false) - // .def("pixel_bytes", - // [](const ImageSpec &spec){ return spec.pixel_bytes(); }) .def( "pixel_bytes", [](const ImageSpec& spec, bool native) { @@ -126,30 +124,36 @@ declare_imagespec(py::module& m) return spec.pixel_bytes(chbegin, chend, native); }, "chbegin"_a, "chend"_a, "native"_a = false) - // .def("scanline_bytes", - // [](const ImageSpec &spec){ return spec.scanline_bytes(); }) .def( "scanline_bytes", [](const ImageSpec& spec, bool native) { return spec.scanline_bytes(native); }, "native"_a = false) - // .def("tile_bytes", - // [](const ImageSpec &spec){ return spec.tile_bytes(); }) + .def("scanline_bytes", + [](const ImageSpec& spec, TypeDesc type) { + return spec.scanline_bytes(type); + }) .def( "tile_bytes", [](const ImageSpec& spec, bool native) { return spec.tile_bytes(native); }, "native"_a = false) - // .def("image_bytes", - // [](const ImageSpec &spec){ return spec.image_bytes(); }) + .def("tile_bytes", [](const ImageSpec& spec, + TypeDesc type) { return spec.tile_bytes(type); }) .def( "image_bytes", [](const ImageSpec& spec, bool native) { return spec.image_bytes(native); }, "native"_a = false) + .def( + "image_bytes", + [](const ImageSpec& spec, TypeDesc datatype) { + return spec.image_bytes(datatype); + }, + "native"_a = false) .def("tile_pixels", &ImageSpec::tile_pixels) .def("image_pixels", &ImageSpec::image_pixels) .def("size_t_safe", &ImageSpec::size_t_safe) diff --git a/testsuite/python-imagespec/ref/out-python3.txt b/testsuite/python-imagespec/ref/out-python3.txt index bb671e9666..e3f432e5b5 100644 --- a/testsuite/python-imagespec/ref/out-python3.txt +++ b/testsuite/python-imagespec/ref/out-python3.txt @@ -60,9 +60,9 @@ channel bytes = 4 channel_bytes(1) = 4 native 1 channel_bytes(4) = 4 native 4 pixel bytes = 20 native 8 -scanline bytes = 12800 native 5120 -tile bytes = 655360 native 262144 -image bytes = 6144000 native 2457600 +scanline bytes = 12800 native 5120 if uint16 6400 +tile bytes = 655360 native 262144 if uint16 327680 +image bytes = 6144000 native 2457600 if uint16 3072000 tile pixels = 32768 image_pixels = 307200 size_t_safe = True diff --git a/testsuite/python-imagespec/ref/out.txt b/testsuite/python-imagespec/ref/out.txt index 59c89e2199..e6342f9262 100644 --- a/testsuite/python-imagespec/ref/out.txt +++ b/testsuite/python-imagespec/ref/out.txt @@ -60,9 +60,9 @@ channel bytes = 4 channel_bytes(1) = 4 native 1 channel_bytes(4) = 4 native 4 pixel bytes = 20 native 8 -scanline bytes = 12800 native 5120 -tile bytes = 655360 native 262144 -image bytes = 6144000 native 2457600 +scanline bytes = 12800 native 5120 if uint16 6400 +tile bytes = 655360 native 262144 if uint16 327680 +image bytes = 6144000 native 2457600 if uint16 3072000 tile pixels = 32768 image_pixels = 307200 size_t_safe = True diff --git a/testsuite/python-imagespec/src/test_imagespec.py b/testsuite/python-imagespec/src/test_imagespec.py index 63714b8360..645d8dc28e 100755 --- a/testsuite/python-imagespec/src/test_imagespec.py +++ b/testsuite/python-imagespec/src/test_imagespec.py @@ -76,9 +76,9 @@ def print_imagespec (spec, msg="") : print (" channel_bytes(1) =", s.channel_bytes(1), "native", s.channel_bytes(1,True)) print (" channel_bytes(4) =", s.channel_bytes(4), "native", s.channel_bytes(4,True)) print ("pixel bytes =", s.pixel_bytes(), "native", s.pixel_bytes(True)) - print ("scanline bytes =", s.scanline_bytes(), "native", s.scanline_bytes(True)) - print ("tile bytes =", s.tile_bytes(), "native", s.tile_bytes(True)) - print ("image bytes =", s.image_bytes(), "native", s.image_bytes(True)) + print ("scanline bytes =", s.scanline_bytes(), "native", s.scanline_bytes(True), "if uint16", s.scanline_bytes("uint16")) + print ("tile bytes =", s.tile_bytes(), "native", s.tile_bytes(True), "if uint16", s.tile_bytes("uint16")) + print ("image bytes =", s.image_bytes(), "native", s.image_bytes(True), "if uint16", s.image_bytes("uint16")) print ("tile pixels =", s.tile_pixels()) print ("image_pixels =", s.image_pixels()) print ("size_t_safe =", s.size_t_safe())