rename jxl -> jpegxl

This commit is contained in:
olokelo 2024-03-19 22:17:35 +01:00
parent 6048520fcf
commit 8fa280f6a5
11 changed files with 73 additions and 74 deletions

View File

@ -4,7 +4,7 @@ import re
import pytest import pytest
from PIL import Image, JxlImagePlugin, features from PIL import Image, JpegXlImagePlugin, features
from .helper import ( from .helper import (
assert_image_similar_tofile, assert_image_similar_tofile,
@ -12,39 +12,38 @@ from .helper import (
) )
try: try:
from PIL import _jxl from PIL import _jpegxl
HAVE_JXL = True HAVE_JPEGXL = True
except ImportError: except ImportError:
HAVE_JXL = False HAVE_JPEGXL = False
# 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 TestUnsupportedJpegXl:
def test_unsupported(self) -> None: def test_unsupported(self) -> None:
if HAVE_JXL: if HAVE_JPEGXL:
JxlImagePlugin.SUPPORTED = False JpegXlImagePlugin.SUPPORTED = False
file_path = "Tests/images/hopper.jxl" file_path = "Tests/images/hopper.jxl"
with pytest.warns(UserWarning): with pytest.raises(OSError):
with pytest.raises(OSError): with Image.open(file_path):
with Image.open(file_path): pass
pass
if HAVE_JXL: if HAVE_JPEGXL:
JxlImagePlugin.SUPPORTED = True JpegXlImagePlugin.SUPPORTED = True
@skip_unless_feature("jxl") @skip_unless_feature("jpegxl")
class TestFileJxl: class TestFileJpegXl:
def setup_method(self) -> None: def setup_method(self) -> None:
self.rgb_mode = "RGB" self.rgb_mode = "RGB"
def test_version(self) -> None: def test_version(self) -> None:
_jxl.JxlDecoderVersion() _jpegxl.JpegXlDecoderVersion()
assert re.search(r"\d+\.\d+\.\d+$", features.version_module("jxl")) assert re.search(r"\d+\.\d+\.\d+$", features.version_module("jpegxl"))
def test_read_rgb(self) -> None: def test_read_rgb(self) -> None:
""" """
@ -63,10 +62,10 @@ class TestFileJxl:
# djxl hopper.jxl hopper_jxl_bits.ppm # djxl hopper.jxl hopper_jxl_bits.ppm
assert_image_similar_tofile(image, "Tests/images/hopper_jxl_bits.ppm", 1.0) assert_image_similar_tofile(image, "Tests/images/hopper_jxl_bits.ppm", 1.0)
def test_JxlDecode_with_invalid_args(self) -> None: def test_JpegXlDecode_with_invalid_args(self) -> None:
""" """
Calling decoder functions with no arguments should result in an error. Calling decoder functions with no arguments should result in an error.
""" """
with pytest.raises(TypeError): with pytest.raises(TypeError):
_jxl.PILJxlDecoder() _jpegxl.PILJpegXlDecoder()

View File

@ -6,7 +6,7 @@ from PIL import Image
from .helper import assert_image_similar_tofile from .helper import assert_image_similar_tofile
_webp = pytest.importorskip("PIL._jxl", reason="JXL support not installed") _jpegxl = pytest.importorskip("PIL._jpegxl", reason="JPEG XL support not installed")
def test_read_rgba() -> None: def test_read_rgba() -> None:

View File

@ -10,7 +10,7 @@ from .helper import (
) )
pytestmark = [ pytestmark = [
skip_unless_feature("jxl"), skip_unless_feature("jpegxl"),
] ]

View File

@ -9,7 +9,7 @@ from PIL import Image
from .helper import skip_unless_feature from .helper import skip_unless_feature
pytestmark = [ pytestmark = [
skip_unless_feature("jxl"), skip_unless_feature("jpegxl"),
] ]
ElementTree: ModuleType | None ElementTree: ModuleType | None

View File

@ -9,8 +9,8 @@ from .helper import PillowLeakTestCase, skip_unless_feature
TEST_FILE = "Tests/images/hopper.jxl" TEST_FILE = "Tests/images/hopper.jxl"
@skip_unless_feature("jxl") @skip_unless_feature("jpegxl")
class TestJxlLeaks(PillowLeakTestCase): class TestJpegXlLeaks(PillowLeakTestCase):
mem_limit = 6 * 1024 # kb mem_limit = 6 * 1024 # kb
iterations = 1000 iterations = 1000

View File

@ -286,7 +286,7 @@ class pil_build_ext(build_ext):
features = [ features = [
"zlib", "zlib",
"jpeg", "jpeg",
"jxl", "jpegxl",
"tiff", "tiff",
"freetype", "freetype",
"raqm", "raqm",
@ -695,13 +695,13 @@ class pil_build_ext(build_ext):
feature.jpeg2000 = "openjp2" feature.jpeg2000 = "openjp2"
feature.openjpeg_version = ".".join(str(x) for x in best_version) feature.openjpeg_version = ".".join(str(x) for x in best_version)
if feature.want("jxl"): if feature.want("jpegxl"):
_dbg("Looking for jxl") _dbg("Looking for jpegxl")
if _find_include_file(self, "jxl/encode.h") and _find_include_file( if _find_include_file(self, "jxl/encode.h") and _find_include_file(
self, "jxl/decode.h" self, "jxl/decode.h"
): ):
if _find_library_file(self, "jxl"): if _find_library_file(self, "jxl"):
feature.jxl = "jxl jxl_threads" feature.jpegxl = "jxl jxl_threads"
if feature.want("imagequant"): if feature.want("imagequant"):
_dbg("Looking for imagequant") _dbg("Looking for imagequant")
@ -786,14 +786,14 @@ class pil_build_ext(build_ext):
# alternate Windows name. # alternate Windows name.
feature.lcms = "lcms2_static" feature.lcms = "lcms2_static"
if feature.jxl: if feature.jpegxl:
# jxl and jxl_threads are required # jxl and jxl_threads are required
libs = feature.jxl.split() libs = feature.jpegxl.split()
defs = [] defs = []
self._update_extension("PIL._jxl", libs, defs) self._update_extension("PIL._jpegxl", libs, defs)
else: else:
self._remove_extension("PIL._jxl") self._remove_extension("PIL._jpegxl")
if feature.want("webp"): if feature.want("webp"):
_dbg("Looking for webp") _dbg("Looking for webp")
@ -955,7 +955,7 @@ class pil_build_ext(build_ext):
(feature.freetype, "FREETYPE2"), (feature.freetype, "FREETYPE2"),
(feature.raqm, "RAQM (Text shaping)", raqm_extra_info), (feature.raqm, "RAQM (Text shaping)", raqm_extra_info),
(feature.lcms, "LITTLECMS2"), (feature.lcms, "LITTLECMS2"),
(feature.jxl, "JXL"), (feature.jpegxl, "JPEG XL"),
(feature.webp, "WEBP"), (feature.webp, "WEBP"),
(feature.webpmux, "WEBPMUX"), (feature.webpmux, "WEBPMUX"),
(feature.xcb, "XCB (X protocol)"), (feature.xcb, "XCB (X protocol)"),
@ -1000,7 +1000,7 @@ ext_modules = [
Extension("PIL._imaging", files), Extension("PIL._imaging", files),
Extension("PIL._imagingft", ["src/_imagingft.c"]), Extension("PIL._imagingft", ["src/_imagingft.c"]),
Extension("PIL._imagingcms", ["src/_imagingcms.c"]), Extension("PIL._imagingcms", ["src/_imagingcms.c"]),
Extension("PIL._jxl", ["src/_jxl.c"]), Extension("PIL._jpegxl", ["src/_jpegxl.c"]),
Extension("PIL._webp", ["src/_webp.c"]), Extension("PIL._webp", ["src/_webp.c"]),
Extension("PIL._imagingtk", ["src/_imagingtk.c", "src/Tk/tkImaging.c"]), Extension("PIL._imagingtk", ["src/_imagingtk.c", "src/Tk/tkImaging.c"]),
Extension("PIL._imagingmath", ["src/_imagingmath.c"]), Extension("PIL._imagingmath", ["src/_imagingmath.c"]),

View File

@ -6,7 +6,7 @@ from io import BytesIO
from . import Image, ImageFile from . import Image, ImageFile
try: try:
from . import _jxl from . import _jpegxl
SUPPORTED = True SUPPORTED = True
except ImportError: except ImportError:
@ -33,14 +33,14 @@ def _accept(prefix: bytes) -> bool:
return is_jxl return is_jxl
class JxlImageFile(ImageFile.ImageFile): class JpegXlImageFile(ImageFile.ImageFile):
format = "JPEG XL" format = "JPEG XL"
format_description = "JPEG XL image" format_description = "JPEG XL image"
__loaded = 0 __loaded = 0
__logical_frame = 0 __logical_frame = 0
def _open(self) -> None: def _open(self) -> None:
self._decoder = _jxl.PILJxlDecoder(self.fp.read()) self._decoder = _jpegxl.PILJpegXlDecoder(self.fp.read())
width, height, mode, has_anim, tps_num, tps_denom, n_loops, n_frames = ( width, height, mode, has_anim, tps_num, tps_denom, n_loops, n_frames = (
self._decoder.get_info() self._decoder.get_info()
@ -174,6 +174,6 @@ class JxlImageFile(ImageFile.ImageFile):
return self.__logical_frame return self.__logical_frame
Image.register_open(JxlImageFile.format, JxlImageFile, _accept) Image.register_open(JpegXlImageFile.format, JpegXlImageFile, _accept)
Image.register_extension(JxlImageFile.format, ".jxl") Image.register_extension(JpegXlImageFile.format, ".jxl")
Image.register_mime(JxlImageFile.format, "image/jxl") Image.register_mime(JpegXlImageFile.format, "image/jxl")

View File

@ -47,7 +47,7 @@ _plugins = [
"IptcImagePlugin", "IptcImagePlugin",
"JpegImagePlugin", "JpegImagePlugin",
"Jpeg2KImagePlugin", "Jpeg2KImagePlugin",
"JxlImagePlugin", "JpegXlImagePlugin",
"McIdasImagePlugin", "McIdasImagePlugin",
"MicImagePlugin", "MicImagePlugin",
"MpegImagePlugin", "MpegImagePlugin",

View File

@ -14,7 +14,7 @@ modules = {
"tkinter": ("PIL._tkinter_finder", "tk_version"), "tkinter": ("PIL._tkinter_finder", "tk_version"),
"freetype2": ("PIL._imagingft", "freetype2_version"), "freetype2": ("PIL._imagingft", "freetype2_version"),
"littlecms2": ("PIL._imagingcms", "littlecms_version"), "littlecms2": ("PIL._imagingcms", "littlecms_version"),
"jxl": ("PIL._jxl", "libjxl_version"), "jpegxl": ("PIL._jpegxl", "libjxl_version"),
"webp": ("PIL._webp", "webpdecoder_version"), "webp": ("PIL._webp", "webpdecoder_version"),
} }
@ -269,7 +269,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"), ("jpegxl", "JPEG XL"),
("jpg", "JPEG"), ("jpg", "JPEG"),
("jpg_2000", "OPENJPEG (JPEG2000)"), ("jpg_2000", "OPENJPEG (JPEG2000)"),
("zlib", "ZLIB (PNG/ZIP)"), ("zlib", "ZLIB (PNG/ZIP)"),

View File

@ -83,13 +83,13 @@ typedef struct {
Py_ssize_t n_frames; Py_ssize_t n_frames;
char *mode; char *mode;
} PILJxlDecoderObject; } PILJpegXlDecoderObject;
static PyTypeObject PILJxlDecoder_Type; static PyTypeObject PILJpegXlDecoder_Type;
void void
_jxl_decoder_dealloc(PyObject *self) { _jxl_decoder_dealloc(PyObject *self) {
PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self;
if (decp->jxl_data) { if (decp->jxl_data) {
free(decp->jxl_data); free(decp->jxl_data);
@ -131,7 +131,7 @@ _jxl_decoder_dealloc(PyObject *self) {
// sets input jxl bitstream loaded into jxl_data // sets input jxl bitstream loaded into jxl_data
// has to be called after every rewind // has to be called after every rewind
void _jxl_decoder_set_input(PyObject *self) { void _jxl_decoder_set_input(PyObject *self) {
PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self;
decp->status = JxlDecoderSetInput(decp->decoder, decp->jxl_data, decp->status = JxlDecoderSetInput(decp->decoder, decp->jxl_data,
decp->jxl_data_len); decp->jxl_data_len);
@ -144,7 +144,7 @@ void _jxl_decoder_set_input(PyObject *self) {
PyObject * PyObject *
_jxl_decoder_rewind(PyObject *self) { _jxl_decoder_rewind(PyObject *self) {
PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self;
JxlDecoderRewind(decp->decoder); JxlDecoderRewind(decp->decoder);
Py_RETURN_NONE; Py_RETURN_NONE;
@ -152,7 +152,7 @@ _jxl_decoder_rewind(PyObject *self) {
bool bool
_jxl_decoder_count_frames(PyObject *self) { _jxl_decoder_count_frames(PyObject *self) {
PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self;
decp->n_frames = 0; decp->n_frames = 0;
@ -179,8 +179,8 @@ PyObject *
_jxl_decoder_new(PyObject *self, PyObject *args) { _jxl_decoder_new(PyObject *self, PyObject *args) {
PyBytesObject *jxl_string; PyBytesObject *jxl_string;
PILJxlDecoderObject *decp = NULL; PILJpegXlDecoderObject *decp = NULL;
decp = PyObject_New(PILJxlDecoderObject, &PILJxlDecoder_Type); decp = PyObject_New(PILJpegXlDecoderObject, &PILJpegXlDecoder_Type);
decp->mode = NULL; decp->mode = NULL;
decp->jxl_data = NULL; decp->jxl_data = NULL;
decp->jxl_data_len = 0; decp->jxl_data_len = 0;
@ -202,7 +202,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) {
return NULL; return NULL;
} }
// this data needs to be copied to PILJxlDecoderObject // this data needs to be copied to PILJpegXlDecoderObject
// so that input bitstream is preserved across calls // so that input bitstream is preserved across calls
const uint8_t *_tmp_jxl_data; const uint8_t *_tmp_jxl_data;
Py_ssize_t _tmp_jxl_data_len; Py_ssize_t _tmp_jxl_data_len;
@ -393,7 +393,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) {
PyObject * PyObject *
_jxl_decoder_get_info(PyObject *self) { _jxl_decoder_get_info(PyObject *self) {
PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self;
return Py_BuildValue( return Py_BuildValue(
"IIsiIIII", "IIsiIIII",
@ -411,7 +411,7 @@ _jxl_decoder_get_info(PyObject *self) {
PyObject * PyObject *
_jxl_decoder_get_next(PyObject *self) { _jxl_decoder_get_next(PyObject *self) {
PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self;
PyObject *bytes; PyObject *bytes;
PyObject *ret; PyObject *ret;
JxlFrameHeader fhdr = {}; JxlFrameHeader fhdr = {};
@ -500,7 +500,7 @@ _jxl_decoder_get_next(PyObject *self) {
PyObject * PyObject *
_jxl_decoder_get_icc(PyObject *self) { _jxl_decoder_get_icc(PyObject *self) {
PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self;
if (!decp->jxl_icc) Py_RETURN_NONE; if (!decp->jxl_icc) Py_RETURN_NONE;
@ -509,7 +509,7 @@ _jxl_decoder_get_icc(PyObject *self) {
PyObject * PyObject *
_jxl_decoder_get_exif(PyObject *self) { _jxl_decoder_get_exif(PyObject *self) {
PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self;
if (!decp->jxl_exif) Py_RETURN_NONE; if (!decp->jxl_exif) Py_RETURN_NONE;
@ -518,15 +518,15 @@ _jxl_decoder_get_exif(PyObject *self) {
PyObject * PyObject *
_jxl_decoder_get_xmp(PyObject *self) { _jxl_decoder_get_xmp(PyObject *self) {
PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self;
if (!decp->jxl_xmp) Py_RETURN_NONE; if (!decp->jxl_xmp) Py_RETURN_NONE;
return PyBytes_FromStringAndSize((const char *)decp->jxl_xmp, decp->jxl_xmp_len); return PyBytes_FromStringAndSize((const char *)decp->jxl_xmp, decp->jxl_xmp_len);
} }
// PILJxlDecoder methods // PILJpegXlDecoder methods
static struct PyMethodDef _jxl_decoder_methods[] = { static struct PyMethodDef _jpegxl_decoder_methods[] = {
{"get_info", (PyCFunction)_jxl_decoder_get_info, METH_NOARGS, "get_info"}, {"get_info", (PyCFunction)_jxl_decoder_get_info, METH_NOARGS, "get_info"},
{"get_next", (PyCFunction)_jxl_decoder_get_next, METH_NOARGS, "get_next"}, {"get_next", (PyCFunction)_jxl_decoder_get_next, METH_NOARGS, "get_next"},
{"get_icc", (PyCFunction)_jxl_decoder_get_icc, METH_NOARGS, "get_icc"}, {"get_icc", (PyCFunction)_jxl_decoder_get_icc, METH_NOARGS, "get_icc"},
@ -536,10 +536,10 @@ static struct PyMethodDef _jxl_decoder_methods[] = {
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
// PILJxlDecoder type definition // PILJpegXlDecoder type definition
static PyTypeObject PILJxlDecoder_Type = { static PyTypeObject PILJpegXlDecoder_Type = {
PyVarObject_HEAD_INIT(NULL, 0) "PILJxlDecoder", /*tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "PILJpegXlDecoder", /*tp_name */
sizeof(PILJxlDecoderObject), /*tp_basicsize */ sizeof(PILJpegXlDecoderObject), /*tp_basicsize */
0, /*tp_itemsize */ 0, /*tp_itemsize */
/* methods */ /* methods */
(destructor)_jxl_decoder_dealloc, /*tp_dealloc*/ (destructor)_jxl_decoder_dealloc, /*tp_dealloc*/
@ -565,7 +565,7 @@ static PyTypeObject PILJxlDecoder_Type = {
0, /*tp_weaklistoffset*/ 0, /*tp_weaklistoffset*/
0, /*tp_iter*/ 0, /*tp_iter*/
0, /*tp_iternext*/ 0, /*tp_iternext*/
_jxl_decoder_methods, /*tp_methods*/ _jpegxl_decoder_methods, /*tp_methods*/
0, /*tp_members*/ 0, /*tp_members*/
0, /*tp_getset*/ 0, /*tp_getset*/
}; };
@ -573,13 +573,13 @@ static PyTypeObject PILJxlDecoder_Type = {
// Return libjxl decoder version available as integer: // Return libjxl decoder version available as integer:
// MAJ*1_000_000 + MIN*1_000 + PATCH // MAJ*1_000_000 + MIN*1_000 + PATCH
PyObject * PyObject *
JxlDecoderVersion_wrapper() { JpegXlDecoderVersion_wrapper() {
return Py_BuildValue("i", JxlDecoderVersion()); return Py_BuildValue("i", JxlDecoderVersion());
} }
// Version as string // Version as string
const char * const char *
JxlDecoderVersion_str(void) { JpegXlDecoderVersion_str(void) {
static char version[20]; static char version[20];
int version_number = JxlDecoderVersion(); int version_number = JxlDecoderVersion();
sprintf( sprintf(
@ -592,22 +592,22 @@ JxlDecoderVersion_str(void) {
return version; return version;
} }
static PyMethodDef jxlMethods[] = { static PyMethodDef jpegxlMethods[] = {
{"JxlDecoderVersion", JxlDecoderVersion_wrapper, METH_NOARGS, "JxlVersion"}, {"JpegXlDecoderVersion", JpegXlDecoderVersion_wrapper, METH_NOARGS, "JpegXlVersion"},
{"PILJxlDecoder", _jxl_decoder_new, METH_VARARGS, "PILJxlDecoder"}, {"PILJpegXlDecoder", _jxl_decoder_new, METH_VARARGS, "PILJpegXlDecoder"},
{NULL, NULL} {NULL, NULL}
}; };
static int static int
setup_module(PyObject *m) { setup_module(PyObject *m) {
if (PyType_Ready(&PILJxlDecoder_Type) < 0) { if (PyType_Ready(&PILJpegXlDecoder_Type) < 0) {
return -1; return -1;
} }
// TODO(oloke) ready object types? // TODO(oloke) ready object types?
PyObject *d = PyModule_GetDict(m); PyObject *d = PyModule_GetDict(m);
PyObject *v = PyUnicode_FromString(JxlDecoderVersion_str()); PyObject *v = PyUnicode_FromString(JpegXlDecoderVersion_str());
PyDict_SetItemString(d, "libjxl_version", v ? v : Py_None); PyDict_SetItemString(d, "libjxl_version", v ? v : Py_None);
Py_XDECREF(v); Py_XDECREF(v);
@ -615,15 +615,15 @@ setup_module(PyObject *m) {
} }
PyMODINIT_FUNC PyMODINIT_FUNC
PyInit__jxl(void) { PyInit__jpegxl(void) {
PyObject *m; PyObject *m;
static PyModuleDef module_def = { static PyModuleDef module_def = {
PyModuleDef_HEAD_INIT, PyModuleDef_HEAD_INIT,
"_jxl", /* m_name */ "_jpegxl", /* m_name */
NULL, /* m_doc */ NULL, /* m_doc */
-1, /* m_size */ -1, /* m_size */
jxlMethods, /* m_methods */ jpegxlMethods, /* m_methods */
}; };
m = PyModule_Create(&module_def); m = PyModule_Create(&module_def);