[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
This commit is contained in:
pre-commit-ci[bot] 2024-03-02 00:38:49 +00:00
parent 8e0c5db1e0
commit a57ebeaaf4
7 changed files with 65 additions and 59 deletions

View File

@ -1,13 +1,13 @@
from __future__ import annotations
import re import re
import pytest import pytest
from PIL import Image, JxlImagePlugin, features from PIL import Image, JxlImagePlugin, features
from .helper import ( from .helper import (
assert_image_equal,
assert_image_similar,
assert_image_similar_tofile, assert_image_similar_tofile,
hopper,
skip_unless_feature, skip_unless_feature,
) )
@ -21,6 +21,7 @@ except ImportError:
# cjxl v0.9.2 41b8cdab # cjxl v0.9.2 41b8cdab
# hopper.jxl: cjxl hopper.png hopper.jxl -q 75 -e 8 # hopper.jxl: cjxl hopper.png hopper.jxl -q 75 -e 8
class TestUnsupportedJxl: class TestUnsupportedJxl:
def test_unsupported(self) -> None: def test_unsupported(self) -> None:
if HAVE_JXL: if HAVE_JXL:
@ -35,6 +36,7 @@ class TestUnsupportedJxl:
if HAVE_JXL: if HAVE_JXL:
JxlImagePlugin.SUPPORTED = True JxlImagePlugin.SUPPORTED = True
@skip_unless_feature("jxl") @skip_unless_feature("jxl")
class TestFileJxl: class TestFileJxl:
def setup_method(self) -> None: def setup_method(self) -> None:
@ -68,5 +70,3 @@ class TestFileJxl:
with pytest.raises(TypeError): with pytest.raises(TypeError):
_jxl.PILJxlDecoder() _jxl.PILJxlDecoder()

View File

@ -4,9 +4,7 @@ import pytest
from PIL import Image from PIL import Image
from .helper import ( from .helper import assert_image_similar_tofile
assert_image_similar_tofile
)
_webp = pytest.importorskip("PIL._jxl", reason="JXL support not installed") _webp = pytest.importorskip("PIL._jxl", reason="JXL support not installed")
@ -29,4 +27,3 @@ def test_read_rgba() -> None:
image.tobytes() image.tobytes()
assert_image_similar_tofile(image, "Tests/images/transparent.png", 1.0) assert_image_similar_tofile(image, "Tests/images/transparent.png", 1.0)

View File

@ -1,9 +1,8 @@
from __future__ import annotations from __future__ import annotations
import pytest import pytest
from packaging.version import parse as parse_version
from PIL import Image, features from PIL import Image
from .helper import ( from .helper import (
assert_image_equal, assert_image_equal,
@ -52,9 +51,9 @@ def test_seeking() -> None:
im1.load() im1.load()
im2.seek(frame) im2.seek(frame)
im2.load() im2.load()
assert_image_equal(im1.convert('RGB'), im2.convert('RGB')) assert_image_equal(im1.convert("RGB"), im2.convert("RGB"))
total_dur += im1.info["duration"] total_dur += im1.info["duration"]
assert im1.info["duration"] == im2.info["duration"] assert im1.info["duration"] == im2.info["duration"]
assert im1.info["timestamp"] == im1.info["timestamp"] assert im1.info["timestamp"] == im1.info["timestamp"]

View File

@ -1,14 +1,12 @@
from __future__ import annotations from __future__ import annotations
from io import BytesIO
from pathlib import Path
from types import ModuleType from types import ModuleType
import pytest import pytest
from PIL import Image from PIL import Image
from .helper import mark_if_feature_version, skip_unless_feature from .helper import skip_unless_feature
pytestmark = [ pytestmark = [
skip_unless_feature("jxl"), skip_unless_feature("jxl"),
@ -26,6 +24,7 @@ except ImportError:
# python -c "from PIL import Image; im=Image.open('Tests/images/flower2.webp'); f=open('/tmp/xmp.xml', 'wb'); f.write(im.info['xmp']); f.close()" # python -c "from PIL import Image; im=Image.open('Tests/images/flower2.webp'); f=open('/tmp/xmp.xml', 'wb'); f.write(im.info['xmp']); f.close()"
# cjxl flower2.jpg flower2.jxl --lossless_jpeg=0 -q 75 -e 8 -x xmp=/tmp/xmp.xml # cjxl flower2.jpg flower2.jxl --lossless_jpeg=0 -q 75 -e 8 -x xmp=/tmp/xmp.xml
def test_read_exif_metadata() -> None: def test_read_exif_metadata() -> None:
file_path = "Tests/images/flower.jxl" file_path = "Tests/images/flower.jxl"
with Image.open(file_path) as image: with Image.open(file_path) as image:
@ -85,4 +84,3 @@ def test_getxmp() -> None:
im.getxmp()["xmpmeta"]["xmptk"] im.getxmp()["xmpmeta"]["xmptk"]
== "Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 " == "Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 "
) )

View File

@ -1,6 +1,9 @@
from io import BytesIO from __future__ import annotations
from . import Image, ImageFile
import struct import struct
from io import BytesIO
from . import Image, ImageFile
try: try:
from . import _jxl from . import _jxl
@ -16,11 +19,14 @@ except ImportError:
## then libjxl decoder is rewinded and we're ready to decode frame by frame ## then libjxl decoder is rewinded and we're ready to decode frame by frame
## if OPEN_COUNTS_FRAMES is False, n_frames will be None until the last frame is decoded ## if OPEN_COUNTS_FRAMES is False, n_frames will be None until the last frame is decoded
## it only applies to animated jpeg xl images ## it only applies to animated jpeg xl images
#OPEN_COUNTS_FRAMES = True # OPEN_COUNTS_FRAMES = True
def _accept(prefix): def _accept(prefix):
is_jxl = prefix[:2] == b'\xff\x0a' \ is_jxl = (
or prefix[:12] == b'\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a' prefix[:2] == b"\xff\x0a"
or prefix[:12] == b"\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a"
)
if is_jxl and not SUPPORTED: if is_jxl and not SUPPORTED:
return "image file could not be identified because JXL support not installed" return "image file could not be identified because JXL support not installed"
return is_jxl return is_jxl
@ -35,7 +41,9 @@ class JxlImageFile(ImageFile.ImageFile):
def _open(self): def _open(self):
self._decoder = _jxl.PILJxlDecoder(self.fp.read()) self._decoder = _jxl.PILJxlDecoder(self.fp.read())
width, height, mode, has_anim, tps_num, tps_denom, n_loops, n_frames = self._decoder.get_info() width, height, mode, has_anim, tps_num, tps_denom, n_loops, n_frames = (
self._decoder.get_info()
)
self._size = width, height self._size = width, height
self.info["loop"] = n_loops self.info["loop"] = n_loops
self.is_animated = has_anim self.is_animated = has_anim
@ -46,7 +54,7 @@ class JxlImageFile(ImageFile.ImageFile):
self.n_frames = 1 self.n_frames = 1
elif n_frames > 0: elif n_frames > 0:
self.n_frames = n_frames self.n_frames = n_frames
self._tps_dur_secs = tps_num/tps_denom self._tps_dur_secs = tps_num / tps_denom
# TODO: handle libjxl timecods # TODO: handle libjxl timecods
self.__timestamp = 0 self.__timestamp = 0
@ -57,13 +65,17 @@ class JxlImageFile(ImageFile.ImageFile):
icc = self._decoder.get_icc() icc = self._decoder.get_icc()
exif = self._decoder.get_exif() exif = self._decoder.get_exif()
xmp = self._decoder.get_xmp() xmp = self._decoder.get_xmp()
if icc: self.info["icc_profile"] = icc if icc:
self.info["icc_profile"] = icc
import traceback import traceback
try: try:
if exif: self.info["exif"] = self._fix_exif(exif) if exif:
self.info["exif"] = self._fix_exif(exif)
except: except:
traceback.print_exc() traceback.print_exc()
if xmp: self.info["xmp"] = xmp if xmp:
self.info["xmp"] = xmp
self._rewind() self._rewind()
@ -73,7 +85,7 @@ class JxlImageFile(ImageFile.ImageFile):
if len(exif) <= 4: if len(exif) <= 4:
return None return None
exif_start_offset = struct.unpack(">I", exif[:4])[0] exif_start_offset = struct.unpack(">I", exif[:4])[0]
return exif[exif_start_offset+4:] return exif[exif_start_offset + 4 :]
def _getexif(self): def _getexif(self):
if "exif" not in self.info: if "exif" not in self.info:
@ -82,7 +94,7 @@ class JxlImageFile(ImageFile.ImageFile):
def getxmp(self): def getxmp(self):
return self._getxmp(self.info["xmp"]) if "xmp" in self.info else {} return self._getxmp(self.info["xmp"]) if "xmp" in self.info else {}
def _get_next(self): def _get_next(self):
# Get next frame # Get next frame
@ -93,14 +105,14 @@ class JxlImageFile(ImageFile.ImageFile):
if next_frame is None: if next_frame is None:
msg = "failed to decode next frame in JXL file" msg = "failed to decode next frame in JXL file"
raise EOFError(msg) raise EOFError(msg)
data, tps_duration, is_last = next_frame data, tps_duration, is_last = next_frame
if is_last and self.n_frames is None: if is_last and self.n_frames is None:
# libjxl said this frame is the last one # libjxl said this frame is the last one
self.n_frames = self.__physical_frame self.n_frames = self.__physical_frame
# duration in miliseconds # duration in miliseconds
duration = 1000 * tps_duration * (1/self._tps_dur_secs) duration = 1000 * tps_duration * (1 / self._tps_dur_secs)
timestamp = self.__timestamp timestamp = self.__timestamp
self.__timestamp += duration self.__timestamp += duration
@ -115,21 +127,22 @@ class JxlImageFile(ImageFile.ImageFile):
def _seek_check(self, frame): def _seek_check(self, frame):
# if image is not animated then only the 0th frame is available # if image is not animated then only the 0th frame is available
if (not self.is_animated and frame != 0) or \ if (not self.is_animated and frame != 0) or (
(self.n_frames is not None and (frame >= self.n_frames or frame < 0)): self.n_frames is not None and (frame >= self.n_frames or frame < 0)
):
msg = "attempt to seek outside sequence" msg = "attempt to seek outside sequence"
raise EOFError(msg) raise EOFError(msg)
return self.tell() != frame return self.tell() != frame
def _seek(self, frame): def _seek(self, frame):
#print("_seek: phy: {}, fr: {}".format(self.__physical_frame, frame)) # print("_seek: phy: {}, fr: {}".format(self.__physical_frame, frame))
if frame == self.__physical_frame: if frame == self.__physical_frame:
return # Nothing to do return # Nothing to do
if frame < self.__physical_frame: if frame < self.__physical_frame:
# also rewind libjxl decoder instance # also rewind libjxl decoder instance
self._rewind(hard=True) self._rewind(hard=True)
while self.__physical_frame < frame: while self.__physical_frame < frame:
self._get_next() # Advance to the requested frame self._get_next() # Advance to the requested frame
@ -144,7 +157,7 @@ class JxlImageFile(ImageFile.ImageFile):
if self.__loaded != self.__logical_frame: if self.__loaded != self.__logical_frame:
self._seek(self.__logical_frame) self._seek(self.__logical_frame)
data, timestamp, duration, is_last = self._get_next() data, timestamp, duration, is_last = self._get_next()
self.info["timestamp"] = timestamp self.info["timestamp"] = timestamp
self.info["duration"] = duration self.info["duration"] = duration
@ -157,9 +170,9 @@ class JxlImageFile(ImageFile.ImageFile):
# you need probably 2*(raw image plane) bytes of memory # you need probably 2*(raw image plane) bytes of memory
self.fp = BytesIO(data) self.fp = BytesIO(data)
self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)] self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)]
return super().load() return super().load()
def load_seek(self, pos): def load_seek(self, pos):
pass pass
@ -169,4 +182,4 @@ class JxlImageFile(ImageFile.ImageFile):
Image.register_open(JxlImageFile.format, JxlImageFile, _accept) Image.register_open(JxlImageFile.format, JxlImageFile, _accept)
Image.register_extension(JxlImageFile.format, ".jxl") Image.register_extension(JxlImageFile.format, ".jxl")
Image.register_mime(JxlImageFile.format, "image/jxl") Image.register_mime(JxlImageFile.format, "image/jxl")

View File

@ -270,8 +270,7 @@ def pilinfo(out=None, supported_formats=True):
("transp_webp", "WEBP Transparency"), ("transp_webp", "WEBP Transparency"),
("webp_mux", "WEBPMUX"), ("webp_mux", "WEBPMUX"),
("webp_anim", "WEBP Animation"), ("webp_anim", "WEBP Animation"),
("jxl", "JPEG XL") ("jxl", "JPEG XL")("jpg", "JPEG"),
("jpg", "JPEG"),
("jpg_2000", "OPENJPEG (JPEG2000)"), ("jpg_2000", "OPENJPEG (JPEG2000)"),
("zlib", "ZLIB (PNG/ZIP)"), ("zlib", "ZLIB (PNG/ZIP)"),
("libtiff", "LIBTIFF"), ("libtiff", "LIBTIFF"),

View File

@ -12,9 +12,9 @@
void _pil_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { void _pil_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) {
pf->num_channels = bi->num_color_channels + bi->num_extra_channels; pf->num_channels = bi->num_color_channels + bi->num_extra_channels;
if (bi->exponent_bits_per_sample > 0 || bi->alpha_exponent_bits > 0) { if (bi->exponent_bits_per_sample > 0 || bi->alpha_exponent_bits > 0) {
pf->data_type = JXL_TYPE_FLOAT; // not yet supported pf->data_type = JXL_TYPE_FLOAT; // not yet supported
} else if (bi->bits_per_sample > 8) { } else if (bi->bits_per_sample > 8) {
@ -22,7 +22,7 @@ void _pil_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) {
} else { } else {
pf->data_type = JXL_TYPE_UINT8; pf->data_type = JXL_TYPE_UINT8;
} }
// this *might* cause some issues on Big-Endian systems // this *might* cause some issues on Big-Endian systems
// would be great to test it // would be great to test it
pf->endianness = JXL_NATIVE_ENDIAN; pf->endianness = JXL_NATIVE_ENDIAN;
@ -37,7 +37,7 @@ char* _pil_jxl_get_mode(const JxlBasicInfo *bi) {
// it will throw an exception but that's for your own good // it will throw an exception but that's for your own good
// you wouldn't want to see distorted image // you wouldn't want to see distorted image
if (bi->bits_per_sample != 8) return "uns"; if (bi->bits_per_sample != 8) return "uns";
// image has transparency // image has transparency
if (bi->alpha_bits > 0) { if (bi->alpha_bits > 0) {
if (bi->num_color_channels == 3) { if (bi->num_color_channels == 3) {
@ -62,7 +62,7 @@ char* _pil_jxl_get_mode(const JxlBasicInfo *bi) {
typedef struct { typedef struct {
PyObject_HEAD JxlDecoder *decoder; PyObject_HEAD JxlDecoder *decoder;
void *runner; void *runner;
uint8_t *jxl_data; // input jxl bitstream uint8_t *jxl_data; // input jxl bitstream
Py_ssize_t jxl_data_len; // length of input jxl bitstream Py_ssize_t jxl_data_len; // length of input jxl bitstream
@ -75,13 +75,13 @@ typedef struct {
Py_ssize_t jxl_exif_len; Py_ssize_t jxl_exif_len;
uint8_t *jxl_xmp; uint8_t *jxl_xmp;
Py_ssize_t jxl_xmp_len; Py_ssize_t jxl_xmp_len;
JxlDecoderStatus status; JxlDecoderStatus status;
JxlBasicInfo basic_info; JxlBasicInfo basic_info;
JxlPixelFormat pixel_format; JxlPixelFormat pixel_format;
Py_ssize_t n_frames; Py_ssize_t n_frames;
char *mode; char *mode;
} PILJxlDecoderObject; } PILJxlDecoderObject;
@ -90,7 +90,7 @@ static PyTypeObject PILJxlDecoder_Type;
void void
_jxl_decoder_dealloc(PyObject *self) { _jxl_decoder_dealloc(PyObject *self) {
PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self;
if (decp->jxl_data) { if (decp->jxl_data) {
free(decp->jxl_data); free(decp->jxl_data);
decp->jxl_data = NULL; decp->jxl_data = NULL;
@ -209,7 +209,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) {
// convert jxl data string to C uint8_t pointer // convert jxl data string to C uint8_t pointer
PyBytes_AsStringAndSize((PyObject *)jxl_string, (char **)&_tmp_jxl_data, &_tmp_jxl_data_len); PyBytes_AsStringAndSize((PyObject *)jxl_string, (char **)&_tmp_jxl_data, &_tmp_jxl_data_len);
// here occurs this copying (inefficiency) // here occurs this copying (inefficiency)
decp->jxl_data = malloc(_tmp_jxl_data_len); decp->jxl_data = malloc(_tmp_jxl_data_len);
memcpy(decp->jxl_data, _tmp_jxl_data, _tmp_jxl_data_len); memcpy(decp->jxl_data, _tmp_jxl_data, _tmp_jxl_data_len);
@ -222,7 +222,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) {
decp->decoder = JxlDecoderCreate(NULL); decp->decoder = JxlDecoderCreate(NULL);
decp->status = JxlDecoderSetParallelRunner(decp->decoder, decp->status = JxlDecoderSetParallelRunner(decp->decoder,
JxlThreadParallelRunner, decp->runner); JxlThreadParallelRunner, decp->runner);
_PIL_JXL_CHECK("JxlDecoderSetParallelRunner") _PIL_JXL_CHECK("JxlDecoderSetParallelRunner")
decp->status = JxlDecoderSubscribeEvents(decp->decoder, decp->status = JxlDecoderSubscribeEvents(decp->decoder,
@ -241,7 +241,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) {
// decode everything up to the first frame // decode everything up to the first frame
do { do {
decp->status = JxlDecoderProcessInput(decp->decoder); decp->status = JxlDecoderProcessInput(decp->decoder);
//printf("Status: %d\n", decp->status); //printf("Status: %d\n", decp->status);
@ -255,7 +255,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) {
// got basic info // got basic info
if (decp->status == JXL_DEC_BASIC_INFO) { if (decp->status == JXL_DEC_BASIC_INFO) {
decp->status = JxlDecoderGetBasicInfo(decp->decoder, decp->status = JxlDecoderGetBasicInfo(decp->decoder,
&decp->basic_info); &decp->basic_info);
_PIL_JXL_CHECK("JxlDecoderGetBasicInfo"); _PIL_JXL_CHECK("JxlDecoderGetBasicInfo");
@ -274,7 +274,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) {
// got color encoding // got color encoding
if (decp->status == JXL_DEC_COLOR_ENCODING) { if (decp->status == JXL_DEC_COLOR_ENCODING) {
decp->status = JxlDecoderGetICCProfileSize(decp->decoder, decp->status = JxlDecoderGetICCProfileSize(decp->decoder,
JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len); JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len);
_PIL_JXL_CHECK("JxlDecoderGetICCProfileSize"); _PIL_JXL_CHECK("JxlDecoderGetICCProfileSize");
@ -337,7 +337,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) {
final_jxl_buf_len += (cur_compr_box_size - remaining); final_jxl_buf_len += (cur_compr_box_size - remaining);
} }
while (decp->status == JXL_DEC_BOX_NEED_MORE_OUTPUT); while (decp->status == JXL_DEC_BOX_NEED_MORE_OUTPUT);
if (is_box_exif) { if (is_box_exif) {
decp->jxl_exif = final_jxl_buf; decp->jxl_exif = final_jxl_buf;
decp->jxl_exif_len = final_jxl_buf_len; decp->jxl_exif_len = final_jxl_buf_len;
@ -444,7 +444,7 @@ _jxl_decoder_get_next(PyObject *self) {
size_t new_outbuf_len; size_t new_outbuf_len;
decp->status = JxlDecoderImageOutBufferSize(decp->decoder, &decp->pixel_format, &new_outbuf_len); decp->status = JxlDecoderImageOutBufferSize(decp->decoder, &decp->pixel_format, &new_outbuf_len);
_PIL_JXL_CHECK("JxlDecoderImageOutBufferSize"); _PIL_JXL_CHECK("JxlDecoderImageOutBufferSize");
// only allocate memory when current buffer is too small // only allocate memory when current buffer is too small
if (decp->outbuf_len < new_outbuf_len) { if (decp->outbuf_len < new_outbuf_len) {
@ -455,7 +455,7 @@ _jxl_decoder_get_next(PyObject *self) {
goto end_with_custom_error; goto end_with_custom_error;
} }
decp->outbuf = _new_outbuf; decp->outbuf = _new_outbuf;
} }
decp->status = JxlDecoderSetImageOutBuffer(decp->decoder, &decp->pixel_format, decp->outbuf, decp->outbuf_len); decp->status = JxlDecoderSetImageOutBuffer(decp->decoder, &decp->pixel_format, decp->outbuf, decp->outbuf_len);
@ -631,4 +631,4 @@ PyInit__jxl(void) {
} }
return m; return m;
} }