Skip to content

Commit 73be2d8

Browse files
authored
feat(webp): Allow finer grained control over WEBP compression settings (#4772)
WEBP uses the `WebPConfig::factor` setting to also control how hard it tries to compress when lossless compression is used. It also uses another `WebPConfig::method` setting to further tune both lossless and lossy schemes. [1] Expose both of these settings so client applications using OIIO can adjust these values as they see fit. This PR makes 4 changes: - Adds support for using a `lossless:factor` value for the `compression` attribute. The 'factor' value controls the effort that WEBP uses during the compression process. - Adds support for using a `webp:method` int attribute. The value of this attribute controls the `WebPConfig::method` setting. Note: this is different than the compression settings but will also affect speed and quality in various ways. - Sets `WebPPicture::use_argb` to `true` when using lossless compression (per the WEBP documentation[2]) - Changes default lossless "quality" from 100 to 70. This results in much faster image output at a very minor increase in file size (e.g. a 2048x2048 test image goes from 1300ms/364kb to 800ms/371kb after this change) Before - just 2 basic forms were possible: ``` Client uses "compression=lossless" - OIIO uses lossless=true, method=6, quality=100, use_argb=false Client uses "compression=webp:90" - OIIO uses lossless=false, method=6, quality=90, use_argb=false ``` After - several additional variations are possible: ``` Client uses "compression=lossless" - OIIO uses lossless=true, method=6, quality=70, use_argb=true Client uses "compression=lossless:100" and "webp:method=4" - OIIO uses lossless=true, method=4, quality=100, use_argb=true Client uses "compression=webp:90" - OIIO uses lossless=false, method=6, quality=90, use_argb=false Client uses "compression=webp:90" and "webp:method=4" - OIIO uses lossless=false, method=4, quality=90, use_argb=false ``` [1] https://developers.google.com/speed/webp/docs/api#advanced_encoding_api [2] See notes around `use_argb` from the above link and see also the internal Encode helper function which keeps lossless and use_argb in sync: https://github.com/webmproject/libwebp/blob/main/src/enc/picture_enc.c#L241 Signed-off-by: Jesse Yurkovich <[email protected]>
1 parent f698ae3 commit 73be2d8

File tree

2 files changed

+46
-20
lines changed

2 files changed

+46
-20
lines changed

src/doc/builtinplugins.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3099,6 +3099,19 @@ control aspects of the writing itself:
30993099
unassociated form (non-premultiplied colors) and should stay that way
31003100
for output rather than being assumed to be associated and get automatic
31013101
un-association to store in the file.
3102+
* - ``Compression``
3103+
- string
3104+
- If supplied, can be either ``"webp:quality"`` or ``"lossless:quality"``
3105+
where quality can be an integer between 0 and 100, and where using "webp"
3106+
indicates a request for lossy compression. For lossy, 0 gives the smallest
3107+
size and 100 the largest. For lossless, this parameter is the amount of effort
3108+
put into the compression: 0 is the fastest but gives larger files compared to
3109+
the slowest, but best, 100. The default, if quality is not specified, is
3110+
100 for lossy and 70 for lossless.
3111+
* - ``webp:method``
3112+
- int
3113+
- A general quality/speed trade-off (0=fast, 6=slower-better) for both
3114+
lossy and lossless image encoding. The default is 6.
31023115

31033116
**Custom I/O Overrides**
31043117

src/webp.imageio/webpoutput.cpp

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -80,37 +80,50 @@ WebpOutput::open(const std::string& name, const ImageSpec& spec, OpenMode mode)
8080
if (!ioproxy_use_or_open(name))
8181
return false;
8282

83-
if (!WebPPictureInit(&m_webp_picture)) {
84-
errorfmt("Couldn't initialize WebPPicture\n");
83+
constexpr int default_lossy_quality = 100;
84+
constexpr int default_lossless_effort = 70;
85+
86+
// Support both 'compression=webp:value' and 'compression=lossless:value'
87+
// The 'webp' form indicates that lossy compression is requested.
88+
bool is_lossless = false;
89+
int quality = default_lossy_quality;
90+
auto comp_qual = m_spec.decode_compression_metadata("webp",
91+
default_lossy_quality);
92+
if (Strutil::iequals(comp_qual.first, "webp")) {
93+
quality = OIIO::clamp(comp_qual.second, 0, 100);
94+
} else {
95+
comp_qual = m_spec.decode_compression_metadata("lossless",
96+
default_lossless_effort);
97+
if (Strutil::iequals(comp_qual.first, "lossless")) {
98+
is_lossless = true;
99+
quality = OIIO::clamp(comp_qual.second, 0, 100);
100+
}
101+
}
102+
103+
if (!WebPConfigPreset(&m_webp_config, WEBP_PRESET_DEFAULT, quality)) {
104+
errorfmt("Couldn't initialize WebPConfig\n");
85105
close();
86106
return false;
87107
}
88108

89-
m_webp_picture.width = m_spec.width;
90-
m_webp_picture.height = m_spec.height;
91-
m_webp_picture.writer = WebpImageWriter;
92-
m_webp_picture.custom_ptr = (void*)ioproxy();
93-
94-
if (!WebPConfigInit(&m_webp_config)) {
109+
if (!WebPPictureInit(&m_webp_picture)) {
95110
errorfmt("Couldn't initialize WebPPicture\n");
96111
close();
97112
return false;
98113
}
99114

100-
auto compqual = m_spec.decode_compression_metadata("webp", 100);
101-
if (Strutil::iequals(compqual.first, "webp")) {
102-
m_webp_config.method = 6;
103-
m_webp_config.quality = OIIO::clamp(compqual.second, 1, 100);
104-
} else {
105-
// If compression name wasn't "webp", don't trust the quality
106-
// metric, just use the default.
107-
m_webp_config.method = 6;
108-
m_webp_config.quality = 100;
109-
}
115+
// Quality/speed trade-off (0=fast, 6=slower-better)
116+
const int method = m_spec.get_int_attribute("webp:method", 6);
117+
m_webp_config.method = OIIO::clamp(method, 0, 6);
110118

111119
// Lossless encoding (0=lossy(default), 1=lossless).
112-
m_webp_config.lossless
113-
= (m_spec.get_string_attribute("compression", "lossy") == "lossless");
120+
m_webp_config.lossless = int(is_lossless);
121+
122+
m_webp_picture.use_argb = m_webp_config.lossless;
123+
m_webp_picture.width = m_spec.width;
124+
m_webp_picture.height = m_spec.height;
125+
m_webp_picture.writer = WebpImageWriter;
126+
m_webp_picture.custom_ptr = (void*)ioproxy();
114127

115128
// forcing UINT8 format
116129
m_spec.set_format(TypeDesc::UINT8);

0 commit comments

Comments
 (0)