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
from array import array
import pytest
from PIL import Image
from .helper import assert_image_equal, hopper
@ -47,6 +49,12 @@ def test_pypy_performance():
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():
src = hopper("L")
data = list(src.getdata())
@ -87,3 +95,18 @@ def test_array_F():
im.putdata(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):
"""
Copies pixel data to this image. This method copies data from a
sequence object into the image, starting at the upper left
corner (0, 0), and continuing until either the image or the
sequence ends. The scale and offset values are used to adjust
the sequence values: **pixel = value*scale + offset**.
Copies pixel data from a flattened sequence object into the image. The
values should start at the upper left corner (0, 0), continue to the
end of the line, followed directly by the first value of the second
line, and so on. Data will be read until either the image or the
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 offset: An optional offset value. The default is 0.0.
"""

View File

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