Changed Image size property to be read-only by default

This commit is contained in:
Andrew Murray 2018-09-30 12:58:02 +10:00
parent 34426f2558
commit 0adeb82e98
44 changed files with 94 additions and 60 deletions

View File

@ -56,9 +56,8 @@ class TestImage(PillowTestCase):
self.assertEqual(im.width, 1)
self.assertEqual(im.height, 2)
im.size = (3, 4)
self.assertEqual(im.width, 3)
self.assertEqual(im.height, 4)
with self.assertRaises(AttributeError) as e:
im.size = (3, 4)
def test_invalid_image(self):
if py3:

View File

@ -154,7 +154,7 @@ class MockImageFile(ImageFile.ImageFile):
def _open(self):
self.rawmode = 'RGBA'
self.mode = 'RGBA'
self.size = (200, 200)
self._size = (200, 200)
self.tile = [("MOCK", (xoff, yoff, xoff+xsize, yoff+ysize), 32, None)]

View File

@ -270,7 +270,7 @@ class BlpImageFile(ImageFile.ImageFile):
self._blp_alpha_encoding, = struct.unpack("<b", self.fp.read(1))
self._blp_mips, = struct.unpack("<b", self.fp.read(1))
self.size = struct.unpack("<II", self.fp.read(8))
self._size = struct.unpack("<II", self.fp.read(8))
if self.magic == b"BLP1":
# Only present for BLP1

View File

@ -143,7 +143,7 @@ class BmpImageFile(ImageFile.ImageFile):
file_info['header_size'])
# ------------------ Special case : header is reported 40, which
# ---------------------- is shorter than real size for bpp >= 16
self.size = file_info['width'], file_info['height']
self._size = file_info['width'], file_info['height']
# -------- If color count was not found in the header, compute from bits
file_info['colors'] = file_info['colors'] if file_info.get('colors', 0) else (1 << file_info['bits'])
# -------------------------------- Check abnormal values for DOS attacks

View File

@ -47,7 +47,7 @@ class BufrStubImageFile(ImageFile.StubImageFile):
# make something up
self.mode = "F"
self.size = 1, 1
self._size = 1, 1
loader = self._load()
if loader:

View File

@ -63,7 +63,7 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
self._bitmap(i32(m[12:]) + offset)
# patch up the bitmap height
self.size = self.size[0], self.size[1]//2
self._size = self.size[0], self.size[1]//2
d, e, o, a = self.tile[0]
self.tile[0] = d, (0, 0)+self.size, o, a

View File

@ -114,7 +114,7 @@ class DdsImageFile(ImageFile.ImageFile):
header = BytesIO(header_bytes)
flags, height, width = struct.unpack("<3I", header.read(12))
self.size = (width, height)
self._size = (width, height)
self.mode = "RGBA"
pitch, depth, mipmaps = struct.unpack("<3I", header.read(12))

View File

@ -217,7 +217,7 @@ class EpsImageFile(ImageFile.ImageFile):
box = None
self.mode = "RGB"
self.size = 1, 1 # FIXME: huh?
self._size = 1, 1 # FIXME: huh?
#
# Load EPS header
@ -244,7 +244,7 @@ class EpsImageFile(ImageFile.ImageFile):
# fields should be integers, but some drivers
# put floating point values there anyway.
box = [int(float(i)) for i in v.split()]
self.size = box[2] - box[0], box[3] - box[1]
self._size = box[2] - box[0], box[3] - box[1]
self.tile = [("eps", (0, 0) + self.size, offset,
(length, box))]
except Exception:
@ -293,7 +293,7 @@ class EpsImageFile(ImageFile.ImageFile):
except ValueError:
break
self.size = int(x), int(y)
self._size = int(x), int(y)
return
s = fp.readline().strip('\r\n')
@ -331,7 +331,7 @@ class EpsImageFile(ImageFile.ImageFile):
return
self.im = Ghostscript(self.tile, self.size, self.fp, scale)
self.mode = self.im.mode
self.size = self.im.size
self._size = self.im.size
self.tile = []
def load_seek(self, *args, **kwargs):

View File

@ -50,7 +50,7 @@ class FITSStubImageFile(ImageFile.StubImageFile):
# make something up
self.mode = "F"
self.size = 1, 1
self._size = 1, 1
loader = self._load()
if loader:

View File

@ -54,7 +54,7 @@ class FliImageFile(ImageFile.ImageFile):
# image characteristics
self.mode = "P"
self.size = i16(s[8:10]), i16(s[10:12])
self._size = i16(s[8:10]), i16(s[10:12])
# animation speed
duration = i32(s[16:20])

View File

@ -81,7 +81,7 @@ class FpxImageFile(ImageFile.ImageFile):
# size (highest resolution)
self.size = prop[0x1000002], prop[0x1000003]
self._size = prop[0x1000002], prop[0x1000003]
size = max(self.size)
i = 1

View File

@ -61,7 +61,7 @@ class FtexImageFile(ImageFile.ImageFile):
def _open(self):
magic = struct.unpack("<I", self.fp.read(4))
version = struct.unpack("<i", self.fp.read(4))
self.size = struct.unpack("<2i", self.fp.read(8))
self._size = struct.unpack("<2i", self.fp.read(8))
mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8))
self.mode = "RGB"

View File

@ -74,7 +74,7 @@ class GbrImageFile(ImageFile.ImageFile):
else:
self.mode = 'RGBA'
self.size = width, height
self._size = width, height
self.info["comment"] = comment

View File

@ -49,7 +49,7 @@ class GdImageFile(ImageFile.ImageFile):
raise SyntaxError("Not a valid GD 2.x .gd file")
self.mode = "L" # FIXME: "P"
self.size = i16(s[2:4]), i16(s[4:6])
self._size = i16(s[2:4]), i16(s[4:6])
trueColor = i8(s[6])
trueColorOffset = 2 if trueColor else 0

View File

@ -65,7 +65,7 @@ class GifImageFile(ImageFile.ImageFile):
raise SyntaxError("not a GIF file")
self.info["version"] = s[:6]
self.size = i16(s[6:]), i16(s[8:])
self._size = i16(s[6:]), i16(s[8:])
self.tile = []
flags = i8(s[10])
bits = (flags & 7) + 1

View File

@ -48,7 +48,7 @@ class GribStubImageFile(ImageFile.StubImageFile):
# make something up
self.mode = "F"
self.size = 1, 1
self._size = 1, 1
loader = self._load()
if loader:

View File

@ -47,7 +47,7 @@ class HDF5StubImageFile(ImageFile.StubImageFile):
# make something up
self.mode = "F"
self.size = 1, 1
self._size = 1, 1
loader = self._load()
if loader:

View File

@ -265,13 +265,33 @@ class IcnsImageFile(ImageFile.ImageFile):
def _open(self):
self.icns = IcnsFile(self.fp)
self.mode = 'RGBA'
self.info['sizes'] = self.icns.itersizes()
self.best_size = self.icns.bestsize()
self.size = (self.best_size[0] * self.best_size[2],
self.best_size[1] * self.best_size[2])
self.info['sizes'] = self.icns.itersizes()
# Just use this to see if it's loaded or not yet.
self.tile = ('',)
@property
def size(self):
return self._size
@size.setter
def size(self, value):
info_size = value
if info_size not in self.info['sizes'] and len(info_size) == 2:
info_size = (info_size[0], info_size[1], 1)
if info_size not in self.info['sizes'] and len(info_size) == 3 and \
info_size[2] == 1:
simple_sizes = [(size[0] * size[2], size[1] * size[2])
for size in self.info['sizes']]
if value in simple_sizes:
info_size = self.info['sizes'][simple_sizes.index(value)]
if info_size not in self.info['sizes']:
raise ValueError(
"This is not one of the allowed sizes of this image")
self._size = value
def load(self):
if len(self.size) == 3:
self.best_size = self.size

View File

@ -169,7 +169,7 @@ class IcoFile(object):
im = BmpImagePlugin.DibImageFile(self.buf)
# change tile dimension to only encompass XOR image
im.size = (im.size[0], int(im.size[1] / 2))
im._size = (im.size[0], int(im.size[1] / 2))
d, e, o, a = im.tile[0]
im.tile[0] = d, (0, 0) + im.size, o, a
@ -263,6 +263,17 @@ class IcoImageFile(ImageFile.ImageFile):
self.size = self.ico.entry[0]['dim']
self.load()
@property
def size(self):
return self._size
@size.setter
def size(self, value):
if value not in self.info['sizes']:
raise ValueError(
"This is not one of the allowed sizes of this image")
self._size = value
def load(self):
im = self.ico.getimage(self.size)
# if tile is PNG, it won't really be loaded yet

View File

@ -196,7 +196,7 @@ class ImImageFile(ImageFile.ImageFile):
raise SyntaxError("Not an IM file")
# Basic attributes
self.size = self.info[SIZE]
self._size = self.info[SIZE]
self.mode = self.info[MODE]
# Skip forward to start of image data

View File

@ -531,7 +531,7 @@ class Image(object):
# FIXME: turn mode and size into delegating properties?
self.im = None
self.mode = ""
self.size = (0, 0)
self._size = (0, 0)
self.palette = None
self.info = {}
self.category = NORMAL
@ -546,11 +546,15 @@ class Image(object):
def height(self):
return self.size[1]
@property
def size(self):
return self._size
def _new(self, im):
new = Image()
new.im = im
new.mode = im.mode
new.size = im.size
new._size = im.size
if im.mode in ('P', 'PA'):
if self.palette:
new.palette = self.palette.copy()
@ -698,7 +702,7 @@ class Image(object):
info, mode, size, palette, data = state
self.info = info
self.mode = mode
self.size = size
self._size = size
self.im = core.new(mode, size)
if mode in ("L", "P") and palette:
self.putpalette(palette)
@ -2104,7 +2108,7 @@ class Image(object):
self.im = im.im
self.mode = im.mode
self.size = size
self._size = size
self.readonly = 0
self.pyaccess = None

View File

@ -78,10 +78,10 @@ class ImtImageFile(ImageFile.ImageFile):
k, v = m.group(1, 2)
if k == "width":
xsize = int(v)
self.size = xsize, ysize
self._size = xsize, ysize
elif k == "height":
ysize = int(v)
self.size = xsize, ysize
self._size = xsize, ysize
elif k == "pixel" and v == "n8":
self.mode = "L"

View File

@ -118,7 +118,7 @@ class IptcImageFile(ImageFile.ImageFile):
self.mode = "CMYK"[id]
# size
self.size = self.getint((3, 20)), self.getint((3, 30))
self._size = self.getint((3, 20)), self.getint((3, 30))
# compression
try:

View File

@ -159,13 +159,13 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
sig = self.fp.read(4)
if sig == b'\xff\x4f\xff\x51':
self.codec = "j2k"
self.size, self.mode = _parse_codestream(self.fp)
self._size, self.mode = _parse_codestream(self.fp)
else:
sig = sig + self.fp.read(8)
if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a':
self.codec = "jp2"
self.size, self.mode = _parse_jp2_header(self.fp)
self._size, self.mode = _parse_jp2_header(self.fp)
else:
raise SyntaxError('not a JPEG 2000 file')
@ -198,8 +198,8 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
if self.reduce:
power = 1 << self.reduce
adjust = power >> 1
self.size = (int((self.size[0] + adjust) / power),
int((self.size[1] + adjust) / power))
self._size = (int((self.size[0] + adjust) / power),
int((self.size[1] + adjust) / power))
if self.tile:
# Update the reduce and layers settings

View File

@ -159,7 +159,7 @@ def SOF(self, marker):
n = i16(self.fp.read(2))-2
s = ImageFile._safe_read(self.fp, n)
self.size = i16(s[3:]), i16(s[1:])
self._size = i16(s[3:]), i16(s[1:])
self.bits = i8(s[0])
if self.bits != 8:
@ -390,7 +390,7 @@ class JpegImageFile(ImageFile.ImageFile):
if scale >= s:
break
e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1]
self.size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s)
self._size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s)
scale = s
self.tile = [(d, e, o, a)]
@ -423,7 +423,7 @@ class JpegImageFile(ImageFile.ImageFile):
pass
self.mode = self.im.mode
self.size = self.im.size
self._size = self.im.size
self.tile = []

View File

@ -59,7 +59,7 @@ class McIdasImageFile(ImageFile.ImageFile):
raise SyntaxError("unsupported McIdas format")
self.mode = mode
self.size = w[10], w[9]
self._size = w[10], w[9]
offset = w[34] + w[15]
stride = w[15] + w[10]*w[11]*w[14]

View File

@ -72,7 +72,7 @@ class MpegImageFile(ImageFile.ImageFile):
raise SyntaxError("not an MPEG file")
self.mode = "RGB"
self.size = s.read(12), s.read(12)
self._size = s.read(12), s.read(12)
# --------------------------------------------------------------------

View File

@ -63,7 +63,7 @@ class MspImageFile(ImageFile.ImageFile):
raise SyntaxError("bad MSP checksum")
self.mode = "1"
self.size = i16(s[4:]), i16(s[6:])
self._size = i16(s[4:]), i16(s[6:])
if s[:4] == b"DanM":
self.tile = [("raw", (0, 0)+self.size, 32, ("1", 0, 1))]

View File

@ -48,14 +48,14 @@ class PcdImageFile(ImageFile.ImageFile):
self.tile_post_rotate = -90
self.mode = "RGB"
self.size = 768, 512 # FIXME: not correct for rotated images!
self._size = 768, 512 # FIXME: not correct for rotated images!
self.tile = [("pcd", (0, 0)+self.size, 96*2048, None)]
def load_end(self):
if self.tile_post_rotate:
# Handle rotated PCDs
self.im = self.im.rotate(self.tile_post_rotate)
self.size = self.im.size
self._size = self.im.size
#

View File

@ -100,7 +100,7 @@ class PcxImageFile(ImageFile.ImageFile):
raise IOError("unknown PCX mode")
self.mode = mode
self.size = bbox[2]-bbox[0], bbox[3]-bbox[1]
self._size = bbox[2]-bbox[0], bbox[3]-bbox[1]
bbox = (0, 0) + self.size
logger.debug("size: %sx%s", *self.size)

View File

@ -50,7 +50,7 @@ class PixarImageFile(ImageFile.ImageFile):
# read rest of header
s = s + self.fp.read(508)
self.size = i16(s[418:420]), i16(s[416:418])
self._size = i16(s[418:420]), i16(s[416:418])
# get channel/depth descriptions
mode = i16(s[424:426]), i16(s[426:428])

View File

@ -577,7 +577,7 @@ class PngImageFile(ImageFile.ImageFile):
# (believe me, I've tried ;-)
self.mode = self.png.im_mode
self.size = self.png.im_size
self._size = self.png.im_size
self.info = self.png.im_info
self.text = self.png.im_text # experimental
self.tile = self.png.im_tile

View File

@ -107,7 +107,7 @@ class PpmImageFile(ImageFile.ImageFile):
self.mode = 'I'
rawmode = 'I;32B'
self.size = xsize, ysize
self._size = xsize, ysize
self.tile = [("raw",
(0, 0, xsize, ysize),
self.fp.tell(),

View File

@ -71,7 +71,7 @@ class PsdImageFile(ImageFile.ImageFile):
raise IOError("not enough channels")
self.mode = mode
self.size = i32(s[18:]), i32(s[14:])
self._size = i32(s[18:]), i32(s[14:])
#
# color mode data

View File

@ -96,7 +96,7 @@ class SgiImageFile(ImageFile.ImageFile):
if rawmode == "":
raise ValueError("Unsupported SGI image mode")
self.size = xsize, ysize
self._size = xsize, ysize
self.mode = rawmode.split(";")[0]
# orientation -1 : scanlines begins at the bottom-left corner

View File

@ -120,7 +120,7 @@ class SpiderImageFile(ImageFile.ImageFile):
if iform != 1:
raise SyntaxError("not a Spider 2D image")
self.size = int(h[12]), int(h[2]) # size in pixels (width, height)
self._size = int(h[12]), int(h[2]) # size in pixels (width, height)
self.istack = int(h[24])
self.imgnumber = int(h[27])

View File

@ -59,7 +59,7 @@ class SunImageFile(ImageFile.ImageFile):
offset = 32
self.size = i32(s[4:8]), i32(s[8:12])
self._size = i32(s[4:8]), i32(s[8:12])
depth = i32(s[12:16])
# data_length = i32(s[16:20]) # unreliable, ignore.

View File

@ -64,7 +64,7 @@ class TgaImageFile(ImageFile.ImageFile):
flags = i8(s[17])
self.size = i16(s[12:]), i16(s[14:])
self._size = i16(s[12:]), i16(s[14:])
# validate header fields
if colormaptype not in (0, 1) or\

View File

@ -1174,7 +1174,7 @@ class TiffImageFile(ImageFile.ImageFile):
# size
xsize = self.tag_v2.get(IMAGEWIDTH)
ysize = self.tag_v2.get(IMAGELENGTH)
self.size = xsize, ysize
self._size = xsize, ysize
if DEBUG:
print("- size:", self.size)

View File

@ -42,7 +42,7 @@ class WebPImageFile(ImageFile.ImageFile):
self.info["icc_profile"] = icc_profile
if exif:
self.info["exif"] = exif
self.size = width, height
self._size = width, height
self.fp = BytesIO(data)
self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
self._n_frames = 1
@ -55,7 +55,7 @@ class WebPImageFile(ImageFile.ImageFile):
# Get info from decoder
width, height, loop_count, bgcolor, frame_count, mode = \
self._decoder.get_info()
self.size = width, height
self._size = width, height
self.info["loop"] = loop_count
bg_a, bg_r, bg_g, bg_b = \
(bgcolor >> 24) & 0xFF, \

View File

@ -143,7 +143,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
raise SyntaxError("Unsupported file format")
self.mode = "RGB"
self.size = size
self._size = size
loader = self._load()
if loader:

View File

@ -65,7 +65,7 @@ class XVThumbImageFile(ImageFile.ImageFile):
s = s.strip().split()
self.mode = "P"
self.size = int(s[0]), int(s[1])
self._size = int(s[0]), int(s[1])
self.palette = ImagePalette.raw("RGB", PALETTE)

View File

@ -63,7 +63,7 @@ class XbmImageFile(ImageFile.ImageFile):
)
self.mode = "1"
self.size = xsize, ysize
self._size = xsize, ysize
self.tile = [("xbm", (0, 0)+self.size, m.end(), None)]

View File

@ -51,7 +51,7 @@ class XpmImageFile(ImageFile.ImageFile):
if m:
break
self.size = int(m.group(1)), int(m.group(2))
self._size = int(m.group(1)), int(m.group(2))
pal = int(m.group(3))
bpp = int(m.group(4))