Skip to content

Commit

Permalink
delegate Image.mode and Image.size to the values on Image.im when ava…
Browse files Browse the repository at this point in the history
…ilable

When setting the values on Image also try to update the values on Image.im. There isn't currently a way to update values in the other direction.
  • Loading branch information
Yay295 committed Aug 5, 2023
1 parent 95257df commit 919dbbe
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 25 deletions.
2 changes: 1 addition & 1 deletion Tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ def test__new(self):
blank_pa.palette = None

def _make_new(base_image, im, palette_result=None):
new_im = base_image._new(im)
new_im = base_image._new(im.im)
assert new_im.mode == im.mode
assert new_im.size == im.size
assert new_im.info == base_image.info
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/GifImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,8 @@ def _seek(self, frame, update_image=True):
self._mode = "RGBA"
del self.info["transparency"]
else:
self._mode = "RGB"
self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG)
self._mode = "RGB"

def _rgb(color):
if self._frame_palette:
Expand Down
13 changes: 6 additions & 7 deletions src/PIL/IcnsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,7 @@ def _open(self):
self.best_size[1] * self.best_size[2],
)

@property
def size(self):
return self._size

@size.setter
@Image.Image.size.setter
def size(self, value):
info_size = value
if info_size not in self.info["sizes"] and len(info_size) == 2:
Expand All @@ -283,7 +279,10 @@ def size(self, value):
if info_size not in self.info["sizes"]:
msg = "This is not one of the allowed sizes of this image"
raise ValueError(msg)
self._size = value
if value != self.size:
self.im = None
self.pyaccess = None
self._size = value

def load(self):
if len(self.size) == 3:
Expand All @@ -306,7 +305,7 @@ def load(self):

self.im = im.im
self._mode = im.mode
self.size = im.size
self._size = im.size

return px

Expand Down
20 changes: 10 additions & 10 deletions src/PIL/IcoImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,36 +310,36 @@ def _open(self):
self.size = self.ico.entry[0]["dim"]
self.load()

@property
def size(self):
return self._size

@size.setter
@Image.Image.size.setter
def size(self, value):
if value not in self.info["sizes"]:
msg = "This is not one of the allowed sizes of this image"
raise ValueError(msg)
self._size = value
if value != self.size:
self.im = None
self.pyaccess = None
self._size = value

def load(self):
if self.im is not None and self.im.size == self.size:
# Already loaded
return Image.Image.load(self)
im = self.ico.getimage(self.size)
size_to_load = self.size
im = self.ico.getimage(size_to_load)
# if tile is PNG, it won't really be loaded yet
im.load()
self.im = im.im
self.pyaccess = None
self._mode = im.mode
if im.size != self.size:
if im.size != size_to_load:
warnings.warn("Image was not the expected size")

index = self.ico.getentryindex(self.size)
index = self.ico.getentryindex(size_to_load)
sizes = list(self.info["sizes"])
sizes[index] = im.size
self.info["sizes"] = set(sizes)

self.size = im.size
self._size = im.size

def load_seek(self):
# Flag the ImageFile.Parser so that it
Expand Down
37 changes: 34 additions & 3 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,16 +480,21 @@ class Image:

def __init__(self):
# FIXME: take "new" parameters / other image?
# FIXME: turn mode and size into delegating properties?
self.im = None
self._mode = ""
self._size = (0, 0)
# do not directly change __mode; use _mode instead
self.__mode = ""
# do not directly change __size; use _size instead
self.__size = (0, 0)
self.palette = None
self.info = {}
self.readonly = 0
self.pyaccess = None
self._exif = None

def _use_im_values(self):
''' Whether or not to try using values from self.im in addition to the values in this class. '''
return self.im is not None

@property
def width(self):
return self.size[0]
Expand All @@ -502,10 +507,36 @@ def height(self):
def size(self):
return self._size

@property
def _size(self):
if self._use_im_values():
return self.im.size
return self.__size

@_size.setter
def _size(self, value):
# set im.size first in case it raises an excepton
if self._use_im_values():
self.im.size = value
self.__size = value

@property
def mode(self):
return self._mode

@property
def _mode(self):
if self._use_im_values():
return self.im.mode
return self.__mode

@_mode.setter
def _mode(self, value):
# set im.mode first in case it raises an excepton
if self._use_im_values():
self.im.mode = value
self.__mode = value

def _new(self, im):
new = Image()
new.im = im
Expand Down
3 changes: 3 additions & 0 deletions src/PIL/ImageFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ def get_format_mimetype(self):
if self.format is not None:
return Image.MIME.get(self.format.upper())

def _use_im_values(self):
return self.tile is None and self.im is not None

def __setstate__(self, state):
self.tile = []
super().__setstate__(state)
Expand Down
45 changes: 42 additions & 3 deletions src/_imaging.c
Original file line number Diff line number Diff line change
Expand Up @@ -3646,11 +3646,49 @@ _getattr_mode(ImagingObject *self, void *closure) {
return PyUnicode_FromString(self->image->mode);
}

static int
_setattr_mode(ImagingObject *self, PyObject *value, void *closure) {
if (value == NULL) {
self->image->mode[0] = '\0';
return 0;
}

const char *mode = PyUnicode_AsUTF8(value);
if (mode == NULL) {
return -1;
}
if (strlen(mode) >= IMAGING_MODE_LENGTH) {
PyErr_SetString(PyExc_ValueError, "given mode name is too long");
return -1;
}

strcpy(self->image->mode, mode);
return 0;
}

static PyObject *
_getattr_size(ImagingObject *self, void *closure) {
return Py_BuildValue("ii", self->image->xsize, self->image->ysize);
}

static int
_setattr_size(ImagingObject *self, PyObject *value, void *closure) {
if (value == NULL) {
self->image->xsize = 0;
self->image->ysize = 0;
return 0;
}

int xsize, ysize;
if (!PyArg_ParseTuple(value, "ii", &xsize, &ysize)) {
return -1;
}

self->image->xsize = xsize;
self->image->ysize = ysize;
return 0;
}

static PyObject *
_getattr_bands(ImagingObject *self, void *closure) {
return PyLong_FromLong(self->image->bands);
Expand Down Expand Up @@ -3679,13 +3717,14 @@ _getattr_unsafe_ptrs(ImagingObject *self, void *closure) {
};

static struct PyGetSetDef getsetters[] = {
{"mode", (getter)_getattr_mode},
{"size", (getter)_getattr_size},
{"mode", (getter)_getattr_mode, (setter)_setattr_mode},
{"size", (getter)_getattr_size, (setter)_setattr_size},
{"bands", (getter)_getattr_bands},
{"id", (getter)_getattr_id},
{"ptr", (getter)_getattr_ptr},
{"unsafe_ptrs", (getter)_getattr_unsafe_ptrs},
{NULL}};
{NULL}
};

/* basic sequence semantics */

Expand Down

0 comments on commit 919dbbe

Please sign in to comment.