Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/doc/builtinplugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3066,6 +3066,11 @@ 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.
* - ``oiio:UnassociatedAlpha``
- int
- If nonzero, will leave alpha unassociated (versus the default of
premultiplying color channels by alpha if the alpha channel is
unassociated).

**Configuration settings for WebP output**

Expand All @@ -3088,6 +3093,12 @@ control aspects of the writing itself:
- ptr
- Pointer to a ``Filesystem::IOProxy`` that will handle the I/O, for
example by writing to a memory buffer.
* - ``oiio:UnassociatedAlpha``
- int
- If nonzero, indicates that the data being passed is already in
unassociated form (non-premultiplied colors) and should stay that way
for output rather than being assumed to be associated and get automatic
un-association to store in the file.

**Custom I/O Overrides**

Expand Down
38 changes: 25 additions & 13 deletions src/webp.imageio/webpinput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ class WebpInput final : public ImageInput {
int m_frame_count = 1;
WebPDemuxer* m_demux = nullptr;
WebPIterator m_iter;
int m_subimage = -1; // Subimage we're pointed to
int m_subimage_read = -1; // Subimage stored in decoded_image
int m_subimage = -1; // Subimage we're pointed to
int m_subimage_read = -1; // Subimage stored in decoded_image
bool m_keep_unassociated_alpha = false; // Do not convert unassociated alpha

void init(void)
{
Expand Down Expand Up @@ -204,6 +205,9 @@ WebpInput::open(const std::string& name, ImageSpec& spec,
// Make space for the decoded image
m_decoded_image.reset(new uint8_t[m_spec.image_bytes()]);

if (config.get_int_attribute("oiio:UnassociatedAlpha", 0) == 1)
m_keep_unassociated_alpha = true;

seek_subimage(0, 0);
spec = m_spec;
return true;
Expand Down Expand Up @@ -306,12 +310,15 @@ WebpInput::read_current_subimage()
m_decoded_image.get() + offset,
m_spec.image_bytes() - offset,
m_spec.scanline_bytes());
// WebP requires unassociated alpha, and it's sRGB.
// Handle this all by wrapping an IB around it.
ImageBuf fullbuf(m_spec,
span<std::byte>((std::byte*)m_decoded_image.get(),
m_spec.image_bytes()));
ImageBufAlgo::premult(fullbuf, fullbuf);

// WebP is unassociated alpha and sRGB.
// Convert to the OIIO-native associated form if required.
if (!m_keep_unassociated_alpha) {
ImageBuf fullbuf(
m_spec, span<std::byte>((std::byte*)m_decoded_image.get(),
m_spec.image_bytes()));
ImageBufAlgo::premult(fullbuf, fullbuf);
}
}
} else {
// This subimage writes *atop* the prior image, we must composite
Expand All @@ -328,10 +335,14 @@ WebpInput::read_current_subimage()
(uint8_t*)fragbuf.localpixels(),
fragspec.image_bytes(),
fragspec.scanline_bytes());
// WebP requires unassociated alpha, and it's sRGB.
// Handle this all by wrapping an IB around it.
ImageBufAlgo::premult(fragbuf, fragbuf);
ImageBufAlgo::over(fullbuf, fragbuf, fullbuf);


// WebP is unassociated alpha and sRGB.
// Convert to the OIIO-native associated form if required.
if (!m_keep_unassociated_alpha) {
ImageBufAlgo::premult(fragbuf, fragbuf);
ImageBufAlgo::over(fullbuf, fragbuf, fullbuf);
}
}

if (!okptr) {
Expand Down Expand Up @@ -371,7 +382,8 @@ WebpInput::close()
}
m_decoded_image.reset();
m_encoded_image.reset();
m_subimage = -1;
m_subimage = -1;
m_keep_unassociated_alpha = false;
init();
return true;
}
Expand Down
26 changes: 17 additions & 9 deletions src/webp.imageio/webpoutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class WebpOutput final : public ImageOutput {
std::string m_filename;
imagesize_t m_scanline_size;
unsigned int m_dither;
bool m_convert_alpha; // Do we deassociate alpha?
std::vector<uint8_t> m_uncompressed_image;

void init()
Expand Down Expand Up @@ -113,7 +114,9 @@ WebpOutput::open(const std::string& name, const ImageSpec& spec, OpenMode mode)

// forcing UINT8 format
m_spec.set_format(TypeDesc::UINT8);
m_dither = m_spec.get_int_attribute("oiio:dither", 0);
m_dither = m_spec.get_int_attribute("oiio:dither", 0);
m_convert_alpha = m_spec.alpha_channel != -1
&& !m_spec.get_int_attribute("oiio:UnassociatedAlpha", 0);

m_scanline_size = m_spec.scanline_bytes();
m_uncompressed_image.resize(m_spec.image_bytes(), 0);
Expand All @@ -136,20 +139,25 @@ WebpOutput::write_scanline(int y, int z, TypeDesc format, const void* data,

if (y == m_spec.height - 1) {
if (m_spec.nchannels == 4) {
// WebP requires unassociated alpha, and it's sRGB.
// Handle this all by wrapping an IB around it.
ImageSpec specwrap(m_spec.width, m_spec.height, 4, TypeUInt8);
ImageBuf bufwrap(specwrap, cspan<uint8_t>(m_uncompressed_image));
ROI rgbroi(0, m_spec.width, 0, m_spec.height, 0, 1, 0, 3);
ImageBufAlgo::pow(bufwrap, bufwrap, 2.2f, rgbroi);
ImageBufAlgo::unpremult(bufwrap, bufwrap);
ImageBufAlgo::pow(bufwrap, bufwrap, 1.0f / 2.2f, rgbroi);
if (m_convert_alpha) {
// WebP requires unassociated alpha, and it's sRGB.
// Handle this all by wrapping an IB around it.
ImageSpec specwrap(m_spec.width, m_spec.height, 4, TypeUInt8);
ImageBuf bufwrap(specwrap,
cspan<uint8_t>(m_uncompressed_image));
ROI rgbroi(0, m_spec.width, 0, m_spec.height, 0, 1, 0, 3);
ImageBufAlgo::pow(bufwrap, bufwrap, 2.2f, rgbroi);
ImageBufAlgo::unpremult(bufwrap, bufwrap);
ImageBufAlgo::pow(bufwrap, bufwrap, 1.0f / 2.2f, rgbroi);
}

WebPPictureImportRGBA(&m_webp_picture, m_uncompressed_image.data(),
m_scanline_size);
} else {
WebPPictureImportRGB(&m_webp_picture, m_uncompressed_image.data(),
m_scanline_size);
}

if (!WebPEncode(&m_webp_config, &m_webp_picture)) {
errorfmt("Failed to encode {} as WebP image", m_filename);
close();
Expand Down