diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py index 76fd09dac9b..4b065de785d 100644 --- a/Tests/test_file_pcx.py +++ b/Tests/test_file_pcx.py @@ -213,5 +213,6 @@ def test_break_padding(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: ) def test_truncated(data_len: int, rawmode: str) -> None: data = b"\x00" * data_len - with pytest.raises(ValueError, match="not enough image data"): - Image.frombuffer("P", (9, 1), data, "raw", rawmode, 0, 1) + with pytest.warns(DeprecationWarning, match=rawmode): + with pytest.raises(ValueError, match="not enough image data"): + Image.frombuffer("P", (9, 1), data, "raw", rawmode, 0, 1) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index c73e6b9296b..20624b26c40 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -83,6 +83,15 @@ Image getdata() identical, except that it returns a tuple of pixel values, instead of an internal Pillow data type. +Reading "P;2L" and "P;4L" raw mode data directly +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 12.3.0 + +Using :py:func:`.Image.frombuffer()`, :py:func:`.Image.frombytes()` or +:py:meth:`~PIL.Image.Image.frombytes()` to read "P;2L" or "P;4L" raw mode data has been +deprecated. + Removed features ---------------- diff --git a/docs/releasenotes/12.3.0.rst b/docs/releasenotes/12.3.0.rst index 534f22b86d3..d4074ff8e5b 100644 --- a/docs/releasenotes/12.3.0.rst +++ b/docs/releasenotes/12.3.0.rst @@ -25,10 +25,12 @@ TODO Deprecations ============ -TODO -^^^^ +Reading "P;2L" and "P;4L" raw mode data directly +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -TODO +Using :py:func:`.Image.frombuffer()`, :py:func:`.Image.frombytes()` or +:py:meth:`~PIL.Image.Image.frombytes()` to read "P;2L" or "P;4L" raw mode data has been +deprecated. API changes =========== diff --git a/src/PIL/Image.py b/src/PIL/Image.py index ebbd8fd35f8..6452edc9335 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -943,11 +943,14 @@ def frombytes( # may pass tuple instead of argument list decoder_args = decoder_args[0] - if decoder_args and decoder_args[0] in {"P;2L", "P;4L"}: - multiple = 4 if decoder_args[0] == "P;2L" else 8 - if len(data) % multiple: - msg = "not enough image data" - raise ValueError(msg) + if decoder_args: + raw_mode = decoder_args[0] + if raw_mode in {"P;2L", "P;4L"}: + deprecate(raw_mode, 14) + multiple = 4 if raw_mode == "P;2L" else 8 + if len(data) % multiple: + msg = "not enough image data" + raise ValueError(msg) # default format if decoder_name == "raw" and decoder_args == (): diff --git a/src/decode.c b/src/decode.c index 5c6c250987d..6937df0c5df 100644 --- a/src/decode.c +++ b/src/decode.c @@ -37,6 +37,7 @@ #include "libImaging/Bit.h" #include "libImaging/Bcn.h" #include "libImaging/Gif.h" +#include "libImaging/PcxDecode.h" #include "libImaging/Raw.h" #include "libImaging/Sgi.h" @@ -618,7 +619,13 @@ PyImaging_PcxDecoderNew(PyObject *self, PyObject *args) { return NULL; } - if (get_unpacker(decoder, mode, rawmode) < 0) { + if (strcmp(rawmode_name, "P;2L") == 0) { + decoder->state.shuffle = unpackP2L; + decoder->state.bits = 2; + } else if (strcmp(rawmode_name, "P;4L") == 0) { + decoder->state.shuffle = unpackP4L; + decoder->state.bits = 4; + } else if (get_unpacker(decoder, mode, rawmode) < 0) { return NULL; } diff --git a/src/libImaging/PcxDecode.c b/src/libImaging/PcxDecode.c index a65952fb1da..551505cfafa 100644 --- a/src/libImaging/PcxDecode.c +++ b/src/libImaging/PcxDecode.c @@ -15,6 +15,37 @@ #include "Imaging.h" +void +unpackP2L(UINT8 *out, const UINT8 *in, int pixels) { + int i, j, m, s; + /* bit layers */ + m = 128; + s = (pixels + 7) / 8; + for (i = j = 0; i < pixels; i++) { + out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0); + if ((m >>= 1) == 0) { + m = 128; + j++; + } + } +} + +void +unpackP4L(UINT8 *out, const UINT8 *in, int pixels) { + int i, j, m, s; + /* bit layers (trust the optimizer ;-) */ + m = 128; + s = (pixels + 7) / 8; + for (i = j = 0; i < pixels; i++) { + out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0) + + ((in[j + 2 * s] & m) ? 4 : 0) + ((in[j + 3 * s] & m) ? 8 : 0); + if ((m >>= 1) == 0) { + m = 128; + j++; + } + } +} + int ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t bytes) { UINT8 n; diff --git a/src/libImaging/PcxDecode.h b/src/libImaging/PcxDecode.h new file mode 100644 index 00000000000..674d04a8668 --- /dev/null +++ b/src/libImaging/PcxDecode.h @@ -0,0 +1,12 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * Declarations for PCX decoder. + */ + +extern void +unpackP2L(UINT8 *out, const UINT8 *in, int pixels); + +extern void +unpackP4L(UINT8 *out, const UINT8 *in, int pixels); diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index 203bcac2ca9..1a5c5bb81a0 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -32,6 +32,7 @@ #include "Imaging.h" #include "Convert.h" +#include "PcxDecode.h" #define R 0 #define G 1 @@ -548,37 +549,6 @@ unpackP4(UINT8 *out, const UINT8 *in, int pixels) { } } -static void -unpackP2L(UINT8 *out, const UINT8 *in, int pixels) { - int i, j, m, s; - /* bit layers */ - m = 128; - s = (pixels + 7) / 8; - for (i = j = 0; i < pixels; i++) { - out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0); - if ((m >>= 1) == 0) { - m = 128; - j++; - } - } -} - -static void -unpackP4L(UINT8 *out, const UINT8 *in, int pixels) { - int i, j, m, s; - /* bit layers (trust the optimizer ;-) */ - m = 128; - s = (pixels + 7) / 8; - for (i = j = 0; i < pixels; i++) { - out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0) + - ((in[j + 2 * s] & m) ? 4 : 0) + ((in[j + 3 * s] & m) ? 8 : 0); - if ((m >>= 1) == 0) { - m = 128; - j++; - } - } -} - /* Unpack to "RGB" image */ void