From 36633107514942f00ed8c5a25ef2c12f8cdd1795 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Mon, 6 Jan 2025 17:06:16 -0800 Subject: [PATCH] fix(png): alpha premultiplication adjustment and attribute (#4585) See discussion https://github.com/AcademySoftwareFoundation/OpenImageIO/discussions/4054 and PR https://github.com/AcademySoftwareFoundation/OpenImageIO/pull/4315 In PR 4315, we fixed PNG read/write to do the required premultiplication in linear space (that is, linearize, multiply by alpha, then go back to the sRGB or gamma space). Which we really believe is "correct." Except that maybe there are a ton of incorrectly made PNG files out there (maybe most of them?) where partial alpha pixels had their premultiplication occur on the sRGB or gamma values directly. In this patch, we partly revert, allowing both potential behaviors, controlled by an attribute "png:linear_premult", which instructs the PNG driver to do any premultiplication in linear space if it's set to nonzero. It can be set globally (via `OIIO::attribute()`), as well as set/overridden on any individual file by setting the attribute in the configuration hints for an ImageInput or in the spec for an ImageOutput. As presented in this patch, we're setting the default to 0, meaning that by default we are reverting back to the old behavior of doing the premultiply of partial alpha pixels on the direct values intead of in a linear space. Applications or sites that are confident that any PNG files they encounter are "correct" can set the attribute to do the multiplication linearly. I'm not 100% confident about the default, and so am very happy to entertain arguments for keeping the default set to do the multiplication in linear space. I had to change an internal spin mutex to a recursive mutex in order to address a latent misdesign that was made symptomatic by this change. Basically, asking for an attribute inside an ImageInput::init could make a deadlock because certain attribute queries (that catalogue the list of plugins) might instantiate those plugins, thus causing their init functions to run, leading to recursive dependence on the mutex that guards attribute queries. --------- Signed-off-by: Larry Gritz Co-authored-by: Brecht Van Lommel --- src/doc/builtinplugins.rst | 26 ++++++++++++++++++- src/include/OpenImageIO/imageio.h | 8 ++++++ src/include/imageio_pvt.h | 1 + src/libOpenImageIO/imageio.cpp | 15 ++++++++--- src/png.imageio/pnginput.cpp | 32 +++++++++++++++-------- src/png.imageio/pngoutput.cpp | 37 +++++++++++++++++---------- testsuite/png/ref/out-libpng15.txt | 38 ++++++++++++++++++++++++++++ testsuite/png/ref/out.txt | 38 ++++++++++++++++++++++++++++ testsuite/png/run.py | 5 +++- testsuite/png/src/gimp_gradient.png | Bin 0 -> 24653 bytes 10 files changed, 171 insertions(+), 29 deletions(-) create mode 100644 testsuite/png/src/gimp_gradient.png diff --git a/src/doc/builtinplugins.rst b/src/doc/builtinplugins.rst index 5b36fd7a07..dfe1b4b00e 100644 --- a/src/doc/builtinplugins.rst +++ b/src/doc/builtinplugins.rst @@ -1754,6 +1754,12 @@ attributes are supported: - ptr - Pointer to a ``Filesystem::IOProxy`` that will handle the I/O, for example by reading from memory rather than the file system. + * - ``png:linear_premult`` + - int + - If nonzero, will convert or gamma-encoded values to linear color + space for any premultiplication-by-alpha step done by the PNG reader. + If zero (the default), any needed premultiplication will happen directly + to the encoded values. **Configuration settings for PNG output** @@ -1797,13 +1803,31 @@ control aspects of the writing itself: to have larger PNG files on disk, you may want to use that value for this attribute. + * - ``png:linear_premult`` + - int + - If nonzero, will convert sRGB or gamma-encoded values to linear color + space for any unpremultiplication-by-alpha step done by the PNG writer. + If zero (the default), any needed unpremultiplication will happen + directly to the encoded sRGB or gamma-corrected values. + **Custom I/O Overrides** PNG input and output both support the "custom I/O" feature via the special ``"oiio:ioproxy"`` attributes (see Sections :ref:`sec-imageoutput-ioproxy` and :ref:`sec-imageinput-ioproxy`) as well as the `set_ioproxy()` methods. - +**Note on premultiplication** + +PNG files encoded as sRGB or gamma-corrected values that also have alpha +should (in theory) have any premultiplication performed in a linear space +(that is, the color should first be linearized, then premultiplied by alpha, +then converted back to the nonlinear form). However, many existing PNG files +are apparently encoded with the assumption that any premultiplication will be +performed directly on the encoded values, so that is the default behavior for +OpenImageIO's PNG reader and writer will. If you want to force the reader or +writer to linearize the values for premultiplication, you can set either the +reader/writer configuration hint or the global OIIO attribute +``png:linear_premult`` to 1. **Limitations** diff --git a/src/include/OpenImageIO/imageio.h b/src/include/OpenImageIO/imageio.h index 20224e8b31..8b3149675c 100644 --- a/src/include/OpenImageIO/imageio.h +++ b/src/include/OpenImageIO/imageio.h @@ -2908,6 +2908,14 @@ OIIO_API std::string geterror(bool clear = true); /// and only set ImageDescription if the parsing fails. Otherwise, always /// set ImageDescription to the first comment block. Default is 1. /// +/// - `int png:linear_premult` (0) +/// +/// If nonzero, will convert perform any necessary premultiplication by +/// alpha steps needed of the PNG reader/writer in a linear color space. +/// If zero (the default), any needed premultiplication will happen +/// directly on the values, even if they are sRGB or gamma-corrected. +/// For more information, please see OpenImageIO's documentation on the +/// built-in PNG format support. /// /// - `int limits:channels` (1024) /// diff --git a/src/include/imageio_pvt.h b/src/include/imageio_pvt.h index a626ddeb94..1ee710c608 100644 --- a/src/include/imageio_pvt.h +++ b/src/include/imageio_pvt.h @@ -43,6 +43,7 @@ extern OIIO_UTIL_API int oiio_print_uncaught_errors; extern int oiio_log_times; extern int openexr_core; extern int jpeg_com_attributes; +extern int png_linear_premult; extern int limit_channels; extern int limit_imagesize_MB; extern int imagebuf_print_uncaught_errors; diff --git a/src/libOpenImageIO/imageio.cpp b/src/libOpenImageIO/imageio.cpp index cdfd004b76..63512fffd3 100644 --- a/src/libOpenImageIO/imageio.cpp +++ b/src/libOpenImageIO/imageio.cpp @@ -49,6 +49,7 @@ atomic_int oiio_try_all_readers(1); // Should we use "Exr core C library"? int openexr_core(OIIO_OPENEXR_CORE_DEFAULT); int jpeg_com_attributes(1); +int png_linear_premult(0); int tiff_half(0); int tiff_multithread(1); int dds_bc5normal(0); @@ -72,7 +73,7 @@ using namespace pvt; namespace { // Hidden global OIIO data. -static spin_mutex attrib_mutex; +static std::recursive_mutex attrib_mutex; static const int maxthreads = 512; // reasonable maximum for sanity check class TimingLog { @@ -347,7 +348,7 @@ attribute(string_view name, TypeDesc type, const void* val) } // Things below here need to buarded by the attrib_mutex - spin_lock lock(attrib_mutex); + std::lock_guard lock(attrib_mutex); if (name == "read_chunk" && type == TypeInt) { oiio_read_chunk = *(const int*)val; return true; @@ -372,6 +373,10 @@ attribute(string_view name, TypeDesc type, const void* val) jpeg_com_attributes = *(const int*)val; return true; } + if (name == "png:linear_premult" && type == TypeInt) { + png_linear_premult = *(const int*)val; + return true; + } if (name == "tiff:half" && type == TypeInt) { tiff_half = *(const int*)val; return true; @@ -460,7 +465,7 @@ getattribute(string_view name, TypeDesc type, void* val) } // Things below here need to buarded by the attrib_mutex - spin_lock lock(attrib_mutex); + std::lock_guard lock(attrib_mutex); if (name == "read_chunk" && type == TypeInt) { *(int*)val = oiio_read_chunk; return true; @@ -551,6 +556,10 @@ getattribute(string_view name, TypeDesc type, void* val) *(int*)val = jpeg_com_attributes; return true; } + if (name == "png:linear_premult" && type == TypeInt) { + *(int*)val = png_linear_premult; + return true; + } if (name == "tiff:half" && type == TypeInt) { *(int*)val = tiff_half; return true; diff --git a/src/png.imageio/pnginput.cpp b/src/png.imageio/pnginput.cpp index 748f22cb8c..d61f908ce6 100644 --- a/src/png.imageio/pnginput.cpp +++ b/src/png.imageio/pnginput.cpp @@ -45,6 +45,7 @@ class PNGInput final : public ImageInput { Imath::Color3f m_bg; ///< Background color int m_next_scanline; bool m_keep_unassociated_alpha; ///< Do not convert unassociated alpha + bool m_linear_premult; ///< Do premult for sRGB images in linear bool m_srgb = false; ///< It's an sRGB image (not gamma) bool m_err = false; float m_gamma = 1.0f; @@ -60,9 +61,10 @@ class PNGInput final : public ImageInput { m_buf.clear(); m_next_scanline = 0; m_keep_unassociated_alpha = false; - m_srgb = false; - m_err = false; - m_gamma = 1.0; + m_linear_premult = OIIO::get_int_attribute("png:linear_premult"); + m_srgb = false; + m_err = false; + m_gamma = 1.0; m_config.reset(); ioproxy_clear(); } @@ -88,8 +90,8 @@ class PNGInput final : public ImageInput { } template - static void associateAlpha(T* data, int size, int channels, - int alpha_channel, bool srgb, float gamma); + void associateAlpha(T* data, int size, int channels, int alpha_channel, + bool srgb, float gamma); }; @@ -189,6 +191,9 @@ PNGInput::open(const std::string& name, ImageSpec& newspec, // Check 'config' for any special requests if (config.get_int_attribute("oiio:UnassociatedAlpha", 0) == 1) m_keep_unassociated_alpha = true; + m_linear_premult = config.get_int_attribute("png:linear_premult", + OIIO::get_int_attribute( + "png:linear_premult")); ioproxy_retrieve_from_config(config); m_config.reset(new ImageSpec(config)); // save config spec return open(name, newspec); @@ -229,7 +234,8 @@ PNGInput::associateAlpha(T* data, int size, int channels, int alpha_channel, { // We need to transform to linear space, associate the alpha, and then // transform back. - if (srgb) { + if (srgb && m_linear_premult) { + // sRGB with request to do premult in linear space for (int x = 0; x < size; ++x, data += channels) { DataArrayProxy val(data); float alpha = val[alpha_channel]; @@ -242,25 +248,29 @@ PNGInput::associateAlpha(T* data, int size, int channels, int alpha_channel, } } } - } else if (gamma == 1.0f) { + } else if (gamma != 1.0f && m_linear_premult) { + // Gamma correction with request to do premult in linear space + float inv_gamma = 1.0f / gamma; for (int x = 0; x < size; ++x, data += channels) { DataArrayProxy val(data); float alpha = val[alpha_channel]; if (alpha != 1.0f) { for (int c = 0; c < channels; c++) if (c != alpha_channel) - data[c] = data[c] * alpha; + val[c] = powf((powf(val[c], gamma)) * alpha, inv_gamma); } } - } else { // With gamma correction - float inv_gamma = 1.0f / gamma; + } else { + // Do the premult directly on the values. This is correct for the + // "gamma=1" case, and is also commonly what is needed for many sRGB + // images (even though it's technically wrong in that case). for (int x = 0; x < size; ++x, data += channels) { DataArrayProxy val(data); float alpha = val[alpha_channel]; if (alpha != 1.0f) { for (int c = 0; c < channels; c++) if (c != alpha_channel) - val[c] = powf((powf(val[c], gamma)) * alpha, inv_gamma); + val[c] = val[c] * alpha; } } } diff --git a/src/png.imageio/pngoutput.cpp b/src/png.imageio/pngoutput.cpp index 26e98362f9..264da8864b 100644 --- a/src/png.imageio/pngoutput.cpp +++ b/src/png.imageio/pngoutput.cpp @@ -47,6 +47,7 @@ class PNGOutput final : public ImageOutput { int m_color_type; ///< PNG color model type bool m_convert_alpha; ///< Do we deassociate alpha? bool m_need_swap; ///< Do we need to swap bytes? + bool m_linear_premult; ///< Do premult for sRGB images in linear bool m_srgb = false; ///< It's an sRGB image (not gamma) float m_gamma = 1.0f; ///< Gamma to use for alpha conversion std::vector m_scratch; @@ -57,13 +58,14 @@ class PNGOutput final : public ImageOutput { // Initialize private members to pre-opened state void init(void) { - m_png = NULL; - m_info = NULL; - m_convert_alpha = true; - m_need_swap = false; - m_srgb = false; - m_err = false; - m_gamma = 1.0; + m_png = NULL; + m_info = NULL; + m_convert_alpha = true; + m_need_swap = false; + m_linear_premult = false; + m_srgb = false; + m_err = false; + m_gamma = 1.0; m_pngtext.clear(); ioproxy_clear(); } @@ -187,6 +189,10 @@ PNGOutput::open(const std::string& name, const ImageSpec& userspec, m_need_swap = (m_spec.format == TypeDesc::UINT16 && littleendian()); + m_linear_premult = m_spec.get_int_attribute("png:linear_premult", + OIIO::get_int_attribute( + "png:linear_premult")); + png_set_filter(m_png, 0, spec().get_int_attribute("png:filter", PNG_NO_FILTERS)); // https://www.w3.org/TR/PNG-Encoders.html#E.Filter-selection @@ -277,7 +283,8 @@ void PNGOutput::deassociateAlpha(T* data, size_t npixels, int channels, int alpha_channel, bool srgb, float gamma) { - if (srgb) { + if (srgb && m_linear_premult) { + // sRGB with request to do unpremult in linear space for (size_t x = 0; x < npixels; ++x, data += channels) { DataArrayProxy val(data); float alpha = val[alpha_channel]; @@ -290,27 +297,31 @@ PNGOutput::deassociateAlpha(T* data, size_t npixels, int channels, } } } - } else if (gamma == 1) { + } else if (gamma != 1.0f && m_linear_premult) { + // Gamma correction with request to do unpremult in linear space for (size_t x = 0; x < npixels; ++x, data += channels) { DataArrayProxy val(data); float alpha = val[alpha_channel]; if (alpha != 0.0f && alpha != 1.0f) { + // See associateAlpha() for an explanation. + float alpha_deassociate = pow(1.0f / val[alpha_channel], gamma); for (int c = 0; c < channels; c++) { if (c != alpha_channel) - val[c] = data[c] / alpha; + val[c] = val[c] * alpha_deassociate; } } } } else { + // Do the unpremult directly on the values. This is correct for the + // "gamma=1" case, and is also commonly what is needed for many sRGB + // images (even though it's technically wrong in that case). for (size_t x = 0; x < npixels; ++x, data += channels) { DataArrayProxy val(data); float alpha = val[alpha_channel]; if (alpha != 0.0f && alpha != 1.0f) { - // See associateAlpha() for an explanation. - float alpha_deassociate = pow(1.0f / val[alpha_channel], gamma); for (int c = 0; c < channels; c++) { if (c != alpha_channel) - val[c] = val[c] * alpha_deassociate; + val[c] = data[c] / alpha; } } } diff --git a/testsuite/png/ref/out-libpng15.txt b/testsuite/png/ref/out-libpng15.txt index 0e54010444..fa0337a2a5 100644 --- a/testsuite/png/ref/out-libpng15.txt +++ b/testsuite/png/ref/out-libpng15.txt @@ -27,6 +27,7 @@ exif.png : 64 x 64, 3 channel, uint8 png SHA-1: 7CB41FEA50720B48BE0C145E1473982B23E9AB77 channel list: R, G, B oiio:ColorSpace: "sRGB" +alphagamma: 1 x 1, 4 channel, float png channel list: R, G, B, A ResolutionUnit: "inch" @@ -46,6 +47,43 @@ exif.png : 64 x 64, 3 channel, uint8 png Constant: Yes Constant Color: 186.00 186.00 186.00 127.00 (of 255) Monochrome: No +gimp_gradient: + 256 x 256, 4 channel, float png + channel list: R, G, B, A + Comment: "Created with GIMP" + DateTime: "2025:01:05 04:44:59" + ICCProfile: 0, 0, 2, 160, 108, 99, 109, 115, 4, 64, 0, 0, 109, 110, 116, 114, ... [672 x uint8] + ResolutionUnit: "inch" + XResolution: 300 + YResolution: 300 + ICCProfile:attributes: "Reflective, Glossy, Positive, Color" + ICCProfile:cmm_type: 1818455411 + ICCProfile:color_space: "RGB" + ICCProfile:copyright: "Public Domain" + ICCProfile:creation_date: "2025:01:05 04:34:16" + ICCProfile:creator_signature: "6c636d73" + ICCProfile:device_class: "Display device profile" + ICCProfile:device_manufacturer_description: "GIMP" + ICCProfile:device_model_description: "sRGB" + ICCProfile:flags: "Not Embedded, Independent" + ICCProfile:manufacturer: "0" + ICCProfile:model: "0" + ICCProfile:platform_signature: "Apple Computer, Inc." + ICCProfile:profile_connection_space: "XYZ" + ICCProfile:profile_description: "GIMP built-in sRGB" + ICCProfile:profile_size: 672 + ICCProfile:profile_version: "4.4.0" + ICCProfile:rendering_intent: "Perceptual" + oiio:ColorSpace: "sRGB" + Stats Min: 0 0 0 0 (of 255) + Stats Max: 255 255 0 255 (of 255) + Stats Avg: 142.37 105.72 0.00 154.72 (of 255) + Stats StdDev: 79.19 98.91 0.00 87.39 (of 255) + Stats NanCount: 0 0 0 0 + Stats InfCount: 0 0 0 0 + Stats FiniteCount: 65536 65536 65536 65536 + Constant: No + Monochrome: No smallalpha.png : 1 x 1, 4 channel, uint8 png Pixel (0, 0): 240 108 119 1 (0.94117653 0.42352945 0.4666667 0.003921569) Comparing "test16.png" and "ref/test16.png" diff --git a/testsuite/png/ref/out.txt b/testsuite/png/ref/out.txt index 39f2cf8310..7345811808 100644 --- a/testsuite/png/ref/out.txt +++ b/testsuite/png/ref/out.txt @@ -31,6 +31,7 @@ exif.png : 64 x 64, 3 channel, uint8 png Exif:FocalLength: 45.7 (45.7 mm) Exif:WhiteBalance: 0 (auto) oiio:ColorSpace: "sRGB" +alphagamma: 1 x 1, 4 channel, float png channel list: R, G, B, A ResolutionUnit: "inch" @@ -50,6 +51,43 @@ exif.png : 64 x 64, 3 channel, uint8 png Constant: Yes Constant Color: 186.00 186.00 186.00 127.00 (of 255) Monochrome: No +gimp_gradient: + 256 x 256, 4 channel, float png + channel list: R, G, B, A + Comment: "Created with GIMP" + DateTime: "2025:01:05 04:44:59" + ICCProfile: 0, 0, 2, 160, 108, 99, 109, 115, 4, 64, 0, 0, 109, 110, 116, 114, ... [672 x uint8] + ResolutionUnit: "inch" + XResolution: 300 + YResolution: 300 + ICCProfile:attributes: "Reflective, Glossy, Positive, Color" + ICCProfile:cmm_type: 1818455411 + ICCProfile:color_space: "RGB" + ICCProfile:copyright: "Public Domain" + ICCProfile:creation_date: "2025:01:05 04:34:16" + ICCProfile:creator_signature: "6c636d73" + ICCProfile:device_class: "Display device profile" + ICCProfile:device_manufacturer_description: "GIMP" + ICCProfile:device_model_description: "sRGB" + ICCProfile:flags: "Not Embedded, Independent" + ICCProfile:manufacturer: "0" + ICCProfile:model: "0" + ICCProfile:platform_signature: "Apple Computer, Inc." + ICCProfile:profile_connection_space: "XYZ" + ICCProfile:profile_description: "GIMP built-in sRGB" + ICCProfile:profile_size: 672 + ICCProfile:profile_version: "4.4.0" + ICCProfile:rendering_intent: "Perceptual" + oiio:ColorSpace: "sRGB" + Stats Min: 0 0 0 0 (of 255) + Stats Max: 255 255 0 255 (of 255) + Stats Avg: 142.37 105.72 0.00 154.72 (of 255) + Stats StdDev: 79.19 98.91 0.00 87.39 (of 255) + Stats NanCount: 0 0 0 0 + Stats InfCount: 0 0 0 0 + Stats FiniteCount: 65536 65536 65536 65536 + Constant: No + Monochrome: No smallalpha.png : 1 x 1, 4 channel, uint8 png Pixel (0, 0): 240 108 119 1 (0.94117653 0.42352945 0.4666667 0.003921569) Comparing "test16.png" and "ref/test16.png" diff --git a/testsuite/png/run.py b/testsuite/png/run.py index 33505f9bed..861e7cc38b 100755 --- a/testsuite/png/run.py +++ b/testsuite/png/run.py @@ -22,7 +22,10 @@ command += oiiotool ("--pattern fill:topleft=1,0,0,1:topright=0,1,0,1:bottomleft=0,0,1,1:bottomright=1,1,1,1 16x16 4 -d uint16 -o test16.png") # regression test for wrong gamma correction for partial alpha -command += oiiotool ("src/alphagamma.png --printinfo:stats=1") +command += oiiotool ("-echo alphagamma: " + + "--oiioattrib png:linear_premult 1 " + + "src/alphagamma.png --printinfo:stats=1") +command += oiiotool ("-echo gimp_gradient: src/gimp_gradient.png --printinfo:stats=1") # Test high quality alpha deassociation using alpha value close to zero. # This example is inspired by Yafes on the Slack. diff --git a/testsuite/png/src/gimp_gradient.png b/testsuite/png/src/gimp_gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..d61da8df2782323edf6c246744ed4040ee74793c GIT binary patch literal 24653 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 U+P$1t8vp