Skip to content

Commit 3bbc865

Browse files
authored
Merge pull request #7872 from radarhere/webp_alpha_quality
2 parents 98067db + 64c8c27 commit 3bbc865

File tree

5 files changed

+48
-4
lines changed

5 files changed

+48
-4
lines changed

Tests/test_file_webp_alpha.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,15 @@ def test_write_unsupported_mode_PA(tmp_path: Path) -> None:
151151
target = im.convert("RGBA")
152152

153153
assert_image_similar(image, target, 25.0)
154+
155+
156+
def test_alpha_quality(tmp_path: Path) -> None:
157+
with Image.open("Tests/images/transparent.png") as im:
158+
out = str(tmp_path / "temp.webp")
159+
im.save(out)
160+
161+
out_quality = str(tmp_path / "quality.webp")
162+
im.save(out_quality, alpha_quality=50)
163+
with Image.open(out) as reloaded:
164+
with Image.open(out_quality) as reloaded_quality:
165+
assert reloaded.tobytes() != reloaded_quality.tobytes()

Tests/test_file_webp_animated.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,21 @@ def test_seek_errors() -> None:
188188

189189
with pytest.raises(EOFError):
190190
im.seek(42)
191+
192+
193+
def test_alpha_quality(tmp_path: Path) -> None:
194+
with Image.open("Tests/images/transparent.png") as im:
195+
first_frame = Image.new("L", im.size)
196+
197+
out = str(tmp_path / "temp.webp")
198+
first_frame.save(out, save_all=True, append_images=[im])
199+
200+
out_quality = str(tmp_path / "quality.webp")
201+
first_frame.save(
202+
out_quality, save_all=True, append_images=[im], alpha_quality=50
203+
)
204+
with Image.open(out) as reloaded:
205+
reloaded.seek(1)
206+
with Image.open(out_quality) as reloaded_quality:
207+
reloaded_quality.seek(1)
208+
assert reloaded.tobytes() != reloaded_quality.tobytes()

docs/handbook/image-file-formats.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1234,11 +1234,15 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
12341234
If present and true, instructs the WebP writer to use lossless compression.
12351235

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

1242+
**alpha_quality**
1243+
Integer, 0-100, defaults to 100. For lossy compression only. 0 gives the
1244+
smallest size and 100 is lossless.
1245+
12421246
**method**
12431247
Quality/speed trade-off (0=fast, 6=slower-better). Defaults to 4.
12441248

src/PIL/WebPImagePlugin.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ def _save_all(im, fp, filename):
217217
verbose = False
218218
lossless = im.encoderinfo.get("lossless", False)
219219
quality = im.encoderinfo.get("quality", 80)
220+
alpha_quality = im.encoderinfo.get("alpha_quality", 100)
220221
method = im.encoderinfo.get("method", 0)
221222
icc_profile = im.encoderinfo.get("icc_profile") or ""
222223
exif = im.encoderinfo.get("exif", "")
@@ -296,6 +297,7 @@ def _save_all(im, fp, filename):
296297
rawmode,
297298
lossless,
298299
quality,
300+
alpha_quality,
299301
method,
300302
)
301303

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

312314
# Force encoder to flush frames
313-
enc.add(None, round(timestamp), 0, 0, "", lossless, quality, 0)
315+
enc.add(None, round(timestamp), 0, 0, "", lossless, quality, alpha_quality, 0)
314316

315317
# Get the final output from the encoder
316318
data = enc.assemble(icc_profile, exif, xmp)
@@ -324,6 +326,7 @@ def _save_all(im, fp, filename):
324326
def _save(im, fp, filename):
325327
lossless = im.encoderinfo.get("lossless", False)
326328
quality = im.encoderinfo.get("quality", 80)
329+
alpha_quality = im.encoderinfo.get("alpha_quality", 100)
327330
icc_profile = im.encoderinfo.get("icc_profile") or ""
328331
exif = im.encoderinfo.get("exif", b"")
329332
if isinstance(exif, Image.Exif):
@@ -343,6 +346,7 @@ def _save(im, fp, filename):
343346
im.size[1],
344347
lossless,
345348
float(quality),
349+
float(alpha_quality),
346350
im.mode,
347351
icc_profile,
348352
method,

src/_webp.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ _anim_encoder_add(PyObject *self, PyObject *args) {
195195
char *mode;
196196
int lossless;
197197
float quality_factor;
198+
float alpha_quality_factor;
198199
int method;
199200
WebPConfig config;
200201
WebPAnimEncoderObject *encp = (WebPAnimEncoderObject *)self;
@@ -203,7 +204,7 @@ _anim_encoder_add(PyObject *self, PyObject *args) {
203204

204205
if (!PyArg_ParseTuple(
205206
args,
206-
"z#iiisifi",
207+
"z#iiisiffi",
207208
(char **)&rgb,
208209
&size,
209210
&timestamp,
@@ -212,6 +213,7 @@ _anim_encoder_add(PyObject *self, PyObject *args) {
212213
&mode,
213214
&lossless,
214215
&quality_factor,
216+
&alpha_quality_factor,
215217
&method)) {
216218
return NULL;
217219
}
@@ -229,6 +231,7 @@ _anim_encoder_add(PyObject *self, PyObject *args) {
229231
}
230232
config.lossless = lossless;
231233
config.quality = quality_factor;
234+
config.alpha_quality = alpha_quality_factor;
232235
config.method = method;
233236

234237
// Validate the config
@@ -578,6 +581,7 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
578581
int height;
579582
int lossless;
580583
float quality_factor;
584+
float alpha_quality_factor;
581585
int method;
582586
int exact;
583587
uint8_t *rgb;
@@ -601,13 +605,14 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
601605

602606
if (!PyArg_ParseTuple(
603607
args,
604-
"y#iiifss#iis#s#",
608+
"y#iiiffss#iis#s#",
605609
(char **)&rgb,
606610
&size,
607611
&width,
608612
&height,
609613
&lossless,
610614
&quality_factor,
615+
&alpha_quality_factor,
611616
&mode,
612617
&icc_bytes,
613618
&icc_size,
@@ -637,6 +642,7 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
637642
}
638643
config.lossless = lossless;
639644
config.quality = quality_factor;
645+
config.alpha_quality = alpha_quality_factor;
640646
config.method = method;
641647
#if WEBP_ENCODER_ABI_VERSION >= 0x0209
642648
// the "exact" flag is only available in libwebp 0.5.0 and later

0 commit comments

Comments
 (0)