def decode(self, buffer): global DEBUG tex = self.args[ 0] #it's not a "real" TexInfo, but the fields are the named the same, so duck typing saves the day! assert self.fd, "Something in PIL broke '_pulls_fd'! contact maintainer." buf = self.fd.read() bpp = bntx_extract.bpps[tex.format_ >> 8] if (tex.format_ >> 8) in bntx_extract.blk_dims: blkWidth, blkHeight = bntx_extract.blk_dims[tex.format_ >> 8] else: blkWidth, blkHeight = (1, 1) assert self.im is not None, "don't have an image before decode was called?" width, height = self.im.size size = swizzle.DIV_ROUND_UP(tex.width, blkWidth) * swizzle.DIV_ROUND_UP( tex.height, blkHeight) * bpp buf = swizzle.deswizzle(width, height, blkWidth, blkHeight, bpp, tex.tileMode, tex.alignment, tex.sizeRange, buf) buf = bytes(buf[:size]) if DEBUG: print("Swizzle size:", size) print("Output buffer size:", len(buf)) if pil: if (tex.format_ >> 8) > 0x19 and (tex.format_ >> 8) < 0x21: bcmode = ( tex.format_ >> 8 ) - 0x19 #yes, this actually works; 0x1a is BC1, 1b is BC2, etc. if DEBUG: print("BC[N] n-value:", bcmode) elif tex.format_ >> 8 in bntx_extract.BCn_formats: raise NotImplementedError( "Unknown BlockCompression format. contact maintainer with sample." ) if (tex.format_ >> 8) in bntx_extract.ASTC_formats: if astc: #do we have the optional codec? self.superdec = Image._getdecoder(self.mode, 'astc', (blkWidth, blkHeight)) else: raise KeyError( "This is an ASTC image, but the codec is not installed. please install 'astc_codec' with pip." ) else: self.superdec = Image._getdecoder(self.mode, 'bcn', (bcmode, )) self.superdec.setimage(self.im, self.state.extents()) s = self.superdec.decode(buf) if s[0] >= 0: raise ValueError("not enough image data") if s[1] != 0: raise ValueError("cannot decode image data") return s else: raise NotImplementedError( "FIXME: non-PIL decoding of BC[n] pixelformats")
def decode(self, buffer): raw_heif_image: RawHeifImage = self.args[0] mode = raw_heif_image.mode raw_decoder = Image._getdecoder(mode, 'raw', (mode, raw_heif_image.stride)) raw_decoder.setimage(self.im) return raw_decoder.decode(raw_heif_image.data)
def _load_libtiff(self): """ Overload method triggered when we detect a g3/g4 tiff Calls out to lib tiff """ pixel = Image.Image.load(self) if self.tile is None: raise IOError("cannot load this image") if not self.tile: return pixel self.load_prepare() if not len(self.tile) == 1: raise IOError("Not exactly one tile") d, e, o, a = self.tile[0] d = Image._getdecoder(self.mode, d, a, self.decoderconfig) try: d.setimage(self.im, e) except ValueError: raise IOError("Couldn't set the image") if hasattr(self.fp, "getvalue"): # We've got a stringio like thing passed in. Yay for all in memory. # The decoder needs the entire file in one shot, so there's not # a lot we can do here other than give it the entire file. # unless we could do something like get the address of the underlying # string for stringio. # # Rearranging for supporting byteio items, since they have a fileno # that returns an IOError if there's no underlying fp. Easier to deal # with here by reordering. if Image.DEBUG: print ("have getvalue. just sending in a string from getvalue") n,e = d.decode(self.fp.getvalue()) elif hasattr(self.fp, "fileno"): # we've got a actual file on disk, pass in the fp. if Image.DEBUG: print ("have fileno, calling fileno version of the decoder.") self.fp.seek(0) n,e = d.decode(b"fpfp") # 4 bytes, otherwise the trace might error out else: # we have something else. if Image.DEBUG: print ("don't have fileno or getvalue. just reading") # UNDONE -- so much for that buffer size thing. n, e = d.decode(self.fp.read()) self.tile = [] self.readonly = 0 self.fp = None # might be shared if e < 0: raise IOError(e) self.load_end() return Image.Image.load(self)
def _load_libtiff(self): """ Overload method triggered when we detect a compressed tiff Calls out to libtiff """ pixel = Image.Image.load(self) if self.tile is None: raise IOError("cannot load this image") if not self.tile: return pixel self.load_prepare() if not len(self.tile) == 1: raise IOError("Not exactly one tile") d, e, o, a = self.tile[0] d = Image._getdecoder(self.mode, 'libtiff', a, self.decoderconfig) try: d.setimage(self.im, e) except ValueError: raise IOError("Couldn't set the image") if hasattr(self.fp, "getvalue"): # We've got a stringio like thing passed in. Yay for all in memory. # The decoder needs the entire file in one shot, so there's not # a lot we can do here other than give it the entire file. # unless we could do something like get the address of the underlying # string for stringio. # # Rearranging for supporting byteio items, since they have a fileno # that returns an IOError if there's no underlying fp. Easier to deal # with here by reordering. if Image.DEBUG: print("have getvalue. just sending in a string from getvalue") n, e = d.decode(self.fp.getvalue()) elif hasattr(self.fp, "fileno"): # we've got a actual file on disk, pass in the fp. if Image.DEBUG: print("have fileno, calling fileno version of the decoder.") self.fp.seek(0) n, e = d.decode( b"fpfp") # 4 bytes, otherwise the trace might error out else: # we have something else. if Image.DEBUG: print("don't have fileno or getvalue. just reading") # UNDONE -- so much for that buffer size thing. n, e = d.decode(self.fp.read()) self.tile = [] self.readonly = 0 self.fp = None # might be shared if e < 0: raise IOError(e) self.load_end() return Image.Image.load(self)
def _set_as_jpeg(self, data, mode): from PIL.JpegImagePlugin import RAWMODE d = Image._getdecoder(mode, "jpeg", (RAWMODE[mode], "")) d.setimage(self.im, self.state.extents()) s = d.decode(data) if s[0] >= 0: raise GalImageError("not enough image data") if s[1] != 0: raise GalImageError("cannot decode image data")
def _set_as_raw(self, data, mode, rawmode=None, stride=0): # override PIL set_as_raw() so we can set stride if not rawmode: rawmode = mode d = Image._getdecoder(mode, "raw", (rawmode, stride, 1)) d.setimage(self.im, self.state.extents()) s = d.decode(data) if s[0] >= 0: raise GalImageError("not enough image data") if s[1] != 0: raise GalImageError("cannot decode image data")
def _load_libtiff(self): """ Overload method triggered when we detect a compressed tiff Calls out to libtiff """ pixel = Image.Image.load(self) if self.tile is None: raise IOError("cannot load this image") if not self.tile: return pixel self.load_prepare() if not len(self.tile) == 1: raise IOError("Not exactly one tile") # (self._compression, (extents tuple), # 0, (rawmode, self._compression, fp)) extents = self.tile[0][1] args = self.tile[0][3] + (self.tag_v2.offset,) decoder = Image._getdecoder(self.mode, 'libtiff', args, self.decoderconfig) try: decoder.setimage(self.im, extents) except ValueError: raise IOError("Couldn't set the image") if hasattr(self.fp, "getvalue"): # We've got a stringio like thing passed in. Yay for all in memory. # The decoder needs the entire file in one shot, so there's not # a lot we can do here other than give it the entire file. # unless we could do something like get the address of the # underlying string for stringio. # # Rearranging for supporting byteio items, since they have a fileno # that returns an IOError if there's no underlying fp. Easier to # deal with here by reordering. if DEBUG: print("have getvalue. just sending in a string from getvalue") n, err = decoder.decode(self.fp.getvalue()) elif hasattr(self.fp, "fileno"): # we've got a actual file on disk, pass in the fp. if DEBUG: print("have fileno, calling fileno version of the decoder.") self.fp.seek(0) # 4 bytes, otherwise the trace might error out n, err = decoder.decode(b"fpfp") else: # we have something else. if DEBUG: print("don't have fileno or getvalue. just reading") # UNDONE -- so much for that buffer size thing. n, err = decoder.decode(self.fp.read()) self.tile = [] self.readonly = 0 # libtiff closed the fp in a, we need to close self.fp, if possible if hasattr(self.fp, 'close'): if not self.__next: self.fp.close() self.fp = None # might be shared if err < 0: raise IOError(err) self.load_end() return Image.Image.load(self)
def load(self): "Load image data based on tile list" pixel = Image.Image.load(self) if self.tile is None: raise IOError("cannot load this image") if not self.tile: return pixel self.map = None use_mmap = self.filename and len(self.tile) == 1 # As of pypy 2.1.0, memory mapping was failing here. use_mmap = use_mmap and not hasattr(sys, 'pypy_version_info') readonly = 0 # look for read/seek overrides try: read = self.load_read # don't use mmap if there are custom read/seek functions use_mmap = False except AttributeError: read = self.fp.read try: seek = self.load_seek use_mmap = False except AttributeError: seek = self.fp.seek if use_mmap: # try memory mapping d, e, o, a = self.tile[0] if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES: try: if hasattr(Image.core, "map"): # use built-in mapper self.map = Image.core.map(self.filename) self.map.seek(o) self.im = self.map.readimage(self.mode, self.size, a[1], a[2]) else: # use mmap, if possible import mmap fp = open(self.filename, "r+") size = os.path.getsize(self.filename) # FIXME: on Unix, use PROT_READ etc self.map = mmap.mmap(fp.fileno(), size) self.im = Image.core.map_buffer( self.map, self.size, d, e, o, a) readonly = 1 except (AttributeError, EnvironmentError, ImportError): self.map = None self.load_prepare() if not self.map: # sort tiles in file order self.tile.sort(key=_tilesort) try: # FIXME: This is a hack to handle TIFF's JpegTables tag. prefix = self.tile_prefix except AttributeError: prefix = b"" # Buffer length read; assign a default value t = 0 for d, e, o, a in self.tile: d = Image._getdecoder(self.mode, d, a, self.decoderconfig) seek(o) try: d.setimage(self.im, e) except ValueError: continue b = prefix t = len(b) while True: try: s = read(self.decodermaxblock) except IndexError as ie: # truncated png/gif if LOAD_TRUNCATED_IMAGES: break else: raise IndexError(ie) if not s and not d.handles_eof: # truncated jpeg self.tile = [] # JpegDecode needs to clean things up here either way # If we don't destroy the decompressor, # we have a memory leak. d.cleanup() if LOAD_TRUNCATED_IMAGES: break else: raise IOError("image file is truncated " "(%d bytes not processed)" % len(b)) b = b + s n, e = d.decode(b) if n < 0: break b = b[n:] t = t + n # Need to cleanup here to prevent leaks in PyPy d.cleanup() self.tile = [] self.readonly = readonly self.fp = None # might be shared if not self.map and (not LOAD_TRUNCATED_IMAGES or t == 0) and e < 0: # still raised if decoder fails to return anything raise_ioerror(e) # post processing if hasattr(self, "tile_post_rotate"): # FIXME: This is a hack to handle rotated PCD's self.im = self.im.rotate(self.tile_post_rotate) self.size = self.im.size self.load_end() return Image.Image.load(self)
def feed(self, data): """ (Consumer) Feed data to the parser. :param data: A string buffer. :exception IOError: If the parser failed to parse the image file. """ # collect data if self.finished: return if self.data is None: self.data = data else: self.data = self.data + data # parse what we have if self.decoder: if self.offset > 0: # skip header skip = min(len(self.data), self.offset) self.data = self.data[skip:] self.offset = self.offset - skip if self.offset > 0 or not self.data: return n, e = self.decoder.decode(self.data) if n < 0: # end of stream self.data = None self.finished = 1 if e < 0: # decoding error self.image = None raise_ioerror(e) else: # end of image return self.data = self.data[n:] elif self.image: # if we end up here with no decoder, this file cannot # be incrementally parsed. wait until we've gotten all # available data pass else: # attempt to open this file try: try: fp = io.BytesIO(self.data) im = Image.open(fp) finally: fp.close() # explicitly close the virtual file except IOError: # traceback.print_exc() pass # not enough data else: flag = hasattr(im, "load_seek") or hasattr(im, "load_read") if flag or len(im.tile) != 1: # custom load code, or multiple tiles self.decode = None else: # initialize decoder im.load_prepare() d, e, o, a = im.tile[0] im.tile = [] self.decoder = Image._getdecoder( im.mode, d, a, im.decoderconfig ) self.decoder.setimage(im.im, e) # calculate decoder offset self.offset = o if self.offset <= len(self.data): self.data = self.data[self.offset:] self.offset = 0 self.image = im
def load(self): "Load image data based on tile list" pixel = Image.Image.load(self) if self.tile is None: raise IOError("cannot load this image") if not self.tile: return pixel self.map = None use_mmap = self.filename and len(self.tile) == 1 # As of pypy 2.1.0, memory mapping was failing here. use_mmap = use_mmap and not hasattr(sys, 'pypy_version_info') readonly = 0 # look for read/seek overrides try: read = self.load_read # don't use mmap if there are custom read/seek functions use_mmap = False except AttributeError: read = self.fp.read try: seek = self.load_seek use_mmap = False except AttributeError: seek = self.fp.seek if use_mmap: # try memory mapping d, e, o, a = self.tile[0] if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES: try: if hasattr(Image.core, "map"): # use built-in mapper self.map = Image.core.map(self.filename) self.map.seek(o) self.im = self.map.readimage( self.mode, self.size, a[1], a[2] ) else: # use mmap, if possible import mmap fp = open(self.filename, "r+") size = os.path.getsize(self.filename) # FIXME: on Unix, use PROT_READ etc self.map = mmap.mmap(fp.fileno(), size) self.im = Image.core.map_buffer( self.map, self.size, d, e, o, a ) readonly = 1 except (AttributeError, EnvironmentError, ImportError): self.map = None self.load_prepare() if not self.map: # sort tiles in file orders self.tile.sort(key=_tilesort) try: # FIXME: This is a hack to handle TIFF's JpegTables tag. prefix = self.tile_prefix except AttributeError: prefix = b"" for d, e, o, a in self.tile: d = Image._getdecoder(self.mode, d, a, self.decoderconfig) seek(o) try: d.setimage(self.im, e) except ValueError: continue b = prefix while True: try: s = read(self.decodermaxblock) except (IndexError, struct.error): # truncated png/gif if LOAD_TRUNCATED_IMAGES: break else: raise IOError("image file is truncated") if not s and not d.handles_eof: # truncated jpeg self.tile = [] # JpegDecode needs to clean things up here either way # If we don't destroy the decompressor, # we have a memory leak. d.cleanup() if LOAD_TRUNCATED_IMAGES: break else: raise IOError("image file is truncated " "(%d bytes not processed)" % len(b)) b = b + s n, e = d.decode(b) if n < 0: break b = b[n:] # Need to cleanup here to prevent leaks in PyPy d.cleanup() self.tile = [] self.readonly = readonly self.fp = None # might be shared if not self.map and not LOAD_TRUNCATED_IMAGES and e < 0: # still raised if decoder fails to return anything raise_ioerror(e) # post processing if hasattr(self, "tile_post_rotate"): # FIXME: This is a hack to handle rotated PCD's self.im = self.im.rotate(self.tile_post_rotate) self.size = self.im.size self.load_end() return Image.Image.load(self)
def load(self): "Load image data based on tile list" pixel = Image.Image.load(self) if self.tile is None: raise IOError("cannot load this image") if not self.tile: return pixel self.map = None readonly = 0 if self.filename and len(self.tile) == 1: # try memory mapping d, e, o, a = self.tile[0] if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES: try: if hasattr(Image.core, "map"): # use built-in mapper self.map = Image.core.map(self.filename) self.map.seek(o) self.im = self.map.readimage(self.mode, self.size, a[1], a[2]) else: # use mmap, if possible import mmap file = open(self.filename, "r+") size = os.path.getsize(self.filename) # FIXME: on Unix, use PROT_READ etc self.map = mmap.mmap(file.fileno(), size) self.im = Image.core.map_buffer( self.map, self.size, d, e, o, a) readonly = 1 except (AttributeError, EnvironmentError, ImportError): self.map = None self.load_prepare() # look for read/seek overrides try: read = self.load_read except AttributeError: read = self.fp.read try: seek = self.load_seek except AttributeError: seek = self.fp.seek if not self.map: # sort tiles in file order self.tile.sort(key=_tilesort) try: # FIXME: This is a hack to handle TIFF's JpegTables tag. prefix = self.tile_prefix except AttributeError: prefix = b"" for d, e, o, a in self.tile: d = Image._getdecoder(self.mode, d, a, self.decoderconfig) seek(o) try: d.setimage(self.im, e) except ValueError: continue b = prefix t = len(b) while True: try: s = read(self.decodermaxblock) except IndexError as ie: # truncated png/gif if LOAD_TRUNCATED_IMAGES: break else: raise IndexError(ie) if not s: # truncated jpeg self.tile = [] if LOAD_TRUNCATED_IMAGES: break else: raise IOError( "image file is truncated (%d bytes not processed)" % len(b)) b = b + s n, e = d.decode(b) if n < 0: break b = b[n:] t = t + n self.tile = [] self.readonly = readonly self.fp = None # might be shared if (not LOAD_TRUNCATED_IMAGES or t == 0) and not self.map and e < 0: # still raised if decoder fails to return anything raise_ioerror(e) # post processing if hasattr(self, "tile_post_rotate"): # FIXME: This is a hack to handle rotated PCD's self.im = self.im.rotate(self.tile_post_rotate) self.size = self.im.size self.load_end() return Image.Image.load(self)
def _load_libtiff(self): """ Overload method triggered when we detect a compressed tiff Calls out to libtiff """ pixel = Image.Image.load(self) if self.tile is None: raise IOError("cannot load this image") if not self.tile: return pixel self.load_prepare() if not len(self.tile) == 1: raise IOError("Not exactly one tile") # (self._compression, (extents tuple), # 0, (rawmode, self._compression, fp)) ignored, extents, ignored_2, args = self.tile[0] args = args + (self.ifd.offset, ) decoder = Image._getdecoder(self.mode, 'libtiff', args, self.decoderconfig) try: decoder.setimage(self.im, extents) except ValueError: raise IOError("Couldn't set the image") if hasattr(self.fp, "getvalue"): # We've got a stringio like thing passed in. Yay for all in memory. # The decoder needs the entire file in one shot, so there's not # a lot we can do here other than give it the entire file. # unless we could do something like get the address of the # underlying string for stringio. # # Rearranging for supporting byteio items, since they have a fileno # that returns an IOError if there's no underlying fp. Easier to # dea. with here by reordering. if Image.DEBUG: print("have getvalue. just sending in a string from getvalue") n, err = decoder.decode(self.fp.getvalue()) elif hasattr(self.fp, "fileno"): # we've got a actual file on disk, pass in the fp. if Image.DEBUG: print("have fileno, calling fileno version of the decoder.") self.fp.seek(0) # 4 bytes, otherwise the trace might error out n, err = decoder.decode(b"fpfp") else: # we have something else. if Image.DEBUG: print("don't have fileno or getvalue. just reading") # UNDONE -- so much for that buffer size thing. n, err = decoder.decode(self.fp.read()) self.tile = [] self.readonly = 0 # libtiff closed the fp in a, we need to close self.fp, if possible if hasattr(self.fp, 'close'): if not self.__next: self.fp.close() self.fp = None # might be shared if err < 0: raise IOError(err) self.load_end() return Image.Image.load(self)
def load(self): "Load image data based on tile list" pixel = Image.Image.load(self) if self.tile is None: raise IOError("cannot load this image") if not self.tile: return pixel self.map = None use_mmap = self.filename and len(self.tile) == 1 # As of pypy 2.1.0, memory mapping was failing here. use_mmap = use_mmap and not hasattr(sys, 'pypy_version_info') readonly = 0 # look for read/seek overrides try: read = self.load_read # don't use mmap if there are custom read/seek functions use_mmap = False except AttributeError: read = self.fp.read try: seek = self.load_seek use_mmap = False except AttributeError: seek = self.fp.seek if use_mmap: # try memory mapping decoder_name, extents, offset, args = self.tile[0] if decoder_name == "raw" and len(args) >= 3 and args[0] == self.mode \ and args[0] in Image._MAPMODES: try: if hasattr(Image.core, "map"): # use built-in mapper WIN32 only self.map = Image.core.map(self.filename) self.map.seek(offset) self.im = self.map.readimage( self.mode, self.size, args[1], args[2] ) else: # use mmap, if possible import mmap fp = open(self.filename, "r") size = os.path.getsize(self.filename) self.map = mmap.mmap(fp.fileno(), size, access=mmap.ACCESS_READ) self.im = Image.core.map_buffer( self.map, self.size, decoder_name, extents, offset, args ) readonly = 1 # After trashing self.im, we might need to reload the palette data. if self.palette: self.palette.dirty = 1 except (AttributeError, EnvironmentError, ImportError): self.map = None self.load_prepare() if not self.map: # sort tiles in file order self.tile.sort(key=_tilesort) try: # FIXME: This is a hack to handle TIFF's JpegTables tag. prefix = self.tile_prefix except AttributeError: prefix = b"" for decoder_name, extents, offset, args in self.tile: decoder = Image._getdecoder(self.mode, decoder_name, args, self.decoderconfig) seek(offset) try: decoder.setimage(self.im, extents) except ValueError: continue if decoder.pulls_fd: decoder.setfd(self.fp) status, err_code = decoder.decode(b"") else: b = prefix while True: try: s = read(self.decodermaxblock) except (IndexError, struct.error): # truncated png/gif if LOAD_TRUNCATED_IMAGES: break else: raise IOError("image file is truncated") if not s: # truncated jpeg self.tile = [] # JpegDecode needs to clean things up here either way # If we don't destroy the decompressor, # we have a memory leak. decoder.cleanup() if LOAD_TRUNCATED_IMAGES: break else: raise IOError("image file is truncated " "(%d bytes not processed)" % len(b)) b = b + s n, err_code = decoder.decode(b) if n < 0: break b = b[n:] # Need to cleanup here to prevent leaks in PyPy decoder.cleanup() self.tile = [] self.readonly = readonly self.fp = None # might be shared if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0: # still raised if decoder fails to return anything raise_ioerror(err_code) self.load_end() return Image.Image.load(self)
def decode(self, buffer): heif_file = self.args[0] mode = heif_file.mode raw_decoder = Image._getdecoder(mode, 'raw', (mode, heif_file.stride)) raw_decoder.setimage(self.im) return raw_decoder.decode(heif_file.data)
def load(self): "Load image data based on tile list" pixel = Image.Image.load(self) if self.tile is None: raise IOError("cannot load this image") if not self.tile: return pixel self.map = None readonly = 0 if self.filename and len(self.tile) == 1: # try memory mapping d, e, o, a = self.tile[0] if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES: try: if hasattr(Image.core, "map"): # use built-in mapper self.map = Image.core.map(self.filename) self.map.seek(o) self.im = self.map.readimage( self.mode, self.size, a[1], a[2] ) else: # use mmap, if possible import mmap file = open(self.filename, "r+") size = os.path.getsize(self.filename) # FIXME: on Unix, use PROT_READ etc self.map = mmap.mmap(file.fileno(), size) self.im = Image.core.map_buffer( self.map, self.size, d, e, o, a ) readonly = 1 except (AttributeError, EnvironmentError, ImportError): self.map = None self.load_prepare() # look for read/seek overrides try: read = self.load_read except AttributeError: read = self.fp.read try: seek = self.load_seek except AttributeError: seek = self.fp.seek if not self.map: # sort tiles in file order self.tile.sort(key=_tilesort) try: # FIXME: This is a hack to handle TIFF's JpegTables tag. prefix = self.tile_prefix except AttributeError: prefix = b"" for d, e, o, a in self.tile: d = Image._getdecoder(self.mode, d, a, self.decoderconfig) seek(o) try: d.setimage(self.im, e) except ValueError: continue b = prefix t = len(b) while True: try: s = read(self.decodermaxblock) except IndexError as ie: # truncated png/gif if LOAD_TRUNCATED_IMAGES: break else: raise IndexError(ie) if not s: # truncated jpeg self.tile = [] if LOAD_TRUNCATED_IMAGES: break else: raise IOError("image file is truncated (%d bytes not processed)" % len(b)) b = b + s n, e = d.decode(b) if n < 0: break b = b[n:] t = t + n self.tile = [] self.readonly = readonly self.fp = None # might be shared if (not LOAD_TRUNCATED_IMAGES or t == 0) and not self.map and e < 0: # still raised if decoder fails to return anything raise_ioerror(e) # post processing if hasattr(self, "tile_post_rotate"): # FIXME: This is a hack to handle rotated PCD's self.im = self.im.rotate(self.tile_post_rotate) self.size = self.im.size self.load_end() return Image.Image.load(self)