def read_png_or_jpeg2000(fobj, start_length, size): (start, length) = start_length fobj.seek(start) sig = fobj.read(12) if sig[:8] == b"\x89PNG\x0d\x0a\x1a\x0a": fobj.seek(start) im = PngImagePlugin.PngImageFile(fobj) Image._decompression_bomb_check(im.size) return {"RGBA": im} elif ( sig[:4] == b"\xff\x4f\xff\x51" or sig[:4] == b"\x0d\x0a\x87\x0a" or sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a" ): if not enable_jpeg2k: raise ValueError( "Unsupported icon subimage format (rebuild PIL " "with JPEG 2000 support to fix this)" ) # j2k, jpc or j2c fobj.seek(start) jp2kstream = fobj.read(length) f = io.BytesIO(jp2kstream) im = Jpeg2KImagePlugin.Jpeg2KImageFile(f) Image._decompression_bomb_check(im.size) if im.mode != "RGBA": im = im.convert("RGBA") return {"RGBA": im} else: raise ValueError("Unsupported icon subimage format")
def _open(self, pilmode=None, as_gray=False): Image = self.format._init_pillow() try: factory, accept = Image.OPEN[self.format.plugin_id] except KeyError: raise RuntimeError("Format %s cannot read images." % self.format.name) self._fp = self._get_file() self._im = factory(self._fp, "") if hasattr(Image, "_decompression_bomb_check"): Image._decompression_bomb_check(self._im.size) # Save the raw mode used by the palette for a BMP because it may not be the number of channels # When the data is read, imageio hands the palette to PIL to handle and clears the rawmode argument # However, there is a bug in PIL with handling animated GIFs with a different color palette on each frame. # This issue is resolved by using the raw palette data but the rawmode information is now lost. So we # store the raw mode for later use if self._im.palette and self._im.palette.dirty: self._im.palette.rawmode_saved = self._im.palette.rawmode pil_try_read(self._im) # Store args self._kwargs = dict(as_gray=as_gray, is_gray=_palette_is_grayscale(self._im)) # setting mode=None is not the same as just not providing it if pilmode is not None: self._kwargs["mode"] = pilmode # Set length self._length = 1 if hasattr(self._im, "n_frames"): self._length = self._im.n_frames
def _open(self, pilmode=None, as_gray=False): Image = self.format._init_pillow() try: factory, accept = Image.OPEN[self.format.plugin_id] except KeyError: raise RuntimeError("Format %s cannot read images." % self.format.name) self._fp = self._get_file() self._im = factory(self._fp, "") if hasattr(Image, "_decompression_bomb_check"): Image._decompression_bomb_check(self._im.size) # Save the raw mode used by the palette for a BMP because it may not be the number of channels # When the data is read, imageio hands the palette to PIL to handle and clears the rawmode argument # However, there is a bug in PIL with handling animated GIFs with a different color palette on each frame. # This issue is resolved by using the raw palette data but the rawmode information is now lost. So we # store the raw mode for later use if self._im.palette and self._im.palette.dirty: self._im.palette.rawmode_saved = self._im.palette.rawmode pil_try_read(self._im) # Store args self._kwargs = dict( as_gray=as_gray, is_gray=_palette_is_grayscale(self._im) ) # setting mode=None is not the same as just not providing it if pilmode is not None: self._kwargs["mode"] = pilmode # Set length self._length = 1 if hasattr(self._im, "n_frames"): self._length = self._im.n_frames
def seek(self, frame): "Select a given frame as current image" self._seek(max(frame, 0)) # Questionable backwards compatibility. # Create a new core image object on second and # subsequent frames in the image. Image may be # different size/mode. Image._decompression_bomb_check(self.size) self.im = Image.core.new(self.mode, self.size)
def seek(self, frame): "Select a given frame as current image" if frame < 0: frame = 0 self._seek(frame) # Create a new core image object on second and # subsequent frames in the image. Image may be # different size/mode. Image._decompression_bomb_check(self.size) self.im = Image.core.new(self.mode, self.size)
def _open(self, **kwargs): Image = self.format._init_pillow() try: factory, accept = Image.OPEN[self.format.plugin_id] except KeyError: raise RuntimeError('Format %s cannot read images.' % self.format.name) self._fp = self.request.get_file() self._im = factory(self._fp, '') if hasattr(Image, '_decompression_bomb_check'): Image._decompression_bomb_check(self._im.size) pil_try_read(self._im) self._grayscale = _palette_is_grayscale(self._im) # Set length self._length = 1 if hasattr(self._im, 'n_frames'): self._length = self._im.n_frames
def _open(self): s = self.fp.read(4) t = self.fp.read(4) if len(s) < 4 or len(t) < 4: raise InvalidFileType("not a GIMP brush") header_size = i32(s) version = i32(t) if header_size < 20: raise InvalidFileType("not a GIMP brush") if version not in (1, 2): raise NotImplementedError("Unsupported GIMP brush version: %s" % version) width = i32(self.fp.read(4)) height = i32(self.fp.read(4)) color_depth = i32(self.fp.read(4)) if width <= 0 or height <= 0: raise InvalidFileType("not a GIMP brush") if color_depth not in (1, 4): raise NotImplementedError("Unsupported GIMP brush color depth: %s" % color_depth) if version == 1: comment_length = header_size-20 else: comment_length = header_size-28 magic_number = self.fp.read(4) if magic_number != b'GIMP': raise InvalidFileType("not a GIMP brush, bad magic number") self.info['spacing'] = i32(self.fp.read(4)) comment = self.fp.read(comment_length)[:-1] if color_depth == 1: self.mode = "L" else: self.mode = 'RGBA' self.size = width, height self.info["comment"] = comment # Image might not be small Image._decompression_bomb_check(self.size) # Data is an uncompressed block of w * h * bytes/pixel self._data_size = width * height * color_depth
def _open(self, pilmode=None, as_gray=False): Image = self.format._init_pillow() try: factory, accept = Image.OPEN[self.format.plugin_id] except KeyError: raise RuntimeError("Format %s cannot read images." % self.format.name) self._fp = self._get_file() self._im = factory(self._fp, "") if hasattr(Image, "_decompression_bomb_check"): Image._decompression_bomb_check(self._im.size) pil_try_read(self._im) # Store args self._kwargs = dict( mode=pilmode, as_gray=as_gray, is_gray=_palette_is_grayscale(self._im) ) # Set length self._length = 1 if hasattr(self._im, "n_frames"): self._length = self._im.n_frames
def _open(self): header_size = i32(self.fp.read(4)) version = i32(self.fp.read(4)) if header_size < 20: raise SyntaxError("not a GIMP brush") if version not in (1, 2): raise SyntaxError("Unsupported GIMP brush version: %s" % version) width = i32(self.fp.read(4)) height = i32(self.fp.read(4)) color_depth = i32(self.fp.read(4)) if width <= 0 or height <= 0: raise SyntaxError("not a GIMP brush") if color_depth not in (1, 4): raise SyntaxError("Unsupported GMP brush color depth: %s" % color_depth) if version == 1: comment_length = header_size-20 else: comment_length = header_size-28 magic_number = self.fp.read(4) if magic_number != b'GIMP': raise SyntaxError("not a GIMP brush, bad magic number") self.info['spacing'] = i32(self.fp.read(4)) comment = self.fp.read(comment_length)[:-1] if color_depth == 1: self.mode = "L" else: self.mode = 'RGBA' self.size = width, height self.info["comment"] = comment # Image might not be small Image._decompression_bomb_check(self.size) # Data is an uncompressed block of w * h * bytes/pixel self._data_size = width * height * color_depth
def imopen(fp): # read header fields header = fp.read(32 + 24 + 32 + 12) size = i32(header, 32), i32(header, 36) offset = i32(header, 40) # load pixel data fp.seek(offset) Image._decompression_bomb_check(size) im = Image.frombytes("P", size, fp.read(size[0] * size[1])) im.putpalette(quake2palette) im.format = "WAL" im.format_description = "Quake2 Texture" # strings are null-terminated im.info["name"] = header[:32].split(b"\0", 1)[0] next_name = header[56:56 + 32].split(b"\0", 1)[0] if next_name: im.info["next_name"] = next_name return im
def imopen(fp): # read header fields header = fp.read(32+24+32+12) size = i32(header, 32), i32(header, 36) offset = i32(header, 40) # load pixel data fp.seek(offset) Image._decompression_bomb_check(size) im = Image.frombytes("P", size, fp.read(size[0] * size[1])) im.putpalette(quake2palette) im.format = "WAL" im.format_description = "Quake2 Texture" # strings are null-terminated im.info["name"] = header[:32].split(b"\0", 1)[0] next_name = header[56:56+32].split(b"\0", 1)[0] if next_name: im.info["next_name"] = next_name return im
def frame(self, idx): """ Get an image from frame idx """ header = self.entry[idx] self.buf.seek(header['offset']) data = self.buf.read(8) self.buf.seek(header['offset']) if data[:8] == PngImagePlugin._MAGIC: # png frame im = PngImagePlugin.PngImageFile(self.buf) else: # XOR + AND mask bmp frame im = BmpImagePlugin.DibImageFile(self.buf) Image._decompression_bomb_check(im.size) # change tile dimension to only encompass XOR image 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 # figure out where AND mask image starts mode = a[0] bpp = 8 for k in BmpImagePlugin.BIT2MODE.keys(): if mode == BmpImagePlugin.BIT2MODE[k][1]: bpp = k break if 32 == bpp: # 32-bit color depth icon image allows semitransparent areas # PIL's DIB format ignores transparency bits, recover them. # The DIB is packed in BGRX byte order where X is the alpha # channel. # Back up to start of bmp data self.buf.seek(o) # extract every 4th byte (eg. 3,7,11,15,...) alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4] # convert to an 8bpp grayscale image mask = Image.frombuffer( 'L', # 8bpp im.size, # (w, h) alpha_bytes, # source chars 'raw', # raw decoder ('L', 0, -1) # 8bpp inverted, unpadded, reversed ) else: # get AND image from end of bitmap w = im.size[0] if (w % 32) > 0: # bitmap row data is aligned to word boundaries w += 32 - (im.size[0] % 32) # the total mask data is # padded row size * height / bits per char and_mask_offset = o + int(im.size[0] * im.size[1] * (bpp / 8.0)) total_bytes = int((w * im.size[1]) / 8) self.buf.seek(and_mask_offset) maskData = self.buf.read(total_bytes) # convert raw data to image mask = Image.frombuffer( '1', # 1 bpp im.size, # (w, h) maskData, # source chars 'raw', # raw decoder ('1;I', int(w / 8), -1) # 1bpp inverted, padded, reversed ) # now we have two images, im is XOR image and mask is AND image # apply mask image as alpha channel im = im.convert('RGBA') im.putalpha(mask) return im
def _seek(self, frame): if frame == 0: # rewind self.__offset = 0 self.dispose = None self.dispose_extent = [0, 0, 0, 0] # x0, y0, x1, y1 self.__frame = -1 self.__fp.seek(self.__rewind) self._prev_im = None self.disposal_method = 0 else: # ensure that the previous frame was loaded if not self.im: self.load() if frame != self.__frame + 1: raise ValueError("cannot seek to frame %d" % frame) self.__frame = frame self.tile = [] self.fp = self.__fp if self.__offset: # backup to last frame self.fp.seek(self.__offset) while self.data(): pass self.__offset = 0 if self.dispose: self.im.paste(self.dispose, self.dispose_extent) from copy import copy self.palette = copy(self.global_palette) while True: s = self.fp.read(1) if not s or s == b";": break elif s == b"!": # # extensions # s = self.fp.read(1) block = self.data() if i8(s) == 249: # # graphic control extension # flags = i8(block[0]) if flags & 1: self.info["transparency"] = i8(block[3]) self.info["duration"] = i16(block[1:3]) * 10 # disposal method - find the value of bits 4 - 6 dispose_bits = 0b00011100 & flags dispose_bits = dispose_bits >> 2 if dispose_bits: # only set the dispose if it is not # unspecified. I'm not sure if this is # correct, but it seems to prevent the last # frame from looking odd for some animations self.disposal_method = dispose_bits elif i8(s) == 255: # # application extension # self.info["extension"] = block, self.fp.tell() if block[:11] == b"NETSCAPE2.0": block = self.data() if len(block) >= 3 and i8(block[0]) == 1: self.info["loop"] = i16(block[1:3]) while self.data(): pass elif s == b",": # # local image # s = self.fp.read(9) # extent x0, y0 = i16(s[0:]), i16(s[2:]) x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:]) self.dispose_extent = x0, y0, x1, y1 flags = i8(s[8]) interlace = (flags & 64) != 0 if flags & 128: bits = (flags & 7) + 1 self.palette =\ ImagePalette.raw("RGB", self.fp.read(3 << bits)) # image data bits = i8(self.fp.read(1)) self.__offset = self.fp.tell() self.tile = [("gif", (x0, y0, x1, y1), self.__offset, (bits, interlace))] break else: pass # raise IOError, "illegal GIF tag `%x`" % i8(s) try: if self.disposal_method < 2: # do not dispose or none specified self.dispose = None elif self.disposal_method == 2: # replace with background colour Image._decompression_bomb_check(self.size) self.dispose = Image.core.fill("P", self.size, self.info["background"]) else: # replace with previous contents if self.im: self.dispose = self.im.copy() # only dispose the extent in this frame if self.dispose: self.dispose = self.dispose.crop(self.dispose_extent) except (AttributeError, KeyError): pass if not self.tile: # self.__fp = None raise EOFError self.mode = "L" if self.palette: self.mode = "P"