Added alpha_quality argument when saving

This commit is contained in:
Andrew Murray 2024-03-13 18:55:26 +11:00
parent 9e3d1a7b05
commit 64c8c27271
5 changed files with 48 additions and 4 deletions

View File

@ -151,3 +151,15 @@ def test_write_unsupported_mode_PA(tmp_path: Path) -> None:
target = im.convert("RGBA") target = im.convert("RGBA")
assert_image_similar(image, target, 25.0) 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()

View File

@ -188,3 +188,21 @@ def test_seek_errors() -> None:
with pytest.raises(EOFError): with pytest.raises(EOFError):
im.seek(42) 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()

View File

@ -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. If present and true, instructs the WebP writer to use lossless compression.
**quality** **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 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 of effort put into the compression: 0 is the fastest, but gives larger
files compared to the slowest, but best, 100. 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** **method**
Quality/speed trade-off (0=fast, 6=slower-better). Defaults to 4. Quality/speed trade-off (0=fast, 6=slower-better). Defaults to 4.

View File

@ -217,6 +217,7 @@ def _save_all(im, fp, filename):
verbose = False verbose = False
lossless = im.encoderinfo.get("lossless", False) lossless = im.encoderinfo.get("lossless", False)
quality = im.encoderinfo.get("quality", 80) quality = im.encoderinfo.get("quality", 80)
alpha_quality = im.encoderinfo.get("alpha_quality", 100)
method = im.encoderinfo.get("method", 0) method = im.encoderinfo.get("method", 0)
icc_profile = im.encoderinfo.get("icc_profile") or "" icc_profile = im.encoderinfo.get("icc_profile") or ""
exif = im.encoderinfo.get("exif", "") exif = im.encoderinfo.get("exif", "")
@ -296,6 +297,7 @@ def _save_all(im, fp, filename):
rawmode, rawmode,
lossless, lossless,
quality, quality,
alpha_quality,
method, method,
) )
@ -310,7 +312,7 @@ def _save_all(im, fp, filename):
im.seek(cur_idx) im.seek(cur_idx)
# Force encoder to flush frames # 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 # Get the final output from the encoder
data = enc.assemble(icc_profile, exif, xmp) data = enc.assemble(icc_profile, exif, xmp)
@ -324,6 +326,7 @@ def _save_all(im, fp, filename):
def _save(im, fp, filename): def _save(im, fp, filename):
lossless = im.encoderinfo.get("lossless", False) lossless = im.encoderinfo.get("lossless", False)
quality = im.encoderinfo.get("quality", 80) quality = im.encoderinfo.get("quality", 80)
alpha_quality = im.encoderinfo.get("alpha_quality", 100)
icc_profile = im.encoderinfo.get("icc_profile") or "" icc_profile = im.encoderinfo.get("icc_profile") or ""
exif = im.encoderinfo.get("exif", b"") exif = im.encoderinfo.get("exif", b"")
if isinstance(exif, Image.Exif): if isinstance(exif, Image.Exif):
@ -343,6 +346,7 @@ def _save(im, fp, filename):
im.size[1], im.size[1],
lossless, lossless,
float(quality), float(quality),
float(alpha_quality),
im.mode, im.mode,
icc_profile, icc_profile,
method, method,

View File

@ -195,6 +195,7 @@ _anim_encoder_add(PyObject *self, PyObject *args) {
char *mode; char *mode;
int lossless; int lossless;
float quality_factor; float quality_factor;
float alpha_quality_factor;
int method; int method;
WebPConfig config; WebPConfig config;
WebPAnimEncoderObject *encp = (WebPAnimEncoderObject *)self; WebPAnimEncoderObject *encp = (WebPAnimEncoderObject *)self;
@ -203,7 +204,7 @@ _anim_encoder_add(PyObject *self, PyObject *args) {
if (!PyArg_ParseTuple( if (!PyArg_ParseTuple(
args, args,
"z#iiisifi", "z#iiisiffi",
(char **)&rgb, (char **)&rgb,
&size, &size,
&timestamp, &timestamp,
@ -212,6 +213,7 @@ _anim_encoder_add(PyObject *self, PyObject *args) {
&mode, &mode,
&lossless, &lossless,
&quality_factor, &quality_factor,
&alpha_quality_factor,
&method)) { &method)) {
return NULL; return NULL;
} }
@ -229,6 +231,7 @@ _anim_encoder_add(PyObject *self, PyObject *args) {
} }
config.lossless = lossless; config.lossless = lossless;
config.quality = quality_factor; config.quality = quality_factor;
config.alpha_quality = alpha_quality_factor;
config.method = method; config.method = method;
// Validate the config // Validate the config
@ -578,6 +581,7 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
int height; int height;
int lossless; int lossless;
float quality_factor; float quality_factor;
float alpha_quality_factor;
int method; int method;
int exact; int exact;
uint8_t *rgb; uint8_t *rgb;
@ -601,13 +605,14 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
if (!PyArg_ParseTuple( if (!PyArg_ParseTuple(
args, args,
"y#iiifss#iis#s#", "y#iiiffss#iis#s#",
(char **)&rgb, (char **)&rgb,
&size, &size,
&width, &width,
&height, &height,
&lossless, &lossless,
&quality_factor, &quality_factor,
&alpha_quality_factor,
&mode, &mode,
&icc_bytes, &icc_bytes,
&icc_size, &icc_size,
@ -637,6 +642,7 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
} }
config.lossless = lossless; config.lossless = lossless;
config.quality = quality_factor; config.quality = quality_factor;
config.alpha_quality = alpha_quality_factor;
config.method = method; config.method = method;
#if WEBP_ENCODER_ABI_VERSION >= 0x0209 #if WEBP_ENCODER_ABI_VERSION >= 0x0209
// the "exact" flag is only available in libwebp 0.5.0 and later // the "exact" flag is only available in libwebp 0.5.0 and later