Merge pull request #5910 from radarhere/putdata

Improved putdata() documentation and data handling
This commit is contained in:
Hugo van Kemenade 2021-12-28 09:53:33 +02:00 committed by GitHub
commit 8e631e4cd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 15 deletions

View File

@ -1,6 +1,8 @@
import sys import sys
from array import array from array import array
import pytest
from PIL import Image from PIL import Image
from .helper import assert_image_equal, hopper from .helper import assert_image_equal, hopper
@ -47,6 +49,12 @@ def test_pypy_performance():
im.putdata(list(range(256)) * 256) im.putdata(list(range(256)) * 256)
def test_mode_with_L_with_float():
im = Image.new("L", (1, 1), 0)
im.putdata([2.0])
assert im.getpixel((0, 0)) == 2
def test_mode_i(): def test_mode_i():
src = hopper("L") src = hopper("L")
data = list(src.getdata()) data = list(src.getdata())
@ -87,3 +95,18 @@ def test_array_F():
im.putdata(arr) im.putdata(arr)
assert len(im.getdata()) == len(arr) assert len(im.getdata()) == len(arr)
def test_not_flattened():
im = Image.new("L", (1, 1))
with pytest.raises(TypeError):
im.putdata([[0]])
with pytest.raises(TypeError):
im.putdata([[0]], 2)
with pytest.raises(TypeError):
im = Image.new("I", (1, 1))
im.putdata([[0]])
with pytest.raises(TypeError):
im = Image.new("F", (1, 1))
im.putdata([[0]])

View File

@ -1705,13 +1705,14 @@ class Image:
def putdata(self, data, scale=1.0, offset=0.0): def putdata(self, data, scale=1.0, offset=0.0):
""" """
Copies pixel data to this image. This method copies data from a Copies pixel data from a flattened sequence object into the image. The
sequence object into the image, starting at the upper left values should start at the upper left corner (0, 0), continue to the
corner (0, 0), and continuing until either the image or the end of the line, followed directly by the first value of the second
sequence ends. The scale and offset values are used to adjust line, and so on. Data will be read until either the image or the
the sequence values: **pixel = value*scale + offset**. sequence ends. The scale and offset values are used to adjust the
sequence values: **pixel = value*scale + offset**.
:param data: A sequence object. :param data: A flattened sequence object.
:param scale: An optional scale value. The default is 1.0. :param scale: An optional scale value. The default is 1.0.
:param offset: An optional offset value. The default is 0.0. :param offset: An optional offset value. The default is 0.0.
""" """

View File

@ -1494,6 +1494,14 @@ _putdata(ImagingObject *self, PyObject *args) {
return NULL; return NULL;
} }
#define set_value_to_item(seq, i) \
op = PySequence_Fast_GET_ITEM(seq, i); \
if (PySequence_Check(op)) { \
PyErr_SetString(PyExc_TypeError, "sequence must be flattened"); \
return NULL; \
} else { \
value = PyFloat_AsDouble(op); \
}
if (image->image8) { if (image->image8) {
if (PyBytes_Check(data)) { if (PyBytes_Check(data)) {
unsigned char *p; unsigned char *p;
@ -1522,11 +1530,12 @@ _putdata(ImagingObject *self, PyObject *args) {
PyErr_SetString(PyExc_TypeError, must_be_sequence); PyErr_SetString(PyExc_TypeError, must_be_sequence);
return NULL; return NULL;
} }
double value;
if (scale == 1.0 && offset == 0.0) { if (scale == 1.0 && offset == 0.0) {
/* Clipped data */ /* Clipped data */
for (i = x = y = 0; i < n; i++) { for (i = x = y = 0; i < n; i++) {
op = PySequence_Fast_GET_ITEM(seq, i); set_value_to_item(seq, i);
image->image8[y][x] = (UINT8)CLIP8(PyLong_AsLong(op)); image->image8[y][x] = (UINT8)CLIP8(value);
if (++x >= (int)image->xsize) { if (++x >= (int)image->xsize) {
x = 0, y++; x = 0, y++;
} }
@ -1535,9 +1544,8 @@ _putdata(ImagingObject *self, PyObject *args) {
} else { } else {
/* Scaled and clipped data */ /* Scaled and clipped data */
for (i = x = y = 0; i < n; i++) { for (i = x = y = 0; i < n; i++) {
PyObject *op = PySequence_Fast_GET_ITEM(seq, i); set_value_to_item(seq, i);
image->image8[y][x] = image->image8[y][x] = CLIP8(value * scale + offset);
CLIP8((int)(PyFloat_AsDouble(op) * scale + offset));
if (++x >= (int)image->xsize) { if (++x >= (int)image->xsize) {
x = 0, y++; x = 0, y++;
} }
@ -1555,9 +1563,10 @@ _putdata(ImagingObject *self, PyObject *args) {
switch (image->type) { switch (image->type) {
case IMAGING_TYPE_INT32: case IMAGING_TYPE_INT32:
for (i = x = y = 0; i < n; i++) { for (i = x = y = 0; i < n; i++) {
op = PySequence_Fast_GET_ITEM(seq, i); double value;
set_value_to_item(seq, i);
IMAGING_PIXEL_INT32(image, x, y) = IMAGING_PIXEL_INT32(image, x, y) =
(INT32)(PyFloat_AsDouble(op) * scale + offset); (INT32)(value * scale + offset);
if (++x >= (int)image->xsize) { if (++x >= (int)image->xsize) {
x = 0, y++; x = 0, y++;
} }
@ -1566,9 +1575,10 @@ _putdata(ImagingObject *self, PyObject *args) {
break; break;
case IMAGING_TYPE_FLOAT32: case IMAGING_TYPE_FLOAT32:
for (i = x = y = 0; i < n; i++) { for (i = x = y = 0; i < n; i++) {
op = PySequence_Fast_GET_ITEM(seq, i); double value;
set_value_to_item(seq, i);
IMAGING_PIXEL_FLOAT32(image, x, y) = IMAGING_PIXEL_FLOAT32(image, x, y) =
(FLOAT32)(PyFloat_AsDouble(op) * scale + offset); (FLOAT32)(value * scale + offset);
if (++x >= (int)image->xsize) { if (++x >= (int)image->xsize) {
x = 0, y++; x = 0, y++;
} }