Skip to content

Commit

Permalink
Merge pull request #7872 from radarhere/webp_alpha_quality
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk authored Mar 21, 2024
2 parents 98067db + 64c8c27 commit 3bbc865
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 4 deletions.
12 changes: 12 additions & 0 deletions Tests/test_file_webp_alpha.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,15 @@ def test_write_unsupported_mode_PA(tmp_path: Path) -> None:
target = im.convert("RGBA")

assert_image_similar(image, target, 25.0)


def test_alpha_quality(tmp_path: Path) -> None:
with Image.open("Tests/images/transparent.png") as im:
out = str(tmp_path / "temp.webp")
im.save(out)

out_quality = str(tmp_path / "quality.webp")
im.save(out_quality, alpha_quality=50)
with Image.open(out) as reloaded:
with Image.open(out_quality) as reloaded_quality:
assert reloaded.tobytes() != reloaded_quality.tobytes()
18 changes: 18 additions & 0 deletions Tests/test_file_webp_animated.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,21 @@ def test_seek_errors() -> None:

with pytest.raises(EOFError):
im.seek(42)


def test_alpha_quality(tmp_path: Path) -> None:
with Image.open("Tests/images/transparent.png") as im:
first_frame = Image.new("L", im.size)

out = str(tmp_path / "temp.webp")
first_frame.save(out, save_all=True, append_images=[im])

out_quality = str(tmp_path / "quality.webp")
first_frame.save(
out_quality, save_all=True, append_images=[im], alpha_quality=50
)
with Image.open(out) as reloaded:
reloaded.seek(1)
with Image.open(out_quality) as reloaded_quality:
reloaded_quality.seek(1)
assert reloaded.tobytes() != reloaded_quality.tobytes()
6 changes: 5 additions & 1 deletion docs/handbook/image-file-formats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1234,11 +1234,15 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
If present and true, instructs the WebP writer to use lossless compression.

**quality**
Integer, 0-100, Defaults to 80. For lossy, 0 gives the smallest
Integer, 0-100, defaults to 80. For lossy, 0 gives the smallest
size and 100 the largest. For lossless, this parameter is the amount
of effort put into the compression: 0 is the fastest, but gives larger
files compared to the slowest, but best, 100.

**alpha_quality**
Integer, 0-100, defaults to 100. For lossy compression only. 0 gives the
smallest size and 100 is lossless.

**method**
Quality/speed trade-off (0=fast, 6=slower-better). Defaults to 4.

Expand Down
6 changes: 5 additions & 1 deletion src/PIL/WebPImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ def _save_all(im, fp, filename):
verbose = False
lossless = im.encoderinfo.get("lossless", False)
quality = im.encoderinfo.get("quality", 80)
alpha_quality = im.encoderinfo.get("alpha_quality", 100)
method = im.encoderinfo.get("method", 0)
icc_profile = im.encoderinfo.get("icc_profile") or ""
exif = im.encoderinfo.get("exif", "")
Expand Down Expand Up @@ -296,6 +297,7 @@ def _save_all(im, fp, filename):
rawmode,
lossless,
quality,
alpha_quality,
method,
)

Expand All @@ -310,7 +312,7 @@ def _save_all(im, fp, filename):
im.seek(cur_idx)

# Force encoder to flush frames
enc.add(None, round(timestamp), 0, 0, "", lossless, quality, 0)
enc.add(None, round(timestamp), 0, 0, "", lossless, quality, alpha_quality, 0)

# Get the final output from the encoder
data = enc.assemble(icc_profile, exif, xmp)
Expand All @@ -324,6 +326,7 @@ def _save_all(im, fp, filename):
def _save(im, fp, filename):
lossless = im.encoderinfo.get("lossless", False)
quality = im.encoderinfo.get("quality", 80)
alpha_quality = im.encoderinfo.get("alpha_quality", 100)
icc_profile = im.encoderinfo.get("icc_profile") or ""
exif = im.encoderinfo.get("exif", b"")
if isinstance(exif, Image.Exif):
Expand All @@ -343,6 +346,7 @@ def _save(im, fp, filename):
im.size[1],
lossless,
float(quality),
float(alpha_quality),
im.mode,
icc_profile,
method,
Expand Down
10 changes: 8 additions & 2 deletions src/_webp.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ _anim_encoder_add(PyObject *self, PyObject *args) {
char *mode;
int lossless;
float quality_factor;
float alpha_quality_factor;
int method;
WebPConfig config;
WebPAnimEncoderObject *encp = (WebPAnimEncoderObject *)self;
Expand All @@ -203,7 +204,7 @@ _anim_encoder_add(PyObject *self, PyObject *args) {

if (!PyArg_ParseTuple(
args,
"z#iiisifi",
"z#iiisiffi",
(char **)&rgb,
&size,
&timestamp,
Expand All @@ -212,6 +213,7 @@ _anim_encoder_add(PyObject *self, PyObject *args) {
&mode,
&lossless,
&quality_factor,
&alpha_quality_factor,
&method)) {
return NULL;
}
Expand All @@ -229,6 +231,7 @@ _anim_encoder_add(PyObject *self, PyObject *args) {
}
config.lossless = lossless;
config.quality = quality_factor;
config.alpha_quality = alpha_quality_factor;
config.method = method;

// Validate the config
Expand Down Expand Up @@ -578,6 +581,7 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
int height;
int lossless;
float quality_factor;
float alpha_quality_factor;
int method;
int exact;
uint8_t *rgb;
Expand All @@ -601,13 +605,14 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {

if (!PyArg_ParseTuple(
args,
"y#iiifss#iis#s#",
"y#iiiffss#iis#s#",
(char **)&rgb,
&size,
&width,
&height,
&lossless,
&quality_factor,
&alpha_quality_factor,
&mode,
&icc_bytes,
&icc_size,
Expand Down Expand Up @@ -637,6 +642,7 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
}
config.lossless = lossless;
config.quality = quality_factor;
config.alpha_quality = alpha_quality_factor;
config.method = method;
#if WEBP_ENCODER_ABI_VERSION >= 0x0209
// the "exact" flag is only available in libwebp 0.5.0 and later
Expand Down

0 comments on commit 3bbc865

Please sign in to comment.