Skip to content

Commit b88fa56

Browse files
committed
Only use aom codec
1 parent 9f41957 commit b88fa56

7 files changed

Lines changed: 22 additions & 180 deletions

File tree

.github/workflows/wheels-dependencies.sh

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,6 @@ function build_libavif {
122122
build_simple nasm 2.16.03 https://www.nasm.us/pub/nasm/releasebuilds/2.16.03
123123
fi
124124

125-
# For rav1e
126-
curl https://sh.rustup.rs -sSf | sh -s -- -y
127-
. "$HOME/.cargo/env"
128-
if [ -z "$IS_ALPINE" ] && [ -z "$SANITIZER" ] && [ -z "$IS_MACOS" ]; then
129-
yum install -y perl
130-
if [[ "$MB_ML_VER" == 2014 ]]; then
131-
yum install -y perl-IPC-Cmd
132-
fi
133-
fi
134-
135125
local out_dir=$(fetch_unpack https://github.com/AOMediaCodec/libavif/archive/refs/tags/v$LIBAVIF_VERSION.tar.gz libavif-$LIBAVIF_VERSION.tar.gz)
136126
(cd $out_dir \
137127
&& CMAKE_POLICY_VERSION_MINIMUM=3.5 cmake \
@@ -142,9 +132,6 @@ function build_libavif {
142132
-DAVIF_LIBSHARPYUV=LOCAL \
143133
-DAVIF_LIBYUV=LOCAL \
144134
-DAVIF_CODEC_AOM=LOCAL \
145-
-DAVIF_CODEC_DAV1D=LOCAL \
146-
-DAVIF_CODEC_RAV1E=LOCAL \
147-
-DAVIF_CODEC_SVT=LOCAL \
148135
-DENABLE_NASM=ON \
149136
-DCMAKE_MODULE_PATH=/tmp/cmake/Modules \
150137
. \

.github/workflows/wheels.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,6 @@ jobs:
160160
& python.exe winbuild\build_prepare.py -v --no-imagequant --architecture=${{ matrix.cibw_arch }}
161161
shell: pwsh
162162

163-
- name: Update rust
164-
if: matrix.cibw_arch == 'AMD64'
165-
run: |
166-
rustup update
167-
168163
- name: Build wheels
169164
run: |
170165
setlocal EnableDelayedExpansion

Tests/test_file_avif.py

Lines changed: 12 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,6 @@ def roundtrip(im: ImageFile.ImageFile, **options: Any) -> ImageFile.ImageFile:
5151
return Image.open(out)
5252

5353

54-
def skip_unless_avif_decoder(codec_name: str) -> pytest.MarkDecorator:
55-
reason = f"{codec_name} decode not available"
56-
return pytest.mark.skipif(
57-
not HAVE_AVIF or not _avif.decoder_codec_available(codec_name), reason=reason
58-
)
59-
60-
61-
def skip_unless_avif_encoder(codec_name: str) -> pytest.MarkDecorator:
62-
reason = f"{codec_name} encode not available"
63-
return pytest.mark.skipif(
64-
not HAVE_AVIF or not _avif.encoder_codec_available(codec_name), reason=reason
65-
)
66-
67-
6854
def is_docker_qemu() -> bool:
6955
try:
7056
init_proc_exe = os.readlink("/proc/1/exe")
@@ -99,15 +85,12 @@ def test_version(self) -> None:
9985
def test_codec_version(self) -> None:
10086
assert AvifImagePlugin.get_codec_version("unknown") is None
10187

102-
for codec_name in ("aom", "dav1d", "rav1e", "svt"):
103-
codec_version = AvifImagePlugin.get_codec_version(codec_name)
104-
if _avif.decoder_codec_available(
105-
codec_name
106-
) or _avif.encoder_codec_available(codec_name):
107-
assert codec_version is not None
108-
assert re.search(r"^v?\d+\.\d+\.\d+(-([a-z\d])+)*$", codec_version)
109-
else:
110-
assert codec_version is None
88+
codec_version = AvifImagePlugin.get_codec_version("aom")
89+
if _avif.decoder_codec_available() or _avif.encoder_codec_available():
90+
assert codec_version is not None
91+
assert re.search(r"^v?\d+\.\d+\.\d+(-([a-z\d])+)*$", codec_version)
92+
else:
93+
assert codec_version is None
11194

11295
def test_read(self) -> None:
11396
"""
@@ -181,6 +164,9 @@ def test_encoder_finish_none_error(
181164
"""Save should raise an OSError if AvifEncoder.finish returns None"""
182165

183166
class _mock_avif:
167+
def encoder_codec_available(self) -> None:
168+
return True
169+
184170
class AvifEncoder:
185171
def __init__(self, *args: Any) -> None:
186172
pass
@@ -433,26 +419,6 @@ def test_encoder_range_invalid(self, tmp_path: Path) -> None:
433419
with pytest.raises(ValueError):
434420
im.save(test_file, range="foo")
435421

436-
@skip_unless_avif_encoder("aom")
437-
def test_encoder_codec_param(self, tmp_path: Path) -> None:
438-
with Image.open(TEST_AVIF_FILE) as im:
439-
test_file = tmp_path / "temp.avif"
440-
im.save(test_file, codec="aom")
441-
442-
def test_encoder_codec_invalid(self, tmp_path: Path) -> None:
443-
with Image.open(TEST_AVIF_FILE) as im:
444-
test_file = tmp_path / "temp.avif"
445-
with pytest.raises(ValueError):
446-
im.save(test_file, codec="foo")
447-
448-
@skip_unless_avif_decoder("dav1d")
449-
def test_decoder_codec_cannot_encode(self, tmp_path: Path) -> None:
450-
with Image.open(TEST_AVIF_FILE) as im:
451-
test_file = tmp_path / "temp.avif"
452-
with pytest.raises(ValueError):
453-
im.save(test_file, codec="dav1d")
454-
455-
@skip_unless_avif_encoder("aom")
456422
@pytest.mark.parametrize(
457423
"advanced",
458424
[
@@ -469,86 +435,30 @@ def test_encoder_advanced_codec_options(
469435
) -> None:
470436
with Image.open(TEST_AVIF_FILE) as im:
471437
ctrl_buf = BytesIO()
472-
im.save(ctrl_buf, "AVIF", codec="aom")
438+
im.save(ctrl_buf, "AVIF")
473439
test_buf = BytesIO()
474440
im.save(
475441
test_buf,
476442
"AVIF",
477-
codec="aom",
478443
advanced=advanced,
479444
)
480445
assert ctrl_buf.getvalue() != test_buf.getvalue()
481446

482-
@skip_unless_avif_encoder("aom")
483447
@pytest.mark.parametrize("advanced", [{"foo": "bar"}, {"foo": 1234}, 1234])
484448
def test_encoder_advanced_codec_options_invalid(
485449
self, tmp_path: Path, advanced: dict[str, str] | int
486450
) -> None:
487451
with Image.open(TEST_AVIF_FILE) as im:
488452
test_file = tmp_path / "temp.avif"
489453
with pytest.raises(ValueError):
490-
im.save(test_file, codec="aom", advanced=advanced)
491-
492-
@skip_unless_avif_decoder("aom")
493-
def test_decoder_codec_param(self, monkeypatch: pytest.MonkeyPatch) -> None:
494-
monkeypatch.setattr(AvifImagePlugin, "DECODE_CODEC_CHOICE", "aom")
495-
496-
with Image.open(TEST_AVIF_FILE) as im:
497-
assert im.size == (128, 128)
498-
499-
@skip_unless_avif_encoder("rav1e")
500-
def test_encoder_codec_cannot_decode(
501-
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
502-
) -> None:
503-
monkeypatch.setattr(AvifImagePlugin, "DECODE_CODEC_CHOICE", "rav1e")
504-
505-
with pytest.raises(ValueError):
506-
with Image.open(TEST_AVIF_FILE):
507-
pass
508-
509-
def test_decoder_codec_invalid(self, monkeypatch: pytest.MonkeyPatch) -> None:
510-
monkeypatch.setattr(AvifImagePlugin, "DECODE_CODEC_CHOICE", "foo")
511-
512-
with pytest.raises(ValueError):
513-
with Image.open(TEST_AVIF_FILE):
514-
pass
515-
516-
@skip_unless_avif_encoder("aom")
517-
def test_encoder_codec_available(self) -> None:
518-
assert _avif.encoder_codec_available("aom") is True
519-
520-
def test_encoder_codec_available_bad_params(self) -> None:
521-
with pytest.raises(TypeError):
522-
_avif.encoder_codec_available()
523-
524-
@skip_unless_avif_decoder("dav1d")
525-
def test_encoder_codec_available_cannot_decode(self) -> None:
526-
assert _avif.encoder_codec_available("dav1d") is False
527-
528-
def test_encoder_codec_available_invalid(self) -> None:
529-
assert _avif.encoder_codec_available("foo") is False
454+
im.save(test_file, advanced=advanced)
530455

531456
def test_encoder_quality_valueerror(self, tmp_path: Path) -> None:
532457
with Image.open(TEST_AVIF_FILE) as im:
533458
test_file = tmp_path / "temp.avif"
534459
with pytest.raises(ValueError):
535460
im.save(test_file, quality="invalid")
536461

537-
@skip_unless_avif_decoder("aom")
538-
def test_decoder_codec_available(self) -> None:
539-
assert _avif.decoder_codec_available("aom") is True
540-
541-
def test_decoder_codec_available_bad_params(self) -> None:
542-
with pytest.raises(TypeError):
543-
_avif.decoder_codec_available()
544-
545-
@skip_unless_avif_encoder("rav1e")
546-
def test_decoder_codec_available_cannot_decode(self) -> None:
547-
assert _avif.decoder_codec_available("rav1e") is False
548-
549-
def test_decoder_codec_available_invalid(self) -> None:
550-
assert _avif.decoder_codec_available("foo") is False
551-
552462
def test_p_mode_transparency(self, tmp_path: Path) -> None:
553463
im = Image.new("P", size=(64, 64))
554464
draw = ImageDraw.Draw(im)
@@ -569,16 +479,10 @@ def test_decoder_strict_flags(self) -> None:
569479
with Image.open("Tests/images/avif/hopper-missing-pixi.avif") as im:
570480
assert im.size == (128, 128)
571481

572-
@skip_unless_avif_encoder("aom")
573482
@pytest.mark.parametrize("speed", [-1, 1, 11])
574483
def test_aom_optimizations(self, tmp_path: Path, speed: int) -> None:
575484
test_file = tmp_path / "temp.avif"
576-
hopper().save(test_file, codec="aom", speed=speed)
577-
578-
@skip_unless_avif_encoder("svt")
579-
def test_svt_optimizations(self, tmp_path: Path) -> None:
580-
test_file = tmp_path / "temp.avif"
581-
hopper().save(test_file, codec="svt", speed=1)
485+
hopper().save(test_file, speed=speed)
582486

583487

584488
@skip_unless_feature("avif")

docs/handbook/image-file-formats.rst

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,11 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
5050
Quality/speed trade-off (0=slower/better, 10=fastest). Defaults to 6.
5151

5252
**max_threads**
53-
Limit the number of active threads used. By default, there is no limit. If the aom
54-
codec is used, there is a maximum of 64.
53+
Limit the number of active threads used. There is a maximum of 64.
5554

5655
**range**
5756
YUV range, either "full" or "limited". Defaults to "full".
5857

59-
**codec**
60-
AV1 codec to use for encoding. Specific values are "aom", "rav1e", and
61-
"svt", presuming the chosen codec is available. Defaults to "auto", which
62-
will choose the first available codec in the order of the preceding list.
63-
6458
**tile_rows** / **tile_cols**
6559
For tile encoding, the (log 2) number of tile rows and columns to use.
6660
Valid values are 0-6, default 0. Ignored if "autotiling" is set to true.

src/PIL/AvifImagePlugin.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@
1313
except ImportError:
1414
SUPPORTED = False
1515

16-
# Decoder options as module globals, until there is a way to pass parameters
17-
# to Image.open (see https://github.com/python-pillow/Pillow/issues/569)
18-
DECODE_CODEC_CHOICE = "auto"
1916
# Decoding is only affected by this for libavif **0.8.4** or greater.
2017
DEFAULT_MAX_THREADS = 0
2118

@@ -73,14 +70,11 @@ def _open(self) -> None:
7370
msg = "image file could not be opened because AVIF support not installed"
7471
raise SyntaxError(msg)
7572

76-
if DECODE_CODEC_CHOICE != "auto" and not _avif.decoder_codec_available(
77-
DECODE_CODEC_CHOICE
78-
):
79-
msg = "Invalid opening codec"
73+
if not _avif.decoder_codec_available():
74+
msg = "Codec not available"
8075
raise ValueError(msg)
8176
self._decoder = _avif.AvifDecoder(
8277
self.fp.read(),
83-
DECODE_CODEC_CHOICE,
8478
_get_default_max_threads(),
8579
)
8680

@@ -165,9 +159,8 @@ def _save(
165159
subsampling = info.get("subsampling", "4:2:0")
166160
speed = info.get("speed", 6)
167161
max_threads = info.get("max_threads", _get_default_max_threads())
168-
codec = info.get("codec", "auto")
169-
if codec != "auto" and not _avif.encoder_codec_available(codec):
170-
msg = "Invalid saving codec"
162+
if not _avif.encoder_codec_available():
163+
msg = "Codec not available"
171164
raise ValueError(msg)
172165
range_ = info.get("range", "full")
173166
tile_rows_log2 = info.get("tile_rows", 0)
@@ -218,7 +211,6 @@ def _save(
218211
quality,
219212
speed,
220213
max_threads,
221-
codec,
222214
range_,
223215
tile_rows_log2,
224216
tile_cols_log2,

src/_avif.c

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -142,21 +142,13 @@ _codec_available(const char *name, avifCodecFlags flags) {
142142

143143
PyObject *
144144
_decoder_codec_available(PyObject *self, PyObject *args) {
145-
char *codec_name;
146-
if (!PyArg_ParseTuple(args, "s", &codec_name)) {
147-
return NULL;
148-
}
149-
int is_available = _codec_available(codec_name, AVIF_CODEC_FLAG_CAN_DECODE);
145+
int is_available = _codec_available("aom", AVIF_CODEC_FLAG_CAN_DECODE);
150146
return PyBool_FromLong(is_available);
151147
}
152148

153149
PyObject *
154150
_encoder_codec_available(PyObject *self, PyObject *args) {
155-
char *codec_name;
156-
if (!PyArg_ParseTuple(args, "s", &codec_name)) {
157-
return NULL;
158-
}
159-
int is_available = _codec_available(codec_name, AVIF_CODEC_FLAG_CAN_ENCODE);
151+
int is_available = _codec_available("aom", AVIF_CODEC_FLAG_CAN_ENCODE);
160152
return PyBool_FromLong(is_available);
161153
}
162154

@@ -229,22 +221,20 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
229221
int tile_rows_log2;
230222
int tile_cols_log2;
231223

232-
char *codec;
233224
char *range;
234225

235226
PyObject *advanced;
236227
int error = 0;
237228

238229
if (!PyArg_ParseTuple(
239230
args,
240-
"(II)siiissiippy*y*iy*O",
231+
"(II)siiisiippy*y*iy*O",
241232
&width,
242233
&height,
243234
&subsampling,
244235
&quality,
245236
&speed,
246237
&max_threads,
247-
&codec,
248238
&range,
249239
&tile_rows_log2,
250240
&tile_cols_log2,
@@ -310,18 +300,10 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
310300
goto end;
311301
}
312302

313-
int is_aom_encode = strcmp(codec, "aom") == 0 ||
314-
(strcmp(codec, "auto") == 0 &&
315-
_codec_available("aom", AVIF_CODEC_FLAG_CAN_ENCODE));
316-
encoder->maxThreads = is_aom_encode && max_threads > 64 ? 64 : max_threads;
303+
encoder->maxThreads = max_threads > 64 ? 64 : max_threads;
317304

318305
encoder->quality = quality;
319306

320-
if (strcmp(codec, "auto") == 0) {
321-
encoder->codecChoice = AVIF_CODEC_CHOICE_AUTO;
322-
} else {
323-
encoder->codecChoice = avifCodecChoiceFromName(codec);
324-
}
325307
if (speed < AVIF_SPEED_SLOWEST) {
326308
speed = AVIF_SPEED_SLOWEST;
327309
} else if (speed > AVIF_SPEED_FASTEST) {
@@ -616,22 +598,14 @@ AvifDecoderNew(PyObject *self_, PyObject *args) {
616598
AvifDecoderObject *self = NULL;
617599
avifDecoder *decoder;
618600

619-
char *codec_str;
620-
avifCodecChoice codec;
621601
int max_threads;
622602

623603
avifResult result;
624604

625-
if (!PyArg_ParseTuple(args, "y*si", &buffer, &codec_str, &max_threads)) {
605+
if (!PyArg_ParseTuple(args, "y*i", &buffer, &max_threads)) {
626606
return NULL;
627607
}
628608

629-
if (strcmp(codec_str, "auto") == 0) {
630-
codec = AVIF_CODEC_CHOICE_AUTO;
631-
} else {
632-
codec = avifCodecChoiceFromName(codec_str);
633-
}
634-
635609
self = PyObject_New(AvifDecoderObject, &AvifDecoder_Type);
636610
if (!self) {
637611
PyErr_SetString(PyExc_RuntimeError, "could not create decoder object");
@@ -653,7 +627,6 @@ AvifDecoderNew(PyObject *self_, PyObject *args) {
653627
// items. libheif v1.11.0 and older does not add the 'pixi' item property to
654628
// AV1 image items.
655629
decoder->strictFlags &= ~AVIF_STRICT_PIXI_REQUIRED;
656-
decoder->codecChoice = codec;
657630

658631
result = avifDecoderSetIOMemory(decoder, buffer.buf, buffer.len);
659632
if (result != AVIF_RESULT_OK) {

0 commit comments

Comments
 (0)