Skip to content

Commit

Permalink
feat: switch to quality setting for AVIF & deprecate qmin/qmax
Browse files Browse the repository at this point in the history
  • Loading branch information
boidolr committed Oct 13, 2023
1 parent 76c3271 commit c6185c5
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.292
hooks:
- id: ruff-format
- id: ruff
args: [--fix]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.1
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ check: venv
## format : Format code.
.PHONY: format
format: venv
$(VENV)/black -q .
$(VENV)/ruff format -q .


## test : Execute tests.
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ For an extended example see [`.pre-commit-config.yaml`](.pre-commit-config.yaml)

- **`optimize-avif`**: Compress `avif` images.
- `--threshold` can be used to configure which size difference should be used to keep the image.
- `--qmin` to configure minimum quality setting (best: 0, worst: 63).
- `--qmax` to configure maximum quality setting (best: 0, worst: 63).
- `--quality` to configure minimum quality setting (best: 100, worst: 0).
- `--effort` to set the quality/speed tradeoff (slowest: 0, fastest: 10).
- **`optimize-jpg`**: Compress `jpeg` images.
- `--threshold` can be used to configure which size difference should be used to keep the image.
Expand Down
31 changes: 26 additions & 5 deletions pre_commit_images/optimize_avif.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import argparse
import sys
import warnings
from collections.abc import Sequence
from pathlib import Path
from typing import IO
Expand All @@ -25,29 +26,49 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
parser.add_argument(
"-min",
"--qmin",
default=50,
type=int,
help="Minimum quality to use for AVIF images (default: %(default)s - from 0 down to 63)",
help="Deprecated: use `quality` instead.",
)
parser.add_argument(
"-max",
"--qmax",
default=30,
type=int,
help="Quality to use for AVIF images (default: %(default)s - from 0 down to 63)",
help="Deprecated: use `quality` instead.",
)
parser.add_argument(
"--quality",
default=None,
type=int,
help="Quality to use for AVIF images (default: 75 - from 0 down to 100)",
)
parser.add_argument(
"-e",
"--effort",
default=4,
type=int,
dest="speed",
help="Effort to use for AVIF images (default: %(default)s - range 0 down to 10)",
)
args = parser.parse_args(argv)

if not args.qmin and not args.qmax and not args.quality:
args.quality = 75

if args.qmin and args.quality or args.qmax and args.quality:
sys.exit("Can not use both `qmin`/`qmax` and `quality`")

if args.qmin or args.qmax:
warnings.warn(
"`qmin`/`qmax` are deprecated, use `quality` instead"
" - it will be the only option for future AVIF versions",
category=DeprecationWarning,
)

options = {option: value for option, value in vars(args).items() if value}

def optimize(source: Path, target: IO[bytes]) -> None:
im = Image.open(source)
im.save(target, format=im.format, speed=args.effort, qmin=args.qmin, qmax=args.qmax)
im.save(target, format=im.format, **options)

success = _optimize_images(args.filenames, optimize, args.threshold)
return 0 if success else 1
Expand Down
50 changes: 32 additions & 18 deletions tests/optimize_avif_test.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,53 @@
import shutil
from pathlib import Path

import pytest

from pre_commit_images.optimize_avif import main


def test_compress_avif(tmpdir):
image = "test.avif"
path = Path(tmpdir) / image
test_file = Path(__file__).parent / image
shutil.copy(test_file, path)
@pytest.fixture
def original_image(tmpdir):
return Path(__file__).parent / "test.avif"


@pytest.fixture
def image(tmpdir, original_image):
path = Path(tmpdir) / "test.avif"
shutil.copy(original_image, path)
return path


assert main(("-min", "63", "-max", "63", "-e", "0", str(path))) == 0
assert test_file.stat().st_size > path.stat().st_size
def test_qmin_qmax_deprecated(image):
with pytest.deprecated_call():
assert main(("-min", "10", str(image))) == 0


def test_compress_avif_below_threshold(tmpdir):
image = "test.avif"
path = Path(tmpdir) / image
test_file = Path(__file__).parent / image
shutil.copy(test_file, path)
def test_qmin_qmax_and_quality(image):
with pytest.raises(SystemExit) as wrapped_exit:
assert main(("-min", "10", "--quality", "20", str(image))) == 1
assert wrapped_exit.type == SystemExit
assert wrapped_exit.value.code == 1


def test_compress_avif(original_image, image):
assert main(("--quality", "75", "-e", "0", str(image))) == 0
assert original_image.stat().st_size > image.stat().st_size


def test_compress_avif_below_threshold(original_image, image):
assert (
main(
(
"-min",
"10",
"-max",
"20",
"--quality",
"90",
"-e",
"10",
"-t",
"6144",
str(path),
str(image),
)
)
== 0
)
assert test_file.stat().st_size == path.stat().st_size
assert original_image.stat().st_size == image.stat().st_size

0 comments on commit c6185c5

Please sign in to comment.