diff --git a/src/doc/imagebuf.rst b/src/doc/imagebuf.rst index 6956188fc7..4386612cb5 100644 --- a/src/doc/imagebuf.rst +++ b/src/doc/imagebuf.rst @@ -369,57 +369,28 @@ For the remainder of this section, we will assume that you have a Example: Visiting all pixels to compute an average color -------------------------------------------------------- -.. code-block:: cpp + .. tabs:: + + .. tab:: C++ + .. literalinclude:: ../../testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp + :language: c++ + :start-after: BEGIN-imagebuf-get-pixel-average + :end-before: END-imagebuf-get-pixel-average + :dedent: 4 - void print_channel_averages (const std::string &filename) - { - // Set up the ImageBuf and read the file - ImageBuf buf (filename); - bool ok = buf.read (0, 0, true, TypeDesc::FLOAT); // Force a float buffer - if (! ok) - return; - - // Initialize a vector to contain the running total - int nc = buf.nchannels(); - std::vector total (n, 0.0f); - - // Iterate over all pixels of the image, summing channels separately - for (ImageBuf::ConstIterator it (buf); ! it.done(); ++it) - for (int c = 0; c < nc; ++c) - total[c] += it[c]; - - // Print the averages - imagesize_t npixels = buf.spec().image_pixels(); - for (int c = 0; c < nc; ++c) - std::cout << "Channel " << c << " avg = " (total[c] / npixels) << "\n"; - } - - -.. _sec-make-black: Example: Set all pixels in a region to black -------------------------------------------- -.. code-block:: cpp - bool make_black (ImageBuf &buf, ROI region) - { - if (buf.spec().format != TypeDesc::FLOAT) - return false; // Assume it's a float buffer - - // Clamp the region's channel range to the channels in the image - roi.chend = std::min (roi.chend, buf.nchannels); - - // Iterate over all pixels in the region... - for (ImageBuf::Iterator it (buf, region); ! it.done(); ++it) { - if (! it.exists()) // Make sure the iterator is pointing - continue; // to a pixel in the data window - for (int c = roi.chbegin; c < roi.chend; ++c) - it[c] = 0.0f; // clear the value - } - return true; - } + .. tabs:: + .. tab:: C++ + .. literalinclude:: ../../testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp + :language: c++ + :start-after: BEGIN-imagebuf-set-region-black + :end-before: END-imagebuf-set-region-black + :dedent: 4 Dealing with buffer data types ============================== @@ -474,37 +445,14 @@ Strategy 2: Template your iterating functions based on buffer type Consider the following alternate version of the `make_black` function from Section `Example: Set all pixels in a region to black`_ :: - template - static bool make_black_impl (ImageBuf &buf, ROI region) - { - // Clamp the region's channel range to the channels in the image - roi.chend = std::min (roi.chend, buf.nchannels); - - // Iterate over all pixels in the region... - for (ImageBuf::Iterator it (buf, region); ! it.done(); ++it) { - if (! it.exists()) // Make sure the iterator is pointing - continue; // to a pixel in the data window - for (int c = roi.chbegin; c < roi.chend; ++c) - it[c] = 0.0f; // clear the value - } - return true; - } - - bool make_black (ImageBuf &buf, ROI region) - { - if (buf.spec().format == TypeDesc::FLOAT) - return make_black_impl (buf, region); - else if (buf.spec().format == TypeDesc::HALF) - return make_black_impl (buf, region); - else if (buf.spec().format == TypeDesc::UINT8) - return make_black_impl (buf, region); - else if (buf.spec().format == TypeDesc::UINT16) - return make_black_impl (buf, region); - else { - buf.error ("Unsupported pixel data format %s", buf.spec().format); - return false; - } - } + .. tabs:: + + .. tab:: C++ + .. literalinclude:: ../../testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp + :language: c++ + :start-after: BEGIN-imagebuf-iterator-template + :end-before: END-imagebuf-iterator-template + :dedent: 4 In this example, we make an implementation that is templated on the buffer type, and then a wrapper that calls the appropriate template specialization @@ -515,22 +463,14 @@ In fact, :file:`imagebufalgo_util.h` provides a macro to do this (and several variants, which will be discussed in more detail in the next chapter). You could rewrite the example even more simply:: - #include - - template - static bool make_black_impl (ImageBuf &buf, ROI region) - { - ... same as before ... - } - - bool make_black (ImageBuf &buf, ROI region) - { - bool ok; - OIIO_DISPATCH_COMMON_TYPES (ok, "make_black", make_black_impl, - buf.spec().format, buf, region); - return ok; - } + .. tabs:: + + .. tab:: C++ + .. literalinclude:: ../../testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp + :language: c++ + :start-after: BEGIN-imagebuf-disptach + :end-before: END-imagebuf-dispatch + :dedent: 4 This other type-dispatching helper macros will be discussed in more detail in Chapter :ref:`chap-imagebufalgo`. - diff --git a/testsuite/docs-examples-cpp/ref/out.txt b/testsuite/docs-examples-cpp/ref/out.txt index c2e31b2ba9..5318f0d5e1 100644 --- a/testsuite/docs-examples-cpp/ref/out.txt +++ b/testsuite/docs-examples-cpp/ref/out.txt @@ -1,3 +1,13 @@ +findaverages.exr : 640 x 480, 3 channel, float openexr + SHA-1: E8D3C267CBC4BBDE41343DCA008773A3E55B8159 +set-region-black.exr : 640 x 480, 3 channel, float openexr + SHA-1: 7006ED82385E7BCD1FB338B9D3DBFF754541E980 +set-region-black-template-dispatch.exr : 640 x 480, 3 channel, half openexr + SHA-1: FEB8B385D8784C4A16F3164D9B1A0F9113D64C86 +set-region-black-template-float.exr : 640 x 480, 3 channel, float openexr + SHA-1: 7006ED82385E7BCD1FB338B9D3DBFF754541E980 +set-region-black-template-uint8.exr : 640 x 480, 3 channel, half openexr + SHA-1: 9E09138C84FC4FDA52A6D76AED751B6551BB3417 error: Uninitialized input image error: Uninitialized input image zero1.exr : 512 x 512, 3 channel, float openexr diff --git a/testsuite/docs-examples-cpp/run.py b/testsuite/docs-examples-cpp/run.py index b3b02cf760..3667844cb4 100755 --- a/testsuite/docs-examples-cpp/run.py +++ b/testsuite/docs-examples-cpp/run.py @@ -26,6 +26,12 @@ # hashes merely check that the images don't change, but saves us the space # of checking in a full copy of the image if it's not needed. hashes = [ + # Outputs from the ImageBuf chapter: + "findaverages.exr", + "set-region-black.exr", + "set-region-black-template-dispatch.exr", + "set-region-black-template-float.exr", + "set-region-black-template-uint8.exr", # Outputs from the ImageBufAlgo chapter: "zero1.exr", "zero2.exr", diff --git a/testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp b/testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp index 278889ba75..916e047d00 100644 --- a/testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp +++ b/testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp @@ -30,12 +30,215 @@ void example1() // /////////////////////////////////////////////////////////////////////////// +#include +// BEGIN-imagebuf-get-pixel-avg +void print_channel_averages(const std::string& filename) +{ + // Set up the ImageBuf and read the file + ImageBuf buf(filename); + bool ok = buf.read(0, 0, true, TypeDesc::FLOAT); // Force a float buffer + if (!ok) + return; + + // Initialize a vector to contain the running total + int nc = buf.nchannels(); + std::vector total(nc, 0.0f); + + // Iterate over all pixels of the image, summing channels separately + for (ImageBuf::ConstIterator it(buf); !it.done(); ++it) + for (int c = 0; c < nc; ++c) + total[c] += it[c]; + + // Print the averages + imagesize_t npixels = buf.spec().image_pixels(); + for (int c = 0; c < nc; ++c) + std::cout << "Channel " << c << " avg = " << (total[c] / npixels) + << "\n"; +} + +// END-imagebuf-get-pixel-avg + +void print_channel_averages_example() +{ + const std::string filename = "findaverages.exr"; + int x_sz = 640; + int y_sz = 480; + ImageBuf A(ImageSpec(x_sz, y_sz, 3, TypeDesc::FLOAT)); + for (int i = 0; i < x_sz; ++i) + for (int j = 0; j < y_sz; ++j) { + // Create a square RGB gradient so determining an average is interesting + A.setpixel(i, j, 0, + cspan( + { powf(float(i) / (x_sz - 1), 2.0f), + powf(float(j) / (y_sz - 1), 2.0f), + powf(float(i * j) / (x_sz * y_sz - 1), 2.0f) })); + } + if (!A.write(filename)) { + std::cout << "error: " << A.geterror() << "\n"; + } else { + print_channel_averages(filename); + } +} + +// BEGIN-imagebuf-set-region-black +bool make_black(ImageBuf& buf, ROI region) +{ + if (buf.spec().format != TypeDesc::FLOAT) + return false; // Assume it's a float buffer + + // Clamp the region's channel range to the channels in the image + region.chend = std::min(region.chend, buf.nchannels()); + // Iterate over all pixels in the region... + for (ImageBuf::Iterator it(buf, region); !it.done(); ++it) { + if (!it.exists()) // Make sure the iterator is pointing + continue; // to a pixel in the data window + for (int c = region.chbegin; c < region.chend; ++c) + it[c] = 0.0f; // clear the value + } + return true; +} +// END-imagebuf-set-region-black + +void make_black_example() +{ + int x_sz = 640; + int y_sz = 480; + ImageBuf A(ImageSpec(x_sz, y_sz, 3, TypeDesc::FLOAT)); + for (int i = 0; i < x_sz; ++i) + for (int j = 0; j < y_sz; ++j) { + // Create RGB gradient so region changing is easy to see + A.setpixel(i, j, 0, + cspan({ float(i) / (x_sz - 1), + float(j) / (y_sz - 1), + float(i * j) / (x_sz * y_sz - 1) })); + } + // A rectangular region straddling the middle of the image above + ROI region(x_sz / 4, x_sz * 3 / 4, y_sz / 4, y_sz * 3 / 4, 0, 1, 0, 3); + if (make_black(A, region)) { + A.write("set-region-black.exr"); + } else { + std::cout << "error: buffer is not a float buffer\n"; + } +} + + + +// BEGIN-imagebuf-iterator-template +#include +template +static bool make_black_impl(ImageBuf& buf, ROI region) +{ + // Clamp the region's channel range to the channels in the image + region.chend = std::min(region.chend, buf.nchannels()); + + // Iterate over all pixels in the region... + for (ImageBuf::Iterator it(buf, region); !it.done(); ++it) { + if (!it.exists()) // Make sure the iterator is pointing + continue; // to a pixel in the data window + for (int c = region.chbegin; c < region.chend; ++c) + it[c] = 0.0f; // clear the value + } + return true; +} + +bool make_black_templated(ImageBuf& buf, ROI region) +{ + if (buf.spec().format == TypeDesc::FLOAT) + return make_black_impl(buf, region); + else if (buf.spec().format == TypeDesc::HALF) + return make_black_impl(buf, region); + else if (buf.spec().format == TypeDesc::UINT8) + return make_black_impl(buf, region); + else if (buf.spec().format == TypeDesc::UINT16) + return make_black_impl(buf, region); + else { + buf.errorf("Unsupported pixel data format %s", + buf.spec().format.c_str()); + return false; + } +} +// END-imagebuf-iterator-template + +void make_black_template_example() +{ + int x_sz = 640; + int y_sz = 480; + ImageBuf A(ImageSpec(x_sz, y_sz, 3, TypeDesc::FLOAT)); + for (int i = 0; i < x_sz; ++i) + for (int j = 0; j < y_sz; ++j) { + // Create RGB gradient so region changing is easy to see + A.setpixel(i, j, 0, + cspan({ float(i) / (x_sz - 1), + float(j) / (y_sz - 1), + float(i * j) / (x_sz * y_sz - 1) })); + } + // A rectangular region straddling the middle of the image above + ROI region(x_sz / 4, x_sz * 3 / 4, y_sz / 4, y_sz * 3 / 4, 0, 1, 0, 3); + if (make_black_templated(A, region)) { + A.write("set-region-black-template-float.exr"); + } else { + std::cout << "error: " << A.geterror() << "\n"; + } + + ImageBuf B(ImageSpec(x_sz, y_sz, 3, TypeDesc::UINT8)); + for (int i = 0; i < x_sz; ++i) + for (int j = 0; j < y_sz; ++j) { + // Create RGB gradient so region changing is easy to see + B.setpixel(i, j, 0, + cspan({ float(i) / (x_sz - 1), + float(j) / (y_sz - 1), + float(i * j) / (x_sz * y_sz - 1) })); + } + // A rectangular region straddling the middle of the image above + if (make_black_templated(B, region)) { + B.write("set-region-black-template-uint8.exr"); + } else { + std::cout << "error: " << B.geterror() << "\n"; + } +} + +// BEGIN-imagebuf-dispatch +#include +bool make_black_dispatch(ImageBuf& buf, ROI region) +{ + bool ok; + OIIO_DISPATCH_COMMON_TYPES(ok, "make_black_dispatch", make_black_impl, + buf.spec().format, buf, region); + return ok; +} +// END-imagebuf-dispatch + +void make_black_dispatch_example() +{ + int x_sz = 640; + int y_sz = 480; + ImageBuf A(ImageSpec(x_sz, y_sz, 3, TypeDesc::UINT16)); + for (int i = 0; i < x_sz; ++i) + for (int j = 0; j < y_sz; ++j) { + // Create RGB gradient so region changing is easy to see + A.setpixel(i, j, 0, + cspan({ float(i) / (x_sz - 1), + float(j) / (y_sz - 1), + float(i * j) / (x_sz * y_sz - 1) })); + } + // A rectangular region straddling the middle of the image above + ROI region(x_sz / 4, x_sz * 3 / 4, y_sz / 4, y_sz * 3 / 4, 0, 1, 0, 3); + if (make_black_templated(A, region)) { + A.write("set-region-black-template-dispatch.exr"); + } else { + std::cout << "error: " << A.geterror() << "\n"; + } +} int main(int /*argc*/, char** /*argv*/) { // Each example function needs to get called here, or it won't execute // as part of the test. - example1(); + // example1(); + print_channel_averages_example(); + make_black_example(); + make_black_template_example(); + make_black_dispatch_example(); return 0; }