Skip to content

Commit 2555a85

Browse files
lgritzzachlewis
authored andcommitted
api: ImageSpec::scanline_bytes, tile_bytes, image_bytes (AcademySoftwareFoundation#4631)
Existing versions could return the expected bytes for a scanline, tile, or image for either the native per-channel types, or the single composite channel type. The new flavor lets you pass in any data type and it tells you how many bytes you'd need for a scanline, tile, or image if all channels were the passed type. (As a special case, the answer for TypeUnknown is the "native" size, same as `thing_bytes(true)`). This is handy for computing how big your buffer needs to be to hold a type-converted scanline or tile. Signed-off-by: Larry Gritz <[email protected]>
1 parent 7fa6930 commit 2555a85

File tree

7 files changed

+99
-19
lines changed

7 files changed

+99
-19
lines changed

src/include/OpenImageIO/imageio.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,13 @@ class OIIO_API ImageSpec {
390390
/// overflow where it's not representable in an `imagesize_t`.
391391
imagesize_t scanline_bytes (bool native=false) const noexcept;
392392

393+
/// Returns the number of bytes comprising each scanline, if all channels
394+
/// were of the given type. If `type` is `TypeUnknown`, then it returns
395+
/// the bytes a scanline using each channel's native type. This will
396+
/// return `std::numeric_limits<imagesize_t>::max()` in the event of an
397+
/// overflow where it's not representable in an `imagesize_t`.
398+
imagesize_t scanline_bytes(TypeDesc type) const noexcept;
399+
393400
/// Return the number of pixels comprising a tile (or 0 if it is not a
394401
/// tiled image). This will return
395402
/// `std::numeric_limits<imagesize_t>::max()` in the event of an
@@ -404,6 +411,13 @@ class OIIO_API ImageSpec {
404411
/// case of per-channel formats).
405412
imagesize_t tile_bytes (bool native=false) const noexcept;
406413

414+
/// Returns the number of bytes comprising each tile, if all channels
415+
/// were of the given type. If `type` is `TypeUnknown`, then it returns
416+
/// the bytes a scanline using each channel's native type. This will
417+
/// return `std::numeric_limits<imagesize_t>::max()` in the event of an
418+
/// overflow where it's not representable in an `imagesize_t`.
419+
imagesize_t tile_bytes(TypeDesc type) const noexcept;
420+
407421
/// Return the number of pixels for an entire image. This will
408422
/// return `std::numeric_limits<imagesize_t>::max()` in the event of
409423
/// an overflow where it's not representable in an `imagesize_t`.
@@ -420,6 +434,12 @@ class OIIO_API ImageSpec {
420434
/// the case of per-channel formats).
421435
imagesize_t image_bytes (bool native=false) const noexcept;
422436

437+
/// Returns the number of bytes comprising an entire image of these
438+
/// dimensions, if the values were all of type `datatype`. For the
439+
/// special case of `datatype == `TypeUnknown`, compute the size of
440+
/// the image in the "native" data types for all channels.
441+
imagesize_t image_bytes(TypeDesc datatype) const noexcept;
442+
423443
/// Verify that on this platform, a `size_t` is big enough to hold the
424444
/// number of bytes (and pixels) in a scanline, a tile, and the
425445
/// whole image. If this returns false, the image is much too big

src/libOpenImageIO/formatspec.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,17 @@ ImageSpec::scanline_bytes(bool native) const noexcept
274274

275275

276276

277+
imagesize_t
278+
ImageSpec::scanline_bytes(TypeDesc type) const noexcept
279+
{
280+
return type == TypeUnknown
281+
? scanline_bytes(true)
282+
: clamped_mult64(clamped_mult64(size_t(width), size_t(nchannels)),
283+
type.size());
284+
}
285+
286+
287+
277288
imagesize_t
278289
ImageSpec::tile_pixels() const noexcept
279290
{
@@ -296,6 +307,17 @@ ImageSpec::tile_bytes(bool native) const noexcept
296307

297308

298309

310+
imagesize_t
311+
ImageSpec::tile_bytes(TypeDesc type) const noexcept
312+
{
313+
return type == TypeUnknown
314+
? tile_bytes(true)
315+
: clamped_mult64(clamped_mult64(tile_pixels(), nchannels),
316+
type.size());
317+
}
318+
319+
320+
299321
imagesize_t
300322
ImageSpec::image_pixels() const noexcept
301323
{
@@ -317,6 +339,17 @@ ImageSpec::image_bytes(bool native) const noexcept
317339

318340

319341

342+
imagesize_t
343+
ImageSpec::image_bytes(TypeDesc datatype) const noexcept
344+
{
345+
if (datatype == TypeUnknown)
346+
return image_bytes(false); // special case: native size
347+
return clamped_mult64(image_pixels(),
348+
imagesize_t(nchannels) * datatype.size());
349+
}
350+
351+
352+
320353
void
321354
ImageSpec::attribute(string_view name, TypeDesc type, const void* value)
322355
{

src/libOpenImageIO/imagespec_test.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,42 @@ test_imagespec_pixels()
8181
OIIO_CHECK_EQUAL((size_t)(BYTES_IN_FLOAT * CHANNELS), spec.pixel_bytes());
8282
OIIO_CHECK_EQUAL((imagesize_t)(BYTES_IN_FLOAT * CHANNELS * WIDTH),
8383
spec.scanline_bytes());
84+
OIIO_CHECK_EQUAL((imagesize_t)(sizeof(uint16_t) * CHANNELS * WIDTH),
85+
spec.scanline_bytes(TypeUInt16));
86+
OIIO_CHECK_EQUAL(spec.scanline_bytes(true),
87+
spec.scanline_bytes(TypeUnknown));
8488
OIIO_CHECK_EQUAL((imagesize_t)(WIDTH * HEIGHT), spec.image_pixels());
8589

8690
// check that the magnitude is right (not clamped) -- should be about > 2^40
87-
long long expected_bytes = BYTES_IN_FLOAT * CHANNELS * WIDTH * HEIGHT;
91+
uint64_t expected_bytes = BYTES_IN_FLOAT * CHANNELS * WIDTH * HEIGHT;
8892
// log (x) / log (2) = log2 (x)
8993
// log (2^32) / log (2) = log2 (2^32) = 32
9094
// log (2^32) * M_LOG2E = 32
9195
double log2_result = log((double)expected_bytes) * M_LOG2E;
9296
OIIO_CHECK_LT(40, log2_result);
93-
OIIO_CHECK_EQUAL((imagesize_t)expected_bytes, spec.image_bytes());
97+
OIIO_CHECK_EQUAL(expected_bytes, spec.image_bytes());
9498

9599
std::cout << "expected_bytes = " << expected_bytes << ", log "
96100
<< log((double)expected_bytes) << std::endl;
101+
102+
OIIO_CHECK_EQUAL(spec.image_bytes(true), spec.image_bytes(TypeUnknown));
103+
OIIO_CHECK_EQUAL(spec.image_bytes(false), spec.image_bytes(TypeFloat));
104+
105+
// Check tiles -- should be zero
106+
OIIO_CHECK_EQUAL(spec.tile_bytes(), 0);
107+
OIIO_CHECK_EQUAL(spec.tile_bytes(TypeUnknown), 0);
108+
OIIO_CHECK_EQUAL(spec.tile_bytes(TypeUInt16), 0);
109+
// Make apparent tiles and check
110+
spec.tile_width = 7;
111+
spec.tile_height = 5;
112+
spec.tile_depth = 3;
113+
OIIO_CHECK_EQUAL((imagesize_t)(BYTES_IN_FLOAT * spec.nchannels
114+
* spec.tile_pixels()),
115+
spec.tile_bytes());
116+
OIIO_CHECK_EQUAL((imagesize_t)(sizeof(uint16_t) * spec.nchannels
117+
* spec.tile_pixels()),
118+
spec.tile_bytes(TypeUInt16));
119+
OIIO_CHECK_EQUAL(spec.tile_bytes(true), spec.tile_bytes(TypeUnknown));
97120
}
98121

99122

src/python/py_imagespec.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,6 @@ declare_imagespec(py::module& m)
112112
return spec.channel_bytes(chan, native);
113113
},
114114
"channel"_a, "native"_a = false)
115-
// .def("pixel_bytes",
116-
// [](const ImageSpec &spec){ return spec.pixel_bytes(); })
117115
.def(
118116
"pixel_bytes",
119117
[](const ImageSpec& spec, bool native) {
@@ -126,30 +124,36 @@ declare_imagespec(py::module& m)
126124
return spec.pixel_bytes(chbegin, chend, native);
127125
},
128126
"chbegin"_a, "chend"_a, "native"_a = false)
129-
// .def("scanline_bytes",
130-
// [](const ImageSpec &spec){ return spec.scanline_bytes(); })
131127
.def(
132128
"scanline_bytes",
133129
[](const ImageSpec& spec, bool native) {
134130
return spec.scanline_bytes(native);
135131
},
136132
"native"_a = false)
137-
// .def("tile_bytes",
138-
// [](const ImageSpec &spec){ return spec.tile_bytes(); })
133+
.def("scanline_bytes",
134+
[](const ImageSpec& spec, TypeDesc type) {
135+
return spec.scanline_bytes(type);
136+
})
139137
.def(
140138
"tile_bytes",
141139
[](const ImageSpec& spec, bool native) {
142140
return spec.tile_bytes(native);
143141
},
144142
"native"_a = false)
145-
// .def("image_bytes",
146-
// [](const ImageSpec &spec){ return spec.image_bytes(); })
143+
.def("tile_bytes", [](const ImageSpec& spec,
144+
TypeDesc type) { return spec.tile_bytes(type); })
147145
.def(
148146
"image_bytes",
149147
[](const ImageSpec& spec, bool native) {
150148
return spec.image_bytes(native);
151149
},
152150
"native"_a = false)
151+
.def(
152+
"image_bytes",
153+
[](const ImageSpec& spec, TypeDesc datatype) {
154+
return spec.image_bytes(datatype);
155+
},
156+
"native"_a = false)
153157
.def("tile_pixels", &ImageSpec::tile_pixels)
154158
.def("image_pixels", &ImageSpec::image_pixels)
155159
.def("size_t_safe", &ImageSpec::size_t_safe)

testsuite/python-imagespec/ref/out-python3.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ channel bytes = 4
6060
channel_bytes(1) = 4 native 1
6161
channel_bytes(4) = 4 native 4
6262
pixel bytes = 20 native 8
63-
scanline bytes = 12800 native 5120
64-
tile bytes = 655360 native 262144
65-
image bytes = 6144000 native 2457600
63+
scanline bytes = 12800 native 5120 if uint16 6400
64+
tile bytes = 655360 native 262144 if uint16 327680
65+
image bytes = 6144000 native 2457600 if uint16 3072000
6666
tile pixels = 32768
6767
image_pixels = 307200
6868
size_t_safe = True

testsuite/python-imagespec/ref/out.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ channel bytes = 4
6060
channel_bytes(1) = 4 native 1
6161
channel_bytes(4) = 4 native 4
6262
pixel bytes = 20 native 8
63-
scanline bytes = 12800 native 5120
64-
tile bytes = 655360 native 262144
65-
image bytes = 6144000 native 2457600
63+
scanline bytes = 12800 native 5120 if uint16 6400
64+
tile bytes = 655360 native 262144 if uint16 327680
65+
image bytes = 6144000 native 2457600 if uint16 3072000
6666
tile pixels = 32768
6767
image_pixels = 307200
6868
size_t_safe = True

testsuite/python-imagespec/src/test_imagespec.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ def print_imagespec (spec: oiio.ImageSpec, msg="") :
7777
print (" channel_bytes(1) =", s.channel_bytes(1), "native", s.channel_bytes(1,True))
7878
print (" channel_bytes(4) =", s.channel_bytes(4), "native", s.channel_bytes(4,True))
7979
print ("pixel bytes =", s.pixel_bytes(), "native", s.pixel_bytes(True))
80-
print ("scanline bytes =", s.scanline_bytes(), "native", s.scanline_bytes(True))
81-
print ("tile bytes =", s.tile_bytes(), "native", s.tile_bytes(True))
82-
print ("image bytes =", s.image_bytes(), "native", s.image_bytes(True))
80+
print ("scanline bytes =", s.scanline_bytes(), "native", s.scanline_bytes(True), "if uint16", s.scanline_bytes("uint16"))
81+
print ("tile bytes =", s.tile_bytes(), "native", s.tile_bytes(True), "if uint16", s.tile_bytes("uint16"))
82+
print ("image bytes =", s.image_bytes(), "native", s.image_bytes(True), "if uint16", s.image_bytes("uint16"))
8383
print ("tile pixels =", s.tile_pixels())
8484
print ("image_pixels =", s.image_pixels())
8585
print ("size_t_safe =", s.size_t_safe())

0 commit comments

Comments
 (0)