delegate Image.mode and Image.size to the values on Image.im when available

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.
This commit is contained in:
Yay295 2023-08-05 15:00:45 -05:00
parent 95257dff87
commit 919dbbe1f1
7 changed files with 97 additions and 25 deletions

View File

@ -662,7 +662,7 @@ class TestImage:
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

View File

@ -328,8 +328,8 @@ class GifImageFile(ImageFile.ImageFile):
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:

View File

@ -261,11 +261,7 @@ class IcnsImageFile(ImageFile.ImageFile):
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:
@ -283,7 +279,10 @@ class IcnsImageFile(ImageFile.ImageFile):
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:
@ -306,7 +305,7 @@ class IcnsImageFile(ImageFile.ImageFile):
self.im = im.im
self._mode = im.mode
self.size = im.size
self._size = im.size
return px

View File

@ -310,36 +310,36 @@ class IcoImageFile(ImageFile.ImageFile):
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

View File

@ -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]
@ -502,10 +507,36 @@ class Image:
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

View File

@ -139,6 +139,9 @@ class ImageFile(Image.Image):
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)

View File

@ -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);
@ -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 */