Example #1
0
    def putpalette(self, data, rawmode="RGB"):
        """
        Attaches a palette to this image.  The image must be a "P" or
        "L" image, and the palette sequence must contain 768 integer
        values, where each group of three values represent the red,
        green, and blue values for the corresponding pixel
        index. Instead of an integer sequence, you can use an 8-bit
        string.

        :param data: A palette sequence (either a list or a string).
        """
        from homework import Sjn_palette

        if self.mode not in ("L", "P"):
            raise ValueError("illegal image mode")
        self.load()
        if isinstance(data, ImagePalette.ImagePalette):
            palette = ImagePalette.raw(data.rawmode, data.palette)
        else:
            if not isinstance(data, bytes):
                if bytes is str:
                    data = "".join(chr(x) for x in data)
                else:
                    data = bytes(data)
            palette = ImagePalette.raw(rawmode, data)
        self.mode = "P"
        self.palette = palette
        self.palette.mode = "RGB"
        self.load()  # install new palette
Example #2
0
    def _open(self):

        # header
        s = self.fp.read(128)
        if not _accept(s):
            raise SyntaxError("not a PCX file")

        # image
        bbox = i16(s, 4), i16(s, 6), i16(s, 8)+1, i16(s, 10)+1
        if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]:
            raise SyntaxError("bad PCX image size")
        logger.debug("BBox: %s %s %s %s", *bbox)

        # format
        version = i8(s[1])
        bits = i8(s[3])
        planes = i8(s[65])
        stride = i16(s, 66)
        logger.debug("PCX version %s, bits %s, planes %s, stride %s",
                     version, bits, planes, stride)

        self.info["dpi"] = i16(s, 12), i16(s, 14)

        if bits == 1 and planes == 1:
            mode = rawmode = "1"

        elif bits == 1 and planes in (2, 4):
            mode = "P"
            rawmode = "P;%dL" % planes
            self.palette = ImagePalette.raw("RGB", s[16:64])

        elif version == 5 and bits == 8 and planes == 1:
            mode = rawmode = "L"
            # FIXME: hey, this doesn't work with the incremental loader !!!
            self.fp.seek(-769, 2)
            s = self.fp.read(769)
            if len(s) == 769 and i8(s[0]) == 12:
                # check if the palette is linear greyscale
                for i in range(256):
                    if s[i*3+1:i*3+4] != o8(i)*3:
                        mode = rawmode = "P"
                        break
                if mode == "P":
                    self.palette = ImagePalette.raw("RGB", s[1:])
            self.fp.seek(128)

        elif version == 5 and bits == 8 and planes == 3:
            mode = "RGB"
            rawmode = "RGB;L"

        else:
            raise IOError("unknown PCX mode")

        self.mode = mode
        self.size = bbox[2]-bbox[0], bbox[3]-bbox[1]

        bbox = (0, 0) + self.size
        logger.debug("size: %sx%s", *self.size)

        self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))]
Example #3
0
    def _open(self):

        # header
        s = self.fp.read(128)
        if not _accept(s):
            raise SyntaxError("not a PCX file")

        # image
        bbox = i16(s, 4), i16(s, 6), i16(s, 8)+1, i16(s, 10)+1
        if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]:
            raise SyntaxError("bad PCX image size")
        logger.debug("BBox: %s %s %s %s", *bbox)

        # format
        version = i8(s[1])
        bits = i8(s[3])
        planes = i8(s[65])
        stride = i16(s, 66)
        logger.debug("PCX version %s, bits %s, planes %s, stride %s",
                     version, bits, planes, stride)

        self.info["dpi"] = i16(s, 12), i16(s, 14)

        if bits == 1 and planes == 1:
            mode = rawmode = "1"

        elif bits == 1 and planes in (2, 4):
            mode = "P"
            rawmode = "P;%dL" % planes
            self.palette = ImagePalette.raw("RGB", s[16:64])

        elif version == 5 and bits == 8 and planes == 1:
            mode = rawmode = "L"
            # FIXME: hey, this doesn't work with the incremental loader !!!
            self.fp.seek(-769, 2)
            s = self.fp.read(769)
            if len(s) == 769 and i8(s[0]) == 12:
                # check if the palette is linear greyscale
                for i in range(256):
                    if s[i*3+1:i*3+4] != o8(i)*3:
                        mode = rawmode = "P"
                        break
                if mode == "P":
                    self.palette = ImagePalette.raw("RGB", s[1:])
            self.fp.seek(128)

        elif version == 5 and bits == 8 and planes == 3:
            mode = "RGB"
            rawmode = "RGB;L"

        else:
            raise IOError("unknown PCX mode")

        self.mode = mode
        self.size = bbox[2]-bbox[0], bbox[3]-bbox[1]

        bbox = (0, 0) + self.size
        logger.debug("size: %sx%s", *self.size)

        self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))]
Example #4
0
    def seek(self, pos):
        if pos >= self._n_frames:
            raise EOFError('seek beyond the end of the sequence')
        if pos < 0:
            raise EOFError("negative frame index is not valid")
        self._pos = pos
        curimg = self._images[self._pos]

        mode = curimg.mode
        self.mode = 'RGB' if mode == 'RGBX' else mode
        self._size = curimg.width, curimg.height
        self.rawmode = mode
        self.info['timestamp'] = curimg.delay
        self.info['duration'] = 0
        if self._pos:
            self.info['duration'] = self.info['timestamp'] - self._images[
                self._pos - 1].delay
        self.info['depth'] = curimg.depth
        if self.fp is not None:
            self.fp.close()
        self.fp = io.BytesIO(curimg.raw)
        if self.mode == 'P':
            # TODO: alpha is lost here.
            self.palette = ImagePalette.raw('RGBX', curimg.palette)
        self.tile.clear()
        self.tile.append(('raw', (0, 0, *self.size), 0, self.rawmode))
        if curimg.exif is None:
            self.info.pop('exif', None)
        else:
            self.info['exif'] = curimg.exif

        return self.load()
Example #5
0
    def _open(self):

        # check magic
        if self.fp.read(6) != _MAGIC:
            raise SyntaxError("not an XV thumbnail file")

        # Skip to beginning of next line
        self.fp.readline()

        # skip info comments
        while True:
            s = self.fp.readline()
            if not s:
                raise SyntaxError("Unexpected EOF reading XV thumbnail file")
            if s[0] != b'#':
                break

        # parse header line (already read)
        s = s.strip().split()

        self.mode = "P"
        self.size = int(s[0:1]), int(s[1:2])

        self.palette = ImagePalette.raw("RGB", PALETTE)

        self.tile = [
            ("raw", (0, 0)+self.size,
             self.fp.tell(), (self.mode, 0, 1)
             )]
Example #6
0
    def _open(self):

        # check magic
        if self.fp.read(6) != _MAGIC:
            raise SyntaxError("not an XV thumbnail file")

        # Skip to beginning of next line
        self.fp.readline()

        # skip info comments
        while True:
            s = self.fp.readline()
            if not s:
                raise SyntaxError("Unexpected EOF reading XV thumbnail file")
            if s[0] != b'#':
                break

        # parse header line (already read)
        s = s.strip().split()

        self.mode = "P"
        self.size = int(s[0:1]), int(s[1:2])

        self.palette = ImagePalette.raw("RGB", PALETTE)

        self.tile = [("raw", (0, 0) + self.size, self.fp.tell(), (self.mode, 0,
                                                                  1))]
Example #7
0
    def chunk_PLTE(self, offset, bytes):
        "PLTE -- palette data"

        s = self.fp.read(bytes)
        if self.mode == "P":
            self.palette = ImagePalette.raw("RGB", s)
        return s
    def _open(self):

        # Screen
        s = self.fp.read(13)
        if s[:6] not in [b"GIF87a", b"GIF89a"]:
            raise SyntaxError("not a GIF file")

        self.info["version"] = s[:6]
        self.size = i16(s[6:]), i16(s[8:])
        self.tile = []
        flags = i8(s[10])
        bits = (flags & 7) + 1

        if flags & 128:
            # get global palette
            self.info["background"] = i8(s[11])
            # check if palette contains colour indices
            p = self.fp.read(3 << bits)
            for i in range(0, len(p), 3):
                if not (i // 3 == i8(p[i]) == i8(p[i + 1]) == i8(p[i + 2])):
                    p = ImagePalette.raw("RGB", p)
                    self.global_palette = self.palette = p
                    break

        self.__fp = self.fp  # FIXME: hack
        self.__rewind = self.fp.tell()
        self.seek(0)  # get ready to read first frame
Example #9
0
    def chunk_PLTE(self, offset, bytes):
        "PLTE -- palette data"

        s = self.fp.read(bytes)
        if self.mode == "P":
            self.palette = ImagePalette.raw("RGB", s)
        return s
Example #10
0
    def _open(self):

        # Screen
        s = self.fp.read(13)
        if s[:6] not in [b"GIF87a", b"GIF89a"]:
            raise SyntaxError("not a GIF file")

        self.info["version"] = s[:6]
        self.size = i16(s[6:]), i16(s[8:])
        self.tile = []
        flags = i8(s[10])
        bits = (flags & 7) + 1

        if flags & 128:
            # get global palette
            self.info["background"] = i8(s[11])
            # check if palette contains colour indices
            p = self.fp.read(3 << bits)
            for i in range(0, len(p), 3):
                if not (i//3 == i8(p[i]) == i8(p[i+1]) == i8(p[i+2])):
                    p = ImagePalette.raw("RGB", p)
                    self.global_palette = self.palette = p
                    break

        self.__fp = self.fp  # FIXME: hack
        self.__rewind = self.fp.tell()
        self._n_frames = None
        self._is_animated = None
        self._seek(0)  # get ready to read first frame
    def _open(self):

        # check magic
        s = self.fp.read(6)
        if s != "P7 332":
            raise SyntaxError, "not an XV thumbnail file"

        # Skip to beginning of next line
        self.fp.readline()

        # skip info comments
        while 1:
            s = self.fp.readline()
            if not s:
                raise SyntaxError, "Unexpected EOF reading XV thumbnail file"
            if s[0] != '#':
                break

        # parse header line (already read)
        s = string.split(s.strip())

        self.mode = "P"
        self.size = int(s[0]), int(s[1])

        self.palette = ImagePalette.raw("RGB", PALETTE)

        self.tile = [
            ("raw", (0, 0)+self.size,
             self.fp.tell(), (self.mode, 0, 1)
             )]
Example #12
0
    def test_rawmode_valueerrors(self):
        # Arrange
        palette = ImagePalette.raw("RGB", list(range(256))*3)

        # Act / Assert
        self.assertRaises(ValueError, palette.tobytes)
        self.assertRaises(ValueError, palette.getcolor, (1, 2, 3))
        f = self.tempfile("temp.lut")
        self.assertRaises(ValueError, palette.save, f)
Example #13
0
    def test_rawmode_getdata(self):
        # Arrange
        data_in = list(range(256))*3
        palette = ImagePalette.raw("RGB", data_in)

        # Act
        rawmode, data_out = palette.getdata()

        # Assert
        self.assertEqual(rawmode, "RGB")
        self.assertEqual(data_in, data_out)
Example #14
0
def test_rawmode_getdata():
    # Arrange
    data_in = list(range(256)) * 3
    palette = ImagePalette.raw("RGB", data_in)

    # Act
    rawmode, data_out = palette.getdata()

    # Assert
    assert rawmode == "RGB"
    assert data_in == data_out
Example #15
0
def test_rawmode_valueerrors(tmp_path):
    # Arrange
    palette = ImagePalette.raw("RGB", list(range(256)) * 3)

    # Act / Assert
    with pytest.raises(ValueError):
        palette.tobytes()
    with pytest.raises(ValueError):
        palette.getcolor((1, 2, 3))
    f = str(tmp_path / "temp.lut")
    with pytest.raises(ValueError):
        palette.save(f)
    def _open(self):

        # HEAD
        s = self.fp.read(128)
        magic = i16(s[4:6])
        if not (magic in [0xAF11, 0xAF12] and i16(s[14:16]) in [0, 3]
                and  # flags
                s[20:22] == b"\x00\x00"):  # reserved
            raise SyntaxError("not an FLI/FLC file")

        # image characteristics
        self.mode = "P"
        self.size = i16(s[8:10]), i16(s[10:12])

        # animation speed
        duration = i32(s[16:20])
        if magic == 0xAF11:
            duration = (duration * 1000) / 70
        self.info["duration"] = duration

        # look for palette
        palette = [(a, a, a) for a in range(256)]

        s = self.fp.read(16)

        self.__offset = 128

        if i16(s[4:6]) == 0xF100:
            # prefix chunk; ignore it
            self.__offset = self.__offset + i32(s)
            s = self.fp.read(16)

        if i16(s[4:6]) == 0xF1FA:
            # look for palette chunk
            s = self.fp.read(6)
            if i16(s[4:6]) == 11:
                self._palette(palette, 2)
            elif i16(s[4:6]) == 4:
                self._palette(palette, 0)

        palette = [o8(r) + o8(g) + o8(b) for (r, g, b) in palette]
        self.palette = ImagePalette.raw("RGB", b"".join(palette))

        # set things up to decode first frame
        self.__frame = -1
        self.__fp = self.fp
        self.__rewind = self.fp.tell()
        self._n_frames = None
        self._is_animated = None
        self.seek(0)
Example #17
0
    def _open(self):

        # HEAD
        s = self.fp.read(128)
        magic = i16(s[4:6])
        if not (magic in [0xAF11, 0xAF12] and
                i16(s[14:16]) in [0, 3] and  # flags
                s[20:22] == b"\x00\x00"):  # reserved
            raise SyntaxError("not an FLI/FLC file")

        # image characteristics
        self.mode = "P"
        self.size = i16(s[8:10]), i16(s[10:12])

        # animation speed
        duration = i32(s[16:20])
        if magic == 0xAF11:
            duration = (duration * 1000) / 70
        self.info["duration"] = duration

        # look for palette
        palette = [(a, a, a) for a in range(256)]

        s = self.fp.read(16)

        self.__offset = 128

        if i16(s[4:6]) == 0xF100:
            # prefix chunk; ignore it
            self.__offset = self.__offset + i32(s)
            s = self.fp.read(16)

        if i16(s[4:6]) == 0xF1FA:
            # look for palette chunk
            s = self.fp.read(6)
            if i16(s[4:6]) == 11:
                self._palette(palette, 2)
            elif i16(s[4:6]) == 4:
                self._palette(palette, 0)

        palette = [o8(r)+o8(g)+o8(b) for (r, g, b) in palette]
        self.palette = ImagePalette.raw("RGB", b"".join(palette))

        # set things up to decode first frame
        self.__frame = -1
        self.__fp = self.fp
        self.__rewind = self.fp.tell()
        self._n_frames = None
        self._is_animated = None
        self.seek(0)
Example #18
0
    def _open(self):
        # Header
        s = self.fp.read(775)

        self.mode = "L"  # FIXME: "P"
        self.size = i16(s[0:2]), i16(s[2:4])

        # transparency index
        tindex = i16(s[5:7])
        if tindex < 256:
            self.info["transparent"] = tindex

        self.palette = ImagePalette.raw("RGB", s[7:])

        self.tile = [("raw", (0, 0) + self.size, 775, ("L", 0, -1))]
Example #19
0
    def _open(self):
        # Header
        s = self.fp.read(775)

        self.mode = "L"  # FIXME: "P"
        self.size = i16(s[0:2]), i16(s[2:4])

        # transparency index
        tindex = i16(s[5:7])
        if tindex < 256:
            self.info["transparent"] = tindex

        self.palette = ImagePalette.raw("RGB", s[7:])

        self.tile = [("raw", (0, 0) + self.size, 775, ("L", 0, -1))]
Example #20
0
    def test_file(self):

        palette = ImagePalette.ImagePalette("RGB", list(range(256))*3)

        f = self.tempfile("temp.lut")

        palette.save(f)

        p = ImagePalette.load(f)

        # load returns raw palette information
        self.assertEqual(len(p[0]), 768)
        self.assertEqual(p[1], "RGB")

        p = ImagePalette.raw(p[1], p[0])
        self.assertIsInstance(p, ImagePalette.ImagePalette)
        self.assertEqual(p.palette, palette.tobytes())
Example #21
0
def test_file(tmp_path):

    palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3)

    f = str(tmp_path / "temp.lut")

    palette.save(f)

    p = ImagePalette.load(f)

    # load returns raw palette information
    assert len(p[0]) == 768
    assert p[1] == "RGB"

    p = ImagePalette.raw(p[1], p[0])
    assert isinstance(p, ImagePalette.ImagePalette)
    assert p.palette == palette.tobytes()
Example #22
0
    def _open(self):

        if self.fp.read(8) != _MAGIC:
            raise SyntaxError("not a PNG file")

        #
        # Parse headers up to the first IDAT chunk

        self.png = PngStream(self.fp)

        while True:

            #
            # get next chunk

            cid, pos, length = self.png.read()

            try:
                s = self.png.call(cid, pos, length)
            except EOFError:
                break
            except AttributeError:
                if Image.DEBUG:
                    print(cid, pos, length, "(unknown)")
                s = ImageFile._safe_read(self.fp, length)

            self.png.crc(cid, s)

        #
        # Copy relevant attributes from the PngStream.  An alternative
        # would be to let the PngStream class modify these attributes
        # directly, but that introduces circular references which are
        # difficult to break if things go wrong in the decoder...
        # (believe me, I've tried ;-)

        self.mode = self.png.im_mode
        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

        if self.png.im_palette:
            rawmode, data = self.png.im_palette
            self.palette = ImagePalette.raw(rawmode, data)

        self.__idat = length  # used by load_read()
Example #23
0
    def _open(self):

        if self.fp.read(8) != _MAGIC:
            raise SyntaxError("not a PNG file")

        #
        # Parse headers up to the first IDAT chunk

        self.png = PngStream(self.fp)

        while True:

            #
            # get next chunk

            cid, pos, len = self.png.read()

            try:
                s = self.png.call(cid, pos, len)
            except EOFError:
                break
            except AttributeError:
                if Image.DEBUG:
                    print(cid, pos, len, "(unknown)")
                s = ImageFile._safe_read(self.fp, len)

            self.png.crc(cid, s)

        #
        # Copy relevant attributes from the PngStream.  An alternative
        # would be to let the PngStream class modify these attributes
        # directly, but that introduces circular references which are
        # difficult to break if things go wrong in the decoder...
        # (believe me, I've tried ;-)

        self.mode = self.png.im_mode
        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

        if self.png.im_palette:
            rawmode, data = self.png.im_palette
            self.palette = ImagePalette.raw(rawmode, data)

        self.__idat = len # used by load_read()
    def _open(self):

        # HEAD
        s = self.fp.read(128)
        magic = i16(s[4:6])
        if magic not in [0xAF11, 0xAF12]:
            raise SyntaxError, "not an FLI/FLC file"

        # image characteristics
        self.mode = "P"
        self.size = i16(s[8:10]), i16(s[10:12])

        # animation speed
        duration = i32(s[16:20])
        if magic == 0xAF11:
            duration = (duration * 1000) / 70
        self.info["duration"] = duration

        # look for palette
        palette = map(lambda a: (a,a,a), range(256))

        s = self.fp.read(16)

        self.__offset = 128

        if i16(s[4:6]) == 0xF100:
            # prefix chunk; ignore it
            self.__offset = self.__offset + i32(s)
            s = self.fp.read(16)

        if i16(s[4:6]) == 0xF1FA:
            # look for palette chunk
            s = self.fp.read(6)
            if i16(s[4:6]) == 11:
                self._palette(palette, 2)
            elif i16(s[4:6]) == 4:
                self._palette(palette, 0)

        palette = map(lambda (r,g,b): chr(r)+chr(g)+chr(b), palette)
        self.palette = ImagePalette.raw("RGB", string.join(palette, ""))

        # set things up to decode first frame
        self.frame = -1
        self.__fp = self.fp

        self.seek(0)
    def test_write_with_raw_palette(self):
        palette = ImagePalette.raw('RGB', b'\x01\x02\x03\x04\x05\x06')
        img = SubImage8Bit(Image.new('P', (2, 2), color=1))
        img.image.putpalette(palette)
        imgs = Images8Bit([img], palette=palette, width=9, height=8)
        buffer = BytesIO()

        save_8bit_sti(imgs, buffer)

        self.assertEqual(buffer.getvalue(),
                         # Header
                         b'STCI\x48\x00\x00\x00\x08\x00\x00\x00' +
                         b'\x00\x00\x00\x00' + b'\x28\x00\x00\x00' + b'\x08\x00\x09\x00' +
                         b'\x00\x01\x00\x00\x01\x00\x08\x08\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' +
                         b'\x08' + (3 * b'\x00') + b'\x00\x00\x00\x00' + (12 * b'\x00') +
                         # Palette
                         b'\x01\x02\x03' + b'\x04\x05\x06' + (254 * b'\x00\x00\x00') +
                         # Sub Image Header
                         b'\x00\x00\x00\x00' + b'\x08\x00\x00\x00' + b'\x00\x00' + b'\x00\x00' + b'\x02\x00' + b'\x02\x00' +
                         # Data
                         b'\x02\x01\x01\x00\x02\x01\x01\x00')
Example #26
0
    def _open(self):

        # HEAD
        s = self.fp.read(32)
        if i32(s) != 0x59a66a95:
            raise SyntaxError("not an SUN raster file")

        offset = 32

        self.size = i32(s[4:8]), i32(s[8:12])

        depth = i32(s[12:16])
        if depth == 1:
            self.mode, rawmode = "1", "1;I"
        elif depth == 8:
            self.mode = rawmode = "L"
        elif depth == 24:
            self.mode, rawmode = "RGB", "BGR"
        else:
            raise SyntaxError("unsupported mode")

        compression = i32(s[20:24])

        if i32(s[24:28]) != 0:
            length = i32(s[28:32])
            offset = offset + length
            self.palette = ImagePalette.raw("RGB;L", self.fp.read(length))
            if self.mode == "L":
                self.mode = rawmode = "P"

        stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3)

        if compression == 1:
            self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride))
                         ]
        elif compression == 2:
            self.tile = [("sun_rle", (0, 0) + self.size, offset, rawmode)]
    def _open(self):

        # HEAD
        s = self.fp.read(32)
        if i32(s) != 0x59a66a95:
            raise SyntaxError("not an SUN raster file")

        offset = 32

        self.size = i32(s[4:8]), i32(s[8:12])

        depth = i32(s[12:16])
        if depth == 1:
            self.mode, rawmode = "1", "1;I"
        elif depth == 8:
            self.mode = rawmode = "L"
        elif depth == 24:
            self.mode, rawmode = "RGB", "BGR"
        else:
            raise SyntaxError("unsupported mode")

        compression = i32(s[20:24])

        if i32(s[24:28]) != 0:
            length = i32(s[28:32])
            offset = offset + length
            self.palette = ImagePalette.raw("RGB;L", self.fp.read(length))
            if self.mode == "L":
                self.mode = rawmode = "P"

        stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3)

        if compression == 1:
            self.tile = [
                ("raw", (0, 0) + self.size, offset, (rawmode, stride))]
        elif compression == 2:
            self.tile = [("sun_rle", (0, 0) + self.size, offset, rawmode)]
Example #28
0
    def _open(self):
        # Quick rejection: if there's not an LF among the first
        # 100 bytes, this is (probably) not a text header.

        if b"\n" not in self.fp.read(100):
            raise InvalidFileType("not an IM file")
        self.fp.seek(0)

        n = 0

        # Default values
        self.info[MODE] = "L"
        self.info[SIZE] = (512, 512)
        self.info[FRAMES] = 1

        self.rawmode = "L"

        while True:
            s = self.fp.read(1)

            # Some versions of IFUNC uses \n\r instead of \r\n...
            if s == b"\r":
                continue

            if not s or s == b'\0' or s == b'\x1A':
                break

            # FIXME: this may read whole file if not a text file
            s = s + self.fp.readline()

            if len(s) > 100:
                raise InvalidFileType("not an IM file")

            if s[-2:] == b'\r\n':
                s = s[:-2]
            elif s[-1:] == b'\n':
                s = s[:-1]

            try:
                m = split.match(s)
            except re.error as v:
                raise InvalidFileType("not an IM file")

            if m:
                k, v = m.group(1, 2)

                # Don't know if this is the correct encoding,
                # but a decent guess (I guess)
                k = k.decode('latin-1', 'replace')
                v = v.decode('latin-1', 'replace')

                # Convert value as appropriate
                if k in [FRAMES, SCALE, SIZE]:
                    v = v.replace("*", ",")
                    v = tuple(map(number, v.split(",")))
                    if len(v) == 1:
                        v = v[0]
                elif k == MODE and v in OPEN:
                    v, self.rawmode = OPEN[v]

                # Add to dictionary. Note that COMMENT tags are
                # combined into a list of strings.
                if k == COMMENT:
                    if k in self.info:
                        self.info[k].append(v)
                    else:
                        self.info[k] = [v]
                else:
                    self.info[k] = v

                if k in TAGS:
                    n += 1

            else:
                s = s.decode("ascii", "replace")
                raise PILReadError("Bad IM header: %r" % (s))

        if not n:
            raise InvalidFileType("Not an IM file")

        # Basic attributes
        self.size = self.info[SIZE]
        self.mode = self.info[MODE]

        # Skip forward to start of image data
        while s and s[0:1] != b'\x1A':
            s = self.fp.read(1)
        if not s:
            raise PILReadError("File truncated")

        if LUT in self.info:
            # convert lookup table to palette or lut attribute
            palette = self.fp.read(768)
            greyscale = 1  # greyscale palette
            linear = 1  # linear greyscale palette
            for i in range(256):
                if palette[i] == palette[i + 256] == palette[i + 512]:
                    if i8(palette[i]) != i:
                        linear = 0
                else:
                    greyscale = 0
            if self.mode == "L" or self.mode == "LA":
                if greyscale:
                    if not linear:
                        self.lut = [i8(c) for c in palette[:256]]
                else:
                    if self.mode == "L":
                        self.mode = self.rawmode = "P"
                    elif self.mode == "LA":
                        self.mode = self.rawmode = "PA"
                    self.palette = ImagePalette.raw("RGB;L", palette)
            elif self.mode == "RGB":
                if not greyscale or not linear:
                    self.lut = [i8(c) for c in palette]

        self.frame = 0

        self.__offset = offs = self.fp.tell()

        self.__fp = self.fp  # FIXME: hack

        if self.rawmode[:2] == "F;":

            # ifunc95 formats
            try:
                # use bit decoder (if necessary)
                bits = int(self.rawmode[2:])
                if bits not in [8, 16, 32]:
                    self.tile = [("bit", (0, 0) + self.size, offs, (bits, 8, 3,
                                                                    0, -1))]
                    return
            except ValueError:
                pass

        if self.rawmode in ["RGB;T", "RYB;T"]:
            # Old LabEye/3PC files.  Would be very surprised if anyone
            # ever stumbled upon such a file ;-)
            size = self.size[0] * self.size[1]
            self.tile = [
                ("raw", (0, 0) + self.size, offs, ("G", 0, -1)),
                ("raw", (0, 0) + self.size, offs + size, ("R", 0, -1)),
                ("raw", (0, 0) + self.size, offs + 2 * size, ("B", 0, -1))
            ]
        else:
            # LabEye/IFUNC files
            self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0,
                                                            -1))]
Example #29
0
    def _setup(self):
        "Setup this image object based on current tags"

        if self.tag.has_key(0xBC01):
            raise IOError, "Windows Media Photo files not yet supported"

        getscalar = self.tag.getscalar

        # extract relevant tags
        self._compression = COMPRESSION_INFO[getscalar(COMPRESSION, 1)]
        self._planar_configuration = getscalar(PLANAR_CONFIGURATION, 1)

        # photometric is a required tag, but not everyone is reading
        # the specification
        photo = getscalar(PHOTOMETRIC_INTERPRETATION, 0)

        fillorder = getscalar(FILLORDER, 1)

        if Image.DEBUG:
            print "*** Summary ***"
            print "- compression:", self._compression
            print "- photometric_interpretation:", photo
            print "- planar_configuration:", self._planar_configuration
            print "- fill_order:", fillorder

        # size
        xsize = getscalar(IMAGEWIDTH)
        ysize = getscalar(IMAGELENGTH)
        self.size = xsize, ysize

        if Image.DEBUG:
            print "- size:", self.size

        format = getscalar(SAMPLEFORMAT, 1)

        # mode: check photometric interpretation and bits per pixel
        key = (photo, format, fillorder, self.tag.get(BITSPERSAMPLE, (1, )),
               self.tag.get(EXTRASAMPLES, ()))
        if Image.DEBUG:
            print "format key:", key
        try:
            self.mode, rawmode = OPEN_INFO[key]
        except KeyError:
            if Image.DEBUG:
                print "- unsupported format"
            raise SyntaxError, "unknown pixel mode"

        if Image.DEBUG:
            print "- raw mode:", rawmode
            print "- pil mode:", self.mode

        self.info["compression"] = self._compression

        # BEGIN PATCH
        xres = getscalar(X_RESOLUTION, (1, 1))
        yres = getscalar(Y_RESOLUTION, (1, 1))

        if xres and yres:
            xres = xres[0] / (xres[1] or 1)
            yres = yres[0] / (yres[1] or 1)
            resunit = getscalar(RESOLUTION_UNIT, 1)
            if resunit == 2:  # Inches
                self.info["dpi"] = xres, yres
            elif resunit == 3:  # Centimeters
                self.info["dpi"] = xres * 2.54, yres * 2.54
            else:  # No absolute unit of measurement.
                self.info["resolution"] = xres, yres
        # END PATCH

        # build tile descriptors
        x = y = l = 0
        self.tile = []
        if self.tag.has_key(STRIPOFFSETS):
            # striped image
            h = getscalar(ROWSPERSTRIP, ysize)
            w = self.size[0]
            a = None
            for o in self.tag[STRIPOFFSETS]:
                if not a:
                    a = self._decoder(rawmode, l)
                self.tile.append(
                    (self._compression, (0, min(y,
                                                ysize), w, min(y + h,
                                                               ysize)), o, a))
                y = y + h
                if y >= self.size[1]:
                    x = y = 0
                    l = l + 1
                    a = None
        elif self.tag.has_key(324):
            # tiled image
            w = getscalar(322)
            h = getscalar(323)
            a = None
            for o in self.tag[324]:
                if not a:
                    a = self._decoder(rawmode, l)
                # FIXME: this doesn't work if the image size
                # is not a multiple of the tile size...
                self.tile.append(
                    (self._compression, (x, y, x + w, y + h), o, a))
                x = x + w
                if x >= self.size[0]:
                    x, y = 0, y + h
                    if y >= self.size[1]:
                        x = y = 0
                        l = l + 1
                        a = None
        else:
            if Image.DEBUG:
                print "- unsupported data organization"
            raise SyntaxError("unknown data organization")

        # fixup palette descriptor
        if self.mode == "P":
            palette = map(lambda a: chr(a / 256), self.tag[COLORMAP])
            self.palette = ImagePalette.raw("RGB;L", string.join(palette, ""))
Example #30
0
 def _galx_info(self, header):
     """LiveMaker GAL/X multiframe (multi-layer) image."""
     read = self.fp.read
     header += read(3)
     info = {}
     info["version"] = header[4:]
     if info["version"] == b"X200":
         header_size = i32le(read(4))
         xml = zlib.decompress(read(header_size))
         try:
             # Note: LiveMaker's code for generating GAL/X images sometimes
             # creates invalid XML, but setting recover=False should let
             # lxml deal with most of these cases.
             root = etree.fromstring(
                 xml,
                 parser=etree.XMLParser(encoding="shift-jis", recover=True))
         except etree.LxmlError as e:
             raise GalImageError(
                 "Could not parse GAL/X image XML metadata: {}".format(e))
         info["width"] = int(root.get("Width", 0))
         info["height"] = int(root.get("Height", 0))
         info["bpp"] = int(root.get("Bpp", 0))
         info["frame_count"] = int(root.get("Count", 0))
         info["compression"] = int(root.get("CompType", 0))
         info["compression_level"] = int(root.get("CompLevel", 0))
         info["randomized"] = root.get("Randomized") != "0"
         info["bg_color"] = int(root.get("BGColor", 0))
         info["block_width"] = int(root.get("BlockWidth", 0))
         info["block_height"] = int(root.get("BlockHeight", 0))
         info["offset"] = header_size + 12
         info["root"] = root
     else:
         raise GalImageError("Unsupported GAL/X version {}".format(header))
     if info["frame_count"] != len(root):
         print("Warning: frame count mismatch")
     info["frames"] = []
     for frame in root:
         frame_info = {}
         if len(frame) > 1:
             print("Warning: Frame contained multiple Layers tags")
         frame_info["name"] = frame.get("Name", "")
         # TODO: figure out what this bounding box is actually for
         # left = int(frame.get('L0', 0))
         # top = int(frame.get('T0', 0))
         # right = int(frame.get('R0', info['width']))
         # bottom = int(frame.get('B0', info['height']))
         # frame_info['box'] = (left, top, right, bottom)
         frame_info["box"] = (0, 0, info["width"], info["height"])
         for layers in frame:
             frame_info["width"] = int(layers.get("Width", info["width"]))
             frame_info["height"] = int(layers.get("Height",
                                                   info["height"]))
             frame_info["bpp"] = int(layers.get("bpp", info["bpp"]))
             stride = (frame_info["width"] * frame_info["bpp"] + 7) // 8
             if frame_info["bpp"] >= 8:
                 # align to 4 byte boundary
                 stride = (stride + 3) & ~3
             if frame_info["bpp"] <= 8:
                 for rgb in layers.iter("RGB"):
                     palette = unhexlify(rgb.text)
                     frame_info["palette"] = ImagePalette.raw(
                         "BGR", palette)
             else:
                 frame_info["palette"] = None
             frame_info["stride"] = stride
             frame_info["alpha_stride"] = (frame_info["width"] + 3) & ~3
             frame_info["layers"] = []
             for layer in layers.iter("Layer"):
                 layer_info = {}
                 left = int(layer.get("Left", 0))
                 top = int(layer.get("Top", 0))
                 layer_info["origin"] = (left, top)
                 layer_info["trans_color"] = int(layer.get(
                     "TransColor", -1))
                 layer_info["visible"] = int(layer.get("Visible", 1))
                 layer_info["alpha"] = int(layer.get("Alpha", 255))
                 layer_info["alpha_on"] = int(layer.get("AlphaOn", 0))
                 frame_info["layers"].append(layer_info)
         info["frames"].append(frame_info)
     return info
Example #31
0
    def _open(self):

        read = self.fp.read

        #
        # header

        s = read(26)
        if s[:4] != b"8BPS" or i16(s[4:]) != 1:
            raise SyntaxError("not a PSD file")

        psd_bits = i16(s[22:])
        psd_channels = i16(s[12:])
        psd_mode = i16(s[24:])

        mode, channels = MODES[(psd_mode, psd_bits)]

        if channels > psd_channels:
            raise IOError("not enough channels")

        self.mode = mode
        self.size = i32(s[18:]), i32(s[14:])

        #
        # color mode data

        size = i32(read(4))
        if size:
            data = read(size)
            if mode == "P" and size == 768:
                self.palette = ImagePalette.raw("RGB;L", data)

        #
        # image resources

        self.resources = []

        size = i32(read(4))
        if size:
            # load resources
            end = self.fp.tell() + size
            while self.fp.tell() < end:
                signature = read(4)
                id = i16(read(2))
                name = read(i8(read(1)))
                if not (len(name) & 1):
                    read(1) # padding
                data = read(i32(read(4)))
                if (len(data) & 1):
                    read(1) # padding
                self.resources.append((id, name, data))
                if id == 1039: # ICC profile
                    self.info["icc_profile"] = data

        #
        # layer and mask information

        self.layers = []

        size = i32(read(4))
        if size:
            end = self.fp.tell() + size
            size = i32(read(4))
            if size:
                self.layers = _layerinfo(self.fp)
            self.fp.seek(end)

        #
        # image descriptor

        self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels)

        # keep the file open
        self._fp = self.fp
        self.frame = 0
Example #32
0
    def convert(self, mode=None, matrix=None, dither=None,
                palette=WEB, colors=256):
        """
        The current version supports all possible conversions between
        "L", "RGB" and "CMYK." The **matrix** argument only supports "L"
        and "RGB".
        :param mode: The requested mode. 
        :param matrix: An optional conversion matrix.  If given, this
           should be 4- or 12-tuple containing floating point values.
        :param dither: Dithering method, used when converting from
           mode "RGB" to "P" or from "RGB" or "L" to "1".
           Available methods are NONE or FLOYDSTEINBERG (default).
        :param palette: Palette to use when converting from mode "RGB"
           to "P".  Available palettes are WEB or ADAPTIVE.
        :param colors: Number of colors to use for the ADAPTIVE palette.
           Defaults to 256.
        """

        if not mode:
            # determine default mode
            if self.mode == "P":
                self.load()
                if self.palette:
                    mode = self.palette.mode
                else:
                    mode = "RGB"
            else:
                return self.copy()

        self.load()

        if matrix:
            # matrix conversion
            if mode not in ("L", "RGB"):
                raise ValueError("illegal conversion")
            im = self.im.convert_matrix(mode, matrix)
            return self._new(im)

        if mode == "P" and self.mode == "RGBA":
            return self.quantize(colors)

        trns = None
        delete_trns = False
        # transparency handling
        if "transparency" in self.info and \
                self.info['transparency'] is not None:
            if self.mode in ('L', 'RGB') and mode == 'RGBA':
                # Use transparent conversion to promote from transparent
                # color to an alpha channel.
                return self._new(self.im.convert_transparent(
                    mode, self.info['transparency']))
            elif self.mode in ('L', 'RGB', 'P') and mode in ('L', 'RGB', 'P'):
                t = self.info['transparency']
                if isinstance(t, bytes):
                    # Dragons. This can't be represented by a single color
                    warnings.warn('Palette images with Transparency  ' +
                                  ' expressed in bytes should be converted ' +
                                  'to RGBA images')
                    delete_trns = True
                else:
                    # get the new transparency color.
                    # use existing conversions
                    trns_im = Image()._new(core.new(self.mode, (1, 1)))
                    if self.mode == 'P':
                        trns_im.putpalette(self.palette)
                        if type(t) == tuple:
                            try:
                                t = trns_im.palette.getcolor(t)
                            except:
                                raise ValueError("Couldn't allocate a palette "+
                                                 "color for transparency")
                    trns_im.putpixel((0, 0), t)

                    if mode in ('L', 'RGB'):
                        trns_im = trns_im.convert(mode)
                    else:
                        # can't just retrieve the palette number, got to do it
                        # after quantization.
                        trns_im = trns_im.convert('RGB')
                    trns = trns_im.getpixel((0, 0))

            elif self.mode == 'P' and mode == 'RGBA':
                t = self.info['transparency']
                delete_trns = True

                if isinstance(t, bytes):
                    self.im.putpalettealphas(t)
                elif isinstance(t, int):
                    self.im.putpalettealpha(t, 0)
                else:
                    raise ValueError("Transparency for P mode should" +
                                     " be bytes or int")

        if mode == "P" and palette == ADAPTIVE:
            im = self.im.quantize(colors)
            new = self._new(im)
            from homework import Sjn_palette
            new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB"))
            if delete_trns:
                # This could possibly happen if we requantize to fewer colors.
                # The transparency would be totally off in that case.
                del(new.info['transparency'])
            if trns is not None:
                try:
                    new.info['transparency'] = new.palette.getcolor(trns)
                except:
                    # if we can't make a transparent color, don't leave the old
                    # transparency hanging around to mess us up.
                    del(new.info['transparency'])
                    warnings.warn("Couldn't allocate palette entry " +
                                  "for transparency")
            return new

        # colorspace conversion
        if dither is None:
            dither = FLOYDSTEINBERG

        try:
            im = self.im.convert(mode, dither)
        except ValueError:
            try:
                # normalize source image and try again
                im = self.im.convert(getmodebase(self.mode))
                im = im.convert(mode, dither)
            except KeyError:
                raise ValueError("illegal conversion")

        new_im = self._new(im)
        if delete_trns:
            # crash fail if we leave a bytes transparency in an rgb/l mode.
            del(new_im.info['transparency'])
        if trns is not None:
            if new_im.mode == 'P':
                try:
                    new_im.info['transparency'] = new_im.palette.getcolor(trns)
                except:
                    del(new_im.info['transparency'])
                    warnings.warn("Couldn't allocate palette entry " +
                                  "for transparency")
            else:
                new_im.info['transparency'] = trns
        return new_im
Example #33
0
    def _bitmap(self, header=0, offset=0):
        """ Read relevant info about the BMP """
        read, seek = self.fp.read, self.fp.seek
        if header:
            seek(header)
        file_info = dict()
        file_info['header_size'] = i32(
            read(4)
        )  # read bmp header size @offset 14 (this is part of the header size)
        file_info['direction'] = -1
        # --------------------- If requested, read header at a specific position
        header_data = ImageFile._safe_read(
            self.fp, file_info['header_size'] -
            4)  # read the rest of the bmp header, without its size
        # --------------------------------------------------- IBM OS/2 Bitmap v1
        # ------ This format has different offsets because of width/height types
        if file_info['header_size'] == 12:
            file_info['width'] = i16(header_data[0:2])
            file_info['height'] = i16(header_data[2:4])
            file_info['planes'] = i16(header_data[4:6])
            file_info['bits'] = i16(header_data[6:8])
            file_info['compression'] = self.RAW
            file_info['palette_padding'] = 3
        # ---------------------------------------------- Windows Bitmap v2 to v5
        elif file_info['header_size'] in (40, 64, 108,
                                          124):  # v3, OS/2 v2, v4, v5
            if file_info['header_size'] >= 40:  # v3 and OS/2
                file_info['y_flip'] = i8(header_data[7]) == 0xff
                file_info['direction'] = 1 if file_info['y_flip'] else -1
                file_info['width'] = i32(header_data[0:4])
                file_info['height'] = i32(
                    header_data[4:8]
                ) if not file_info['y_flip'] else 2**32 - i32(header_data[4:8])
                file_info['planes'] = i16(header_data[8:10])
                file_info['bits'] = i16(header_data[10:12])
                file_info['compression'] = i32(header_data[12:16])
                file_info['data_size'] = i32(
                    header_data[16:20])  # byte size of pixel data
                file_info['pixels_per_meter'] = (i32(header_data[20:24]),
                                                 i32(header_data[24:28]))
                file_info['colors'] = i32(header_data[28:32])
                file_info['palette_padding'] = 4
                self.info["dpi"] = tuple(
                    map(lambda x: int(math.ceil(x / 39.3701)),
                        file_info['pixels_per_meter']))
                if file_info['compression'] == self.BITFIELDS:
                    if len(header_data) >= 52:
                        for idx, mask in enumerate(
                            ['r_mask', 'g_mask', 'b_mask', 'a_mask']):
                            file_info[mask] = i32(header_data[36 + idx * 4:40 +
                                                              idx * 4])
                    else:
                        for mask in ['r_mask', 'g_mask', 'b_mask', 'a_mask']:
                            file_info[mask] = i32(read(4))
                    file_info['rgb_mask'] = (file_info['r_mask'],
                                             file_info['g_mask'],
                                             file_info['b_mask'])
                    file_info['rgba_mask'] = (file_info['r_mask'],
                                              file_info['g_mask'],
                                              file_info['b_mask'],
                                              file_info['a_mask'])
        else:
            raise IOError("Unsupported BMP header type (%d)" %
                          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']
        # -------- 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
        if file_info['width'] * file_info['height'] > 2**31:
            raise IOError("Unsupported BMP Size: (%dx%d)" % self.size)
        # ----------------------- Check bit depth for unusual unsupported values
        self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None))
        if self.mode is None:
            raise IOError("Unsupported BMP pixel depth (%d)" %
                          file_info['bits'])
        # ----------------- Process BMP with Bitfields compression (not palette)
        if file_info['compression'] == self.BITFIELDS:
            SUPPORTED = {
                32: [(0xff0000, 0xff00, 0xff, 0x0),
                     (0xff0000, 0xff00, 0xff, 0xff000000),
                     (0x0, 0x0, 0x0, 0x0)],
                24: [(0xff0000, 0xff00, 0xff)],
                16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
            }
            MASK_MODES = {
                (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
                (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
                (32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
                (24, (0xff0000, 0xff00, 0xff)): "BGR",
                (16, (0xf800, 0x7e0, 0x1f)): "BGR;16",
                (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"
            }
            if file_info['bits'] in SUPPORTED:
                if file_info['bits'] == 32 and file_info[
                        'rgba_mask'] in SUPPORTED[file_info['bits']]:
                    raw_mode = MASK_MODES[(file_info['bits'],
                                           file_info['rgba_mask'])]
                    self.mode = "RGBA" if raw_mode in ("BGRA", ) else self.mode
                elif file_info['bits'] in (
                        24, 16
                ) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]:
                    raw_mode = MASK_MODES[(file_info['bits'],
                                           file_info['rgb_mask'])]
                else:
                    raise IOError("Unsupported BMP bitfields layout")
            else:
                raise IOError("Unsupported BMP bitfields layout")
        elif file_info['compression'] == self.RAW:
            if file_info['bits'] == 32 and header == 22:  # 32-bit .cur offset
                raw_mode, self.mode = "BGRA", "RGBA"
        else:
            raise IOError("Unsupported BMP compression (%d)" %
                          file_info['compression'])
        # ---------------- Once the header is processed, process the palette/LUT
        if self.mode == "P":  # Paletted for 1, 4 and 8 bit images
            # ----------------------------------------------------- 1-bit images
            if not (0 < file_info['colors'] <= 65536):
                raise IOError("Unsupported BMP Palette size (%d)" %
                              file_info['colors'])
            else:
                padding = file_info['palette_padding']
                palette = read(padding * file_info['colors'])
                greyscale = True
                indices = (0, 255) if file_info['colors'] == 2 else list(
                    range(file_info['colors']))
                # ------------------ Check if greyscale and ignore palette if so
                for ind, val in enumerate(indices):
                    rgb = palette[ind * padding:ind * padding + 3]
                    if rgb != o8(val) * 3:
                        greyscale = False
                # -------- If all colors are grey, white or black, ditch palette
                if greyscale:
                    self.mode = "1" if file_info['colors'] == 2 else "L"
                    raw_mode = self.mode
                else:
                    self.mode = "P"
                    self.palette = ImagePalette.raw(
                        "BGRX" if padding == 4 else "BGR", palette)

        # ----------------------------- Finally set the tile data for the plugin
        self.info['compression'] = file_info['compression']
        self.tile = [
            ('raw', (0, 0, file_info['width'], file_info['height']), offset
             or self.fp.tell(),
             (raw_mode,
              ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3),
              file_info['direction']))
        ]
Example #34
0
    def seek(self, frame):

        if frame == 0:
            # rewind
            self.__offset = 0
            self.dispose = None
            self.__frame = -1
            self.__fp.seek(self.__rewind)

        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 = self.dispose
            self.dispose = None

        self.palette = 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
                    try:
                        # disposal methods
                        if flags & 8:
                            # replace with background colour
                            self.dispose = Image.core.fill(
                                "P", self.size, self.info["background"])
                        elif flags & 16:
                            # replace with previous contents
                            self.dispose = self.im.copy()
                    except (AttributeError, KeyError):
                        pass
                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:])
                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)

        if not self.tile:
            # self.__fp = None
            raise EOFError("no more images in GIF file")

        self.mode = "L"
        if self.palette:
            self.mode = "P"
Example #35
0
 def _gal_frames(self, info):
     read = self.fp.read
     seek = self.fp.seek
     frames = []
     offsets = []
     seek(info["offset"])
     info["frames"] = []
     for i in range(info["frame_count"]):
         frame_info = {}
         name_len = i32le(read(4))
         frame_info["name"] = read(name_len).decode("cp932")
         mask = i32le(read(4))
         seek(9, 1)
         layer_count = i32le(read(4))
         if layer_count < 1:
             raise GalImageError("Invalid GAL frame")
         frame_info["width"] = si32le(read(4))
         frame_info["height"] = si32le(read(4))
         bpp = i32le(read(4))
         if bpp not in _GAL_MODE or bpp > 32:
             print(layer_count)
             print(frame_info, mask)
             print(bpp)
             raise GalImageError("Unsupported GAL pixel format")
         frame_info["bpp"] = bpp
         if bpp <= 8:
             palette_size = 1 << bpp
             frame_info["palette"] = ImagePalette.raw(
                 "BGRX", read(palette_size * 4))
         else:
             frame_info["palette"] = None
         mode, rawmode = _GAL_MODE[bpp]
         layermode = mode
         stride = (frame_info["width"] * bpp + 7) // 8
         if bpp >= 8:
             # align to 4 byte boundary
             stride = (stride + 3) & ~3
         frame_info["stride"] = stride
         frame_info["alpha_stride"] = (frame_info["width"] + 3) & ~3
         frame_info["layers"] = []
         for j in range(layer_count):
             layer_info = {}
             left = si32le(read(4))
             top = si32le(read(4))
             layer_info["origin"] = (left, top)
             layer_info["visible"] = read(1)[0]
             layer_info["trans_color"] = si32le(read(4))
             layer_info["alpha"] = si32le(read(4))
             layer_info["alpha_on"] = read(1)[0]
             name_len = i32le(read(4))
             seek(name_len, 1)
             if int(info["version"]) >= 107:
                 layer_info["lock"] = read(1)[0]
             if j == 0:
                 offsets.append(self.fp.tell())
             else:
                 print(
                     "Warning: multilayer Gale images not fully supported")
             layer_size = si32le(read(4))
             seek(layer_size, 1)
             alpha_size = si32le(read(4))
             if layer_info["alpha_on"] and alpha_size > 0:
                 if mode == "RGB":
                     mode = "RGBA"
                 elif mode == "P":
                     mode = "PA"
                 else:
                     raise GalImageError("unsupported GAL alpha mode")
             seek(alpha_size, 1)
             frame_info["layers"].append(layer_info)
         info["frames"].append(frame_info)
         box = (0, 0, frame_info["width"], frame_info["height"])
         frames.append((frame_info["name"], layer_count, mode, layermode,
                        rawmode, box, frame_info["palette"]))
         # TODO: handle multi-frame images
         break
     return frames, offsets
Example #36
0
    def _bitmap(self, header=0, offset=0):

        if header:
            self.fp.seek(header)

        read = self.fp.read

        # CORE/INFO
        s = read(4)
        s = s + ImageFile._safe_read(self.fp, i32(s) - 4)

        if len(s) == 12:

            # OS/2 1.0 CORE
            bits = i16(s[10:])
            self.size = i16(s[4:]), i16(s[6:])
            compression = 0
            lutsize = 3
            colors = 0
            direction = -1

        elif len(s) in [40, 64]:

            # WIN 3.1 or OS/2 2.0 INFO
            bits = i16(s[14:])
            self.size = i32(s[4:]), i32(s[8:])
            compression = i32(s[16:])
            lutsize = 4
            colors = i32(s[32:])
            direction = -1
            if i8(s[11]) == 0xff:
                # upside-down storage
                self.size = self.size[0], 2**32 - self.size[1]
                direction = 0

        else:
            raise IOError("Unsupported BMP header type (%d)" % len(s))

        if not colors:
            colors = 1 << bits

        # MODE
        try:
            self.mode, rawmode = BIT2MODE[bits]
        except KeyError:
            raise IOError("Unsupported BMP pixel depth (%d)" % bits)

        if compression == 3:
            # BI_BITFIELDS compression
            mask = i32(read(4)), i32(read(4)), i32(read(4))
            if bits == 32 and mask == (0xff0000, 0x00ff00, 0x0000ff):
                rawmode = "BGRX"
            elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f):
                rawmode = "BGR;16"
            elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f):
                rawmode = "BGR;15"
            else:
                # print bits, map(hex, mask)
                raise IOError("Unsupported BMP bitfields layout")
        elif compression != 0:
            raise IOError("Unsupported BMP compression (%d)" % compression)

        # LUT
        if self.mode == "P":
            palette = []
            greyscale = 1
            if colors == 2:
                indices = (0, 255)
            else:
                indices = list(range(colors))
            for i in indices:
                rgb = read(lutsize)[:3]
                if rgb != o8(i) * 3:
                    greyscale = 0
                palette.append(rgb)
            if greyscale:
                if colors == 2:
                    self.mode = rawmode = "1"
                else:
                    self.mode = rawmode = "L"
            else:
                self.mode = "P"
                self.palette = ImagePalette.raw("BGR", b"".join(palette))

        if not offset:
            offset = self.fp.tell()

        self.tile = [("raw", (0, 0) + self.size, offset,
                      (rawmode, ((self.size[0] * bits + 31) >> 3) & (~3),
                       direction))]

        self.info["compression"] = compression
Example #37
0
    def _bitmap(self, header = 0, offset = 0):

        if header:
            self.fp.seek(header)

        read = self.fp.read

        # CORE/INFO
        s = read(4)
        s = s + ImageFile._safe_read(self.fp, i32(s)-4)

        if len(s) == 12:

            # OS/2 1.0 CORE
            bits = i16(s[10:])
            self.size = i16(s[4:]), i16(s[6:])
            compression = 0
            lutsize = 3
            colors = 0
            direction = -1

        elif len(s) in [40, 64]:

            # WIN 3.1 or OS/2 2.0 INFO
            bits = i16(s[14:])
            self.size = i32(s[4:]), i32(s[8:])
            compression = i32(s[16:])
            lutsize = 4
            colors = i32(s[32:])
            direction = -1
            if i8(s[11]) == 0xff:
                # upside-down storage
                self.size = self.size[0], 2**32 - self.size[1]
                direction = 0

        else:
            raise IOError("Unsupported BMP header type (%d)" % len(s))

        if not colors:
            colors = 1 << bits

        # MODE
        try:
            self.mode, rawmode = BIT2MODE[bits]
        except KeyError:
            raise IOError("Unsupported BMP pixel depth (%d)" % bits)

        if compression == 3:
            # BI_BITFIELDS compression
            mask = i32(read(4)), i32(read(4)), i32(read(4))
            if bits == 32 and mask == (0xff0000, 0x00ff00, 0x0000ff):
                rawmode = "BGRX"
            elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f):
                rawmode = "BGR;16"
            elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f):
                rawmode = "BGR;15"
            else:
                # print bits, map(hex, mask)
                raise IOError("Unsupported BMP bitfields layout")
        elif compression != 0:
            raise IOError("Unsupported BMP compression (%d)" % compression)

        # LUT
        if self.mode == "P":
            palette = []
            greyscale = 1
            if colors == 2:
                indices = (0, 255)
            else:
                indices = list(range(colors))
            for i in indices:
                rgb = read(lutsize)[:3]
                if rgb != o8(i)*3:
                    greyscale = 0
                palette.append(rgb)
            if greyscale:
                if colors == 2:
                    self.mode = rawmode = "1"
                else:
                    self.mode = rawmode = "L"
            else:
                self.mode = "P"
                self.palette = ImagePalette.raw(
                    "BGR", b"".join(palette)
                    )

        if not offset:
            offset = self.fp.tell()

        self.tile = [("raw",
                     (0, 0) + self.size,
                     offset,
                     (rawmode, ((self.size[0]*bits+31)>>3)&(~3), direction))]

        self.info["compression"] = compression
Example #38
0
    def _bitmap(self, header=0, offset=0):
        """ Read relevant info about the BMP """
        read, seek = self.fp.read, self.fp.seek
        if header:
            seek(header)
        file_info = dict()
        file_info["header_size"] = i32(read(4))  # read bmp header size @offset 14 (this is part of the header size)
        file_info["direction"] = -1
        # --------------------- If requested, read header at a specific position
        header_data = ImageFile._safe_read(
            self.fp, file_info["header_size"] - 4
        )  # read the rest of the bmp header, without its size
        # --------------------------------------------------- IBM OS/2 Bitmap v1
        # ------ This format has different offsets because of width/height types
        if file_info["header_size"] == 12:
            file_info["width"] = i16(header_data[0:2])
            file_info["height"] = i16(header_data[2:4])
            file_info["planes"] = i16(header_data[4:6])
            file_info["bits"] = i16(header_data[6:8])
            file_info["compression"] = self.RAW
            file_info["palette_padding"] = 3
        # ---------------------------------------------- Windows Bitmap v2 to v5
        elif file_info["header_size"] in (40, 64, 108, 124):  # v3, OS/2 v2, v4, v5
            if file_info["header_size"] >= 40:  # v3 and OS/2
                file_info["y_flip"] = i8(header_data[7]) == 0xFF
                file_info["direction"] = 1 if file_info["y_flip"] else -1
                file_info["width"] = i32(header_data[0:4])
                file_info["height"] = (
                    i32(header_data[4:8]) if not file_info["y_flip"] else 2 ** 32 - i32(header_data[4:8])
                )
                file_info["planes"] = i16(header_data[8:10])
                file_info["bits"] = i16(header_data[10:12])
                file_info["compression"] = i32(header_data[12:16])
                file_info["data_size"] = i32(header_data[16:20])  # byte size of pixel data
                file_info["pixels_per_meter"] = (i32(header_data[20:24]), i32(header_data[24:28]))
                file_info["colors"] = i32(header_data[28:32])
                file_info["palette_padding"] = 4
                self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), file_info["pixels_per_meter"]))
                if file_info["compression"] == self.BITFIELDS:
                    if len(header_data) >= 52:
                        for idx, mask in enumerate(["r_mask", "g_mask", "b_mask", "a_mask"]):
                            file_info[mask] = i32(header_data[36 + idx * 4 : 40 + idx * 4])
                    else:
                        for mask in ["r_mask", "g_mask", "b_mask", "a_mask"]:
                            file_info[mask] = i32(read(4))
                    file_info["rgb_mask"] = (file_info["r_mask"], file_info["g_mask"], file_info["b_mask"])
                    file_info["rgba_mask"] = (
                        file_info["r_mask"],
                        file_info["g_mask"],
                        file_info["b_mask"],
                        file_info["a_mask"],
                    )
        else:
            raise IOError("Unsupported BMP header type (%d)" % 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"]
        # -------- 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
        if file_info["width"] * file_info["height"] > 2 ** 31:
            raise IOError("Unsupported BMP Size: (%dx%d)" % self.size)
        # ----------------------- Check bit depth for unusual unsupported values
        self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None))
        if self.mode is None:
            raise IOError("Unsupported BMP pixel depth (%d)" % file_info["bits"])
        # ----------------- Process BMP with Bitfields compression (not palette)
        if file_info["compression"] == self.BITFIELDS:
            SUPPORTED = {
                32: [(0xFF0000, 0xFF00, 0xFF, 0x0), (0xFF0000, 0xFF00, 0xFF, 0xFF000000), (0x0, 0x0, 0x0, 0x0)],
                24: [(0xFF0000, 0xFF00, 0xFF)],
                16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)],
            }
            MASK_MODES = {
                (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX",
                (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA",
                (32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
                (24, (0xFF0000, 0xFF00, 0xFF)): "BGR",
                (16, (0xF800, 0x7E0, 0x1F)): "BGR;16",
                (16, (0x7C00, 0x3E0, 0x1F)): "BGR;15",
            }
            if file_info["bits"] in SUPPORTED:
                if file_info["bits"] == 32 and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]]:
                    raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])]
                    self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode
                elif file_info["bits"] in (24, 16) and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]]:
                    raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])]
                else:
                    raise IOError("Unsupported BMP bitfields layout")
            else:
                raise IOError("Unsupported BMP bitfields layout")
        elif file_info["compression"] == self.RAW:
            if file_info["bits"] == 32 and header == 22:  # 32-bit .cur offset
                raw_mode, self.mode = "BGRA", "RGBA"
        else:
            raise IOError("Unsupported BMP compression (%d)" % file_info["compression"])
        # ---------------- Once the header is processed, process the palette/LUT
        if self.mode == "P":  # Paletted for 1, 4 and 8 bit images
            # ----------------------------------------------------- 1-bit images
            if not (0 < file_info["colors"] <= 65536):
                raise IOError("Unsupported BMP Palette size (%d)" % file_info["colors"])
            else:
                padding = file_info["palette_padding"]
                palette = read(padding * file_info["colors"])
                greyscale = True
                indices = (0, 255) if file_info["colors"] == 2 else list(range(file_info["colors"]))
                # ------------------ Check if greyscale and ignore palette if so
                for ind, val in enumerate(indices):
                    rgb = palette[ind * padding : ind * padding + 3]
                    if rgb != o8(val) * 3:
                        greyscale = False
                # -------- If all colors are grey, white or black, ditch palette
                if greyscale:
                    self.mode = "1" if file_info["colors"] == 2 else "L"
                    raw_mode = self.mode
                else:
                    self.mode = "P"
                    self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", palette)

        # ----------------------------- Finally set the tile data for the plugin
        self.info["compression"] = file_info["compression"]
        self.tile = [
            (
                "raw",
                (0, 0, file_info["width"], file_info["height"]),
                offset or self.fp.tell(),
                (raw_mode, ((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3), file_info["direction"]),
            )
        ]
Example #39
0
    def _setup(self):
        "Setup this image object based on current tags"

        if 0xBC01 in self.tag:
            raise IOError("Windows Media Photo files not yet supported")

        getscalar = self.tag.getscalar

        # extract relevant tags
        self._compression = COMPRESSION_INFO[getscalar(COMPRESSION, 1)]
        self._planar_configuration = getscalar(PLANAR_CONFIGURATION, 1)

        # photometric is a required tag, but not everyone is reading
        # the specification
        photo = getscalar(PHOTOMETRIC_INTERPRETATION, 0)

        fillorder = getscalar(FILLORDER, 1)

        if Image.DEBUG:
            print("*** Summary ***")
            print("- compression:", self._compression)
            print("- photometric_interpretation:", photo)
            print("- planar_configuration:", self._planar_configuration)
            print("- fill_order:", fillorder)

        # size
        xsize = getscalar(IMAGEWIDTH)
        ysize = getscalar(IMAGELENGTH)
        self.size = xsize, ysize

        if Image.DEBUG:
            print("- size:", self.size)

        format = getscalar(SAMPLEFORMAT, 1)

        # mode: check photometric interpretation and bits per pixel
        key = (
            self.tag.prefix, photo, format, fillorder,
            self.tag.get(BITSPERSAMPLE, (1,)),
            self.tag.get(EXTRASAMPLES, ())
            )
        if Image.DEBUG:
            print("format key:", key)
        try:
            self.mode, rawmode = OPEN_INFO[key]
        except KeyError:
            if Image.DEBUG:
                print("- unsupported format")
            raise SyntaxError("unknown pixel mode")

        if Image.DEBUG:
            print("- raw mode:", rawmode)
            print("- pil mode:", self.mode)

        self.info["compression"] = self._compression

        xres = getscalar(X_RESOLUTION, (1, 1))
        yres = getscalar(Y_RESOLUTION, (1, 1))

        if xres and not isinstance(xres, tuple):
            xres = (xres, 1.)
        if yres and not isinstance(yres, tuple):
            yres = (yres, 1.)
        if xres and yres:
            xres = xres[0] / (xres[1] or 1)
            yres = yres[0] / (yres[1] or 1)
            resunit = getscalar(RESOLUTION_UNIT, 1)
            if resunit == 2: # dots per inch
                self.info["dpi"] = xres, yres
            elif resunit == 3: # dots per centimeter. convert to dpi
                self.info["dpi"] = xres * 2.54, yres * 2.54
            else: # No absolute unit of measurement
                self.info["resolution"] = xres, yres

        # build tile descriptors
        x = y = l = 0
        self.tile = []
        if STRIPOFFSETS in self.tag:
            # striped image
            offsets = self.tag[STRIPOFFSETS]
            h = getscalar(ROWSPERSTRIP, ysize)
            w = self.size[0]
            if self._compression in ["tiff_ccitt", "group3",
                                     "group4", "tiff_raw_16"]:
                ## if Image.DEBUG:
                ##     print "Activating g4 compression for whole file"

                # Decoder expects entire file as one tile.
                # There's a buffer size limit in load (64k)
                # so large g4 images will fail if we use that
                # function.
                #
                # Setup the one tile for the whole image, then
                # replace the existing load function with our
                # _load_libtiff function.

                self.load = self._load_libtiff

                # To be nice on memory footprint, if there's a
                # file descriptor, use that instead of reading
                # into a string in python.

                # libtiff closes the file descriptor, so pass in a dup.
                try:
                    fp = hasattr(self.fp, "fileno") and os.dup(self.fp.fileno())
                except IOError:
                    # io.BytesIO have a fileno, but returns an IOError if
                    # it doesn't use a file descriptor.
                    fp = False

                # Offset in the tile tuple is 0, we go from 0,0 to
                # w,h, and we only do this once -- eds
                a = (rawmode, self._compression, fp )
                self.tile.append(
                    (self._compression,
                     (0, 0, w, ysize),
                     0, a))
                a = None

            else:
                for i in range(len(offsets)):
                    a = self._decoder(rawmode, l, i)
                    self.tile.append(
                        (self._compression,
                        (0, min(y, ysize), w, min(y+h, ysize)),
                        offsets[i], a))
                    if Image.DEBUG:
                        print ("tiles: ", self.tile)
                    y = y + h
                    if y >= self.size[1]:
                        x = y = 0
                        l = l + 1
                    a = None
        elif TILEOFFSETS in self.tag:
            # tiled image
            w = getscalar(322)
            h = getscalar(323)
            a = None
            for o in self.tag[TILEOFFSETS]:
                if not a:
                    a = self._decoder(rawmode, l)
                # FIXME: this doesn't work if the image size
                # is not a multiple of the tile size...
                self.tile.append(
                    (self._compression,
                    (x, y, x+w, y+h),
                    o, a))
                x = x + w
                if x >= self.size[0]:
                    x, y = 0, y + h
                    if y >= self.size[1]:
                        x = y = 0
                        l = l + 1
                        a = None
        else:
            if Image.DEBUG:
                print("- unsupported data organization")
            raise SyntaxError("unknown data organization")

        # fixup palette descriptor

        if self.mode == "P":
            palette = [o8(a // 256) for a in self.tag[COLORMAP]]
            self.palette = ImagePalette.raw("RGB;L", b"".join(palette))
Example #40
0
    def _open(self):

        if not _accept(self.fp.read(9)):
            raise SyntaxError("not an XPM file")

        # skip forward to next string
        while True:
            s = self.fp.readline()
            if not s:
                raise SyntaxError("broken XPM file")
            m = xpm_head.match(s)
            if m:
                break

        self.size = int(m.group(1)), int(m.group(2))

        pal = int(m.group(3))
        bpp = int(m.group(4))

        if pal > 256 or bpp != 1:
            raise ValueError("cannot read this XPM file")

        #
        # load palette description

        palette = [b"\0\0\0"] * 256

        for i in range(pal):

            s = self.fp.readline()
            if s[-2:] == b'\r\n':
                s = s[:-2]
            elif s[-1:] in b'\r\n':
                s = s[:-1]

            c = i8(s[1])
            s = s[2:-2].split()

            for i in range(0, len(s), 2):

                if s[i] == b"c":

                    # process colour key
                    rgb = s[i+1]
                    if rgb == b"None":
                        self.info["transparency"] = c
                    elif rgb[0:1] == b"#":
                        # FIXME: handle colour names (see ImagePalette.py)
                        rgb = int(rgb[1:], 16)
                        palette[c] = o8((rgb >> 16) & 255) +\
                                     o8((rgb >> 8) & 255) +\
                                     o8(rgb & 255)
                    else:
                        # unknown colour
                        raise ValueError("cannot read this XPM file")
                    break

            else:

                # missing colour key
                raise ValueError("cannot read this XPM file")

        self.mode = "P"
        self.palette = ImagePalette.raw("RGB", b"".join(palette))

        self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), ("P", 0, 1))]
Example #41
0
    def _setup(self):
        "Setup this image object based on current tags"

        if 0xBC01 in self.tag:
            raise IOError("Windows Media Photo files not yet supported")

        getscalar = self.tag.getscalar

        # extract relevant tags
        self._compression = COMPRESSION_INFO[getscalar(COMPRESSION, 1)]
        self._planar_configuration = getscalar(PLANAR_CONFIGURATION, 1)

        # photometric is a required tag, but not everyone is reading
        # the specification
        photo = getscalar(PHOTOMETRIC_INTERPRETATION, 0)

        fillorder = getscalar(FILLORDER, 1)

        if Image.DEBUG:
            print("*** Summary ***")
            print("- compression:", self._compression)
            print("- photometric_interpretation:", photo)
            print("- planar_configuration:", self._planar_configuration)
            print("- fill_order:", fillorder)

        # size
        xsize = getscalar(IMAGEWIDTH)
        ysize = getscalar(IMAGELENGTH)
        self.size = xsize, ysize

        if Image.DEBUG:
            print("- size:", self.size)

        format = getscalar(SAMPLEFORMAT, 1)

        # mode: check photometric interpretation and bits per pixel
        key = (self.tag.prefix, photo, format, fillorder,
               self.tag.get(BITSPERSAMPLE,
                            (1, )), self.tag.get(EXTRASAMPLES, ()))
        if Image.DEBUG:
            print("format key:", key)
        try:
            self.mode, rawmode = OPEN_INFO[key]
        except KeyError:
            if Image.DEBUG:
                print("- unsupported format")
            raise SyntaxError("unknown pixel mode")

        if Image.DEBUG:
            print("- raw mode:", rawmode)
            print("- pil mode:", self.mode)

        self.info["compression"] = self._compression

        xres = getscalar(X_RESOLUTION, (1, 1))
        yres = getscalar(Y_RESOLUTION, (1, 1))

        if xres and not isinstance(xres, tuple):
            xres = (xres, 1.)
        if yres and not isinstance(yres, tuple):
            yres = (yres, 1.)
        if xres and yres:
            xres = xres[0] / (xres[1] or 1)
            yres = yres[0] / (yres[1] or 1)
            resunit = getscalar(RESOLUTION_UNIT, 1)
            if resunit == 2:  # dots per inch
                self.info["dpi"] = xres, yres
            elif resunit == 3:  # dots per centimeter. convert to dpi
                self.info["dpi"] = xres * 2.54, yres * 2.54
            else:  # No absolute unit of measurement
                self.info["resolution"] = xres, yres

        # build tile descriptors
        x = y = l = 0
        self.tile = []
        if STRIPOFFSETS in self.tag:
            # striped image
            offsets = self.tag[STRIPOFFSETS]
            h = getscalar(ROWSPERSTRIP, ysize)
            w = self.size[0]
            if READ_LIBTIFF or self._compression in [
                    "tiff_ccitt", "group3", "group4", "tiff_jpeg",
                    "tiff_adobe_deflate", "tiff_thunderscan", "tiff_deflate",
                    "tiff_sgilog", "tiff_sgilog24", "tiff_raw_16"
            ]:
                # if Image.DEBUG:
                #     print "Activating g4 compression for whole file"

                # Decoder expects entire file as one tile.
                # There's a buffer size limit in load (64k)
                # so large g4 images will fail if we use that
                # function.
                #
                # Setup the one tile for the whole image, then
                # replace the existing load function with our
                # _load_libtiff function.

                self.load = self._load_libtiff

                # To be nice on memory footprint, if there's a
                # file descriptor, use that instead of reading
                # into a string in python.

                # libtiff closes the file descriptor, so pass in a dup.
                try:
                    fp = hasattr(self.fp, "fileno") and \
                        os.dup(self.fp.fileno())
                except IOError:
                    # io.BytesIO have a fileno, but returns an IOError if
                    # it doesn't use a file descriptor.
                    fp = False

                # libtiff handles the fillmode for us, so 1;IR should
                # actually be 1;I. Including the R double reverses the
                # bits, so stripes of the image are reversed.  See
                # https://github.com/python-pillow/Pillow/issues/279
                if fillorder == 2:
                    key = (self.tag.prefix, photo, format, 1,
                           self.tag.get(BITSPERSAMPLE,
                                        (1, )), self.tag.get(EXTRASAMPLES, ()))
                    if Image.DEBUG:
                        print("format key:", key)
                    # this should always work, since all the
                    # fillorder==2 modes have a corresponding
                    # fillorder=1 mode
                    self.mode, rawmode = OPEN_INFO[key]
                # libtiff always returns the bytes in native order.
                # we're expecting image byte order. So, if the rawmode
                # contains I;16, we need to convert from native to image
                # byte order.
                if self.mode in ('I;16B', 'I;16') and 'I;16' in rawmode:
                    rawmode = 'I;16N'

                # Offset in the tile tuple is 0, we go from 0,0 to
                # w,h, and we only do this once -- eds
                a = (rawmode, self._compression, fp)
                self.tile.append((self._compression, (0, 0, w, ysize), 0, a))
                a = None

            else:
                for i in range(len(offsets)):
                    a = self._decoder(rawmode, l, i)
                    self.tile.append(
                        (self._compression, (0, min(y, ysize), w,
                                             min(y + h,
                                                 ysize)), offsets[i], a))
                    if Image.DEBUG:
                        print("tiles: ", self.tile)
                    y = y + h
                    if y >= self.size[1]:
                        x = y = 0
                        l += 1
                    a = None
        elif TILEOFFSETS in self.tag:
            # tiled image
            w = getscalar(322)
            h = getscalar(323)
            a = None
            for o in self.tag[TILEOFFSETS]:
                if not a:
                    a = self._decoder(rawmode, l)
                # FIXME: this doesn't work if the image size
                # is not a multiple of the tile size...
                self.tile.append(
                    (self._compression, (x, y, x + w, y + h), o, a))
                x = x + w
                if x >= self.size[0]:
                    x, y = 0, y + h
                    if y >= self.size[1]:
                        x = y = 0
                        l += 1
                        a = None
        else:
            if Image.DEBUG:
                print("- unsupported data organization")
            raise SyntaxError("unknown data organization")

        # fixup palette descriptor

        if self.mode == "P":
            palette = [o8(a // 256) for a in self.tag[COLORMAP]]
            self.palette = ImagePalette.raw("RGB;L", b"".join(palette))
Example #42
0
    def _setup(self):
        "Setup this image object based on current tags"

        if 0xBC01 in self.tag_v2:
            raise IOError("Windows Media Photo files not yet supported")

        # extract relevant tags
        self._compression = COMPRESSION_INFO[self.tag_v2.get(COMPRESSION, 1)]
        self._planar_configuration = self.tag_v2.get(PLANAR_CONFIGURATION, 1)

        # photometric is a required tag, but not everyone is reading
        # the specification
        photo = self.tag_v2.get(PHOTOMETRIC_INTERPRETATION, 0)

        fillorder = self.tag_v2.get(FILLORDER, 1)

        if DEBUG:
            print("*** Summary ***")
            print("- compression:", self._compression)
            print("- photometric_interpretation:", photo)
            print("- planar_configuration:", self._planar_configuration)
            print("- fill_order:", fillorder)

        # size
        xsize = self.tag_v2.get(IMAGEWIDTH)
        ysize = self.tag_v2.get(IMAGELENGTH)
        self.size = xsize, ysize

        if DEBUG:
            print("- size:", self.size)

        format = self.tag_v2.get(SAMPLEFORMAT, (1,))
        if len(format) > 1 and max(format) == min(format) == 1:
            # SAMPLEFORMAT is properly per band, so an RGB image will
            # be (1,1,1).  But, we don't support per band pixel types,
            # and anything more than one band is a uint8. So, just
            # take the first element. Revisit this if adding support
            # for more exotic images.
            format = (1,)

        # mode: check photometric interpretation and bits per pixel
        key = (
            self.tag_v2.prefix, photo, format, fillorder,
            self.tag_v2.get(BITSPERSAMPLE, (1,)),
            self.tag_v2.get(EXTRASAMPLES, ())
            )
        if DEBUG:
            print("format key:", key)
        try:
            self.mode, rawmode = OPEN_INFO[key]
        except KeyError:
            if DEBUG:
                print("- unsupported format")
            raise SyntaxError("unknown pixel mode")

        if DEBUG:
            print("- raw mode:", rawmode)
            print("- pil mode:", self.mode)

        self.info["compression"] = self._compression

        xres = self.tag_v2.get(X_RESOLUTION, (1, 1))
        yres = self.tag_v2.get(Y_RESOLUTION, (1, 1))

        if xres and not isinstance(xres, tuple):
            xres = (xres, 1.)
        if yres and not isinstance(yres, tuple):
            yres = (yres, 1.)
        if xres and yres:
            xres = xres[0] / (xres[1] or 1)
            yres = yres[0] / (yres[1] or 1)
            resunit = self.tag_v2.get(RESOLUTION_UNIT, 1)
            if resunit == 2:  # dots per inch
                self.info["dpi"] = xres, yres
            elif resunit == 3:  # dots per centimeter. convert to dpi
                self.info["dpi"] = xres * 2.54, yres * 2.54
            else:  # No absolute unit of measurement
                self.info["resolution"] = xres, yres

        # build tile descriptors
        x = y = l = 0
        self.tile = []
        if STRIPOFFSETS in self.tag_v2:
            # striped image
            offsets = self.tag_v2[STRIPOFFSETS]
            h = self.tag_v2.get(ROWSPERSTRIP, ysize)
            w = self.size[0]
            if READ_LIBTIFF or self._compression in ["tiff_ccitt", "group3",
                                                     "group4", "tiff_jpeg",
                                                     "tiff_adobe_deflate",
                                                     "tiff_thunderscan",
                                                     "tiff_deflate",
                                                     "tiff_sgilog",
                                                     "tiff_sgilog24",
                                                     "tiff_raw_16"]:
                # if DEBUG:
                #     print "Activating g4 compression for whole file"

                # Decoder expects entire file as one tile.
                # There's a buffer size limit in load (64k)
                # so large g4 images will fail if we use that
                # function.
                #
                # Setup the one tile for the whole image, then
                # replace the existing load function with our
                # _load_libtiff function.

                self.load = self._load_libtiff

                # To be nice on memory footprint, if there's a
                # file descriptor, use that instead of reading
                # into a string in python.

                # libtiff closes the file descriptor, so pass in a dup.
                try:
                    fp = hasattr(self.fp, "fileno") and \
                        os.dup(self.fp.fileno())
                    # flush the file descriptor, prevents error on pypy 2.4+
                    # should also eliminate the need for fp.tell for py3
                    # in _seek
                    if hasattr(self.fp, "flush"):
                        self.fp.flush()
                except IOError:
                    # io.BytesIO have a fileno, but returns an IOError if
                    # it doesn't use a file descriptor.
                    fp = False

                # libtiff handles the fillmode for us, so 1;IR should
                # actually be 1;I. Including the R double reverses the
                # bits, so stripes of the image are reversed.  See
                # https://github.com/python-pillow/Pillow/issues/279
                if fillorder == 2:
                    key = (
                        self.tag_v2.prefix, photo, format, 1,
                        self.tag_v2.get(BITSPERSAMPLE, (1,)),
                        self.tag_v2.get(EXTRASAMPLES, ())
                        )
                    if DEBUG:
                        print("format key:", key)
                    # this should always work, since all the
                    # fillorder==2 modes have a corresponding
                    # fillorder=1 mode
                    self.mode, rawmode = OPEN_INFO[key]
                # libtiff always returns the bytes in native order.
                # we're expecting image byte order. So, if the rawmode
                # contains I;16, we need to convert from native to image
                # byte order.
                if self.mode in ('I;16B', 'I;16') and 'I;16' in rawmode:
                    rawmode = 'I;16N'

                # Offset in the tile tuple is 0, we go from 0,0 to
                # w,h, and we only do this once -- eds
                a = (rawmode, self._compression, fp)
                self.tile.append(
                    (self._compression,
                     (0, 0, w, ysize),
                     0, a))
                a = None

            else:
                for i in range(len(offsets)):
                    a = self._decoder(rawmode, l, i)
                    self.tile.append(
                        (self._compression,
                            (0, min(y, ysize), w, min(y+h, ysize)),
                            offsets[i], a))
                    if DEBUG:
                        print("tiles: ", self.tile)
                    y = y + h
                    if y >= self.size[1]:
                        x = y = 0
                        l += 1
                    a = None
        elif TILEOFFSETS in self.tag_v2:
            # tiled image
            w = self.tag_v2.get(322)
            h = self.tag_v2.get(323)
            a = None
            for o in self.tag_v2[TILEOFFSETS]:
                if not a:
                    a = self._decoder(rawmode, l)
                # FIXME: this doesn't work if the image size
                # is not a multiple of the tile size...
                self.tile.append(
                    (self._compression,
                        (x, y, x+w, y+h),
                        o, a))
                x = x + w
                if x >= self.size[0]:
                    x, y = 0, y + h
                    if y >= self.size[1]:
                        x = y = 0
                        l += 1
                        a = None
        else:
            if DEBUG:
                print("- unsupported data organization")
            raise SyntaxError("unknown data organization")

        # fixup palette descriptor

        if self.mode == "P":
            palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]]
            self.palette = ImagePalette.raw("RGB;L", b"".join(palette))
Example #43
0
    def _open(self):

        read = self.fp.read

        #
        # header

        s = read(26)
        if s[:4] != b"8BPS" or i16(s[4:]) != 1:
            raise SyntaxError("not a PSD file")

        psd_bits = i16(s[22:])
        psd_channels = i16(s[12:])
        psd_mode = i16(s[24:])

        mode, channels = MODES[(psd_mode, psd_bits)]

        if channels > psd_channels:
            raise IOError("not enough channels")

        self.mode = mode
        self.size = i32(s[18:]), i32(s[14:])

        #
        # color mode data

        size = i32(read(4))
        if size:
            data = read(size)
            if mode == "P" and size == 768:
                self.palette = ImagePalette.raw("RGB;L", data)

        #
        # image resources

        self.resources = []

        size = i32(read(4))
        if size:
            # load resources
            end = self.fp.tell() + size
            while self.fp.tell() < end:
                signature = read(4)
                id = i16(read(2))
                name = read(i8(read(1)))
                if not (len(name) & 1):
                    read(1)  # padding
                data = read(i32(read(4)))
                if (len(data) & 1):
                    read(1)  # padding
                self.resources.append((id, name, data))
                if id == 1039:  # ICC profile
                    self.info["icc_profile"] = data

        #
        # layer and mask information

        self.layers = []

        size = i32(read(4))
        if size:
            end = self.fp.tell() + size
            size = i32(read(4))
            if size:
                self.layers = _layerinfo(self.fp)
            self.fp.seek(end)

        #
        # image descriptor

        self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels)

        # keep the file open
        self._fp = self.fp
        self.frame = 0
    def seek(self, frame):

        if frame == 0:
            # rewind
            self.__offset = 0
            self.dispose = None
            self.__frame = -1
            self.__fp.seek(self.__rewind)

        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 = self.dispose
            self.dispose = None

        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
                    try:
                        # disposal methods
                        if flags & 8:
                            # replace with background colour
                            self.dispose = Image.core.fill("P", self.size, self.info["background"])
                        elif flags & 16:
                            # replace with previous contents
                            self.dispose = self.im.copy()
                    except (AttributeError, KeyError):
                        pass
                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:])
                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)

        if not self.tile:
            # self.__fp = None
            raise EOFError("no more images in GIF file")

        self.mode = "L"
        if self.palette:
            self.mode = "P"
    def _open(self):

        # process header
        s = self.fp.read(18)

        id = i8(s[0])

        colormaptype = i8(s[1])
        imagetype = i8(s[2])

        depth = i8(s[16])

        flags = i8(s[17])

        self.size = i16(s[12:]), i16(s[14:])

        # validate header fields
        if id != 0 or colormaptype not in (0, 1) or\
           self.size[0] <= 0 or self.size[1] <= 0 or\
           depth not in (1, 8, 16, 24, 32):
            raise SyntaxError("not a TGA file")

        # image mode
        if imagetype in (3, 11):
            self.mode = "L"
            if depth == 1:
                self.mode = "1"  # ???
        elif imagetype in (1, 9):
            self.mode = "P"
        elif imagetype in (2, 10):
            self.mode = "RGB"
            if depth == 32:
                self.mode = "RGBA"
        else:
            raise SyntaxError("unknown TGA mode")

        # orientation
        orientation = flags & 0x30
        if orientation == 0x20:
            orientation = 1
        elif not orientation:
            orientation = -1
        else:
            raise SyntaxError("unknown TGA orientation")

        self.info["orientation"] = orientation

        if imagetype & 8:
            self.info["compression"] = "tga_rle"

        if colormaptype:
            # read palette
            start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:])
            if mapdepth == 16:
                self.palette = ImagePalette.raw(
                    "BGR;16", b"\0" * 2 * start + self.fp.read(2 * size))
            elif mapdepth == 24:
                self.palette = ImagePalette.raw(
                    "BGR", b"\0" * 3 * start + self.fp.read(3 * size))
            elif mapdepth == 32:
                self.palette = ImagePalette.raw(
                    "BGRA", b"\0" * 4 * start + self.fp.read(4 * size))

        # setup tile descriptor
        try:
            rawmode = MODES[(imagetype & 7, depth)]
            if imagetype & 8:
                # compressed
                self.tile = [("tga_rle", (0, 0) + self.size, self.fp.tell(),
                              (rawmode, orientation, depth))]
            else:
                self.tile = [("raw", (0, 0) + self.size, self.fp.tell(),
                              (rawmode, 0, orientation))]
        except KeyError:
            pass  # cannot decode
Example #46
0
    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) == 254:
                    #
                    # comment extension
                    #
                    self.info["comment"] = block
                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
                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"
Example #47
0
    def _bitmap(self, header=0, offset=0):

        if header:
            self.fp.seek(header)

        read = self.fp.read

        # CORE/INFO
        s = read(4)
        s = s + ImageFile._safe_read(self.fp, i32(s) - 4)

        if len(s) == 12:

            # OS/2 1.0 CORE
            bits = i16(s[10:])
            self.size = i16(s[4:]), i16(s[6:])
            compression = 0
            lutsize = 3
            colors = 0
            direction = -1

        elif len(s) in [40, 64, 108, 124]:

            # WIN 3.1 or OS/2 2.0 INFO
            bits = i16(s[14:])
            self.size = i32(s[4:]), i32(s[8:])
            compression = i32(s[16:])
            pxperm = (i32(s[24:]), i32(s[28:]))  # Pixels per meter
            lutsize = 4
            colors = i32(s[32:])
            direction = -1
            if i8(s[11]) == 0xFF:
                # upside-down storage
                self.size = self.size[0], 2 ** 32 - self.size[1]
                direction = 0

            self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), pxperm))

        else:
            raise IOError("Unsupported BMP header type (%d)" % len(s))

        if (self.size[0] * self.size[1]) > 2 ** 31:
            # Prevent DOS for > 2gb images
            raise IOError("Unsupported BMP Size: (%dx%d)" % self.size)

        if not colors:
            colors = 1 << bits

        # MODE
        try:
            self.mode, rawmode = BIT2MODE[bits]
        except KeyError:
            raise IOError("Unsupported BMP pixel depth (%d)" % bits)

        if compression == 3:
            # BI_BITFIELDS compression
            mask = i32(read(4)), i32(read(4)), i32(read(4))
            if bits == 32 and mask == (0xFF0000, 0x00FF00, 0x0000FF):
                rawmode = "BGRX"
            elif bits == 16 and mask == (0x00F800, 0x0007E0, 0x00001F):
                rawmode = "BGR;16"
            elif bits == 16 and mask == (0x007C00, 0x0003E0, 0x00001F):
                rawmode = "BGR;15"
            else:
                # print bits, map(hex, mask)
                raise IOError("Unsupported BMP bitfields layout")
        elif compression != 0:
            raise IOError("Unsupported BMP compression (%d)" % compression)

        # LUT
        if self.mode == "P":
            palette = []
            greyscale = 1
            if colors == 2:
                indices = (0, 255)
            elif colors > 2 ** 16 or colors <= 0:  # We're reading a i32.
                raise IOError("Unsupported BMP Palette size (%d)" % colors)
            else:
                indices = list(range(colors))
            for i in indices:
                rgb = read(lutsize)[:3]
                if rgb != o8(i) * 3:
                    greyscale = 0
                palette.append(rgb)
            if greyscale:
                if colors == 2:
                    self.mode = rawmode = "1"
                else:
                    self.mode = rawmode = "L"
            else:
                self.mode = "P"
                self.palette = ImagePalette.raw("BGR", b"".join(palette))

        if not offset:
            offset = self.fp.tell()

        self.tile = [
            ("raw", (0, 0) + self.size, offset, (rawmode, ((self.size[0] * bits + 31) >> 3) & (~3), direction))
        ]

        self.info["compression"] = compression
Example #48
0
    def _open(self):

        # The Sun Raster file header is 32 bytes in length and has the following format:

        #     typedef struct _SunRaster
        #     {
        #         DWORD MagicNumber;      /* Magic (identification) number */
        #         DWORD Width;            /* Width of image in pixels */
        #         DWORD Height;           /* Height of image in pixels */
        #         DWORD Depth;            /* Number of bits per pixel */
        #         DWORD Length;           /* Size of image data in bytes */
        #         DWORD Type;             /* Type of raster file */
        #         DWORD ColorMapType;     /* Type of color map */
        #         DWORD ColorMapLength;   /* Size of the color map in bytes */
        #     } SUNRASTER;

        # HEAD
        s = self.fp.read(32)
        if i32(s) != 0x59a66a95:
            raise InvalidFileType("not a SUN raster file")

        offset = 32

        self.size = i32(s[4:8]), i32(s[8:12])

        depth = i32(s[12:16])
        data_length = i32(s[16:20])  # unreliable, ignore.
        file_type = i32(s[20:24])
        palette_type = i32(s[24:28])  # 0: None, 1: RGB, 2: Raw/arbitrary
        palette_length = i32(s[28:32])

        if depth == 1:
            self.mode, rawmode = "1", "1;I"
        elif depth == 4:
            self.mode, rawmode = "L", "L;4"
        elif depth == 8:
            self.mode = rawmode = "L"
        elif depth == 24:
            if file_type == 3:
                self.mode, rawmode = "RGB", "RGB"
            else:
                self.mode, rawmode = "RGB", "BGR"
        elif depth == 32:
            if file_type == 3:
                self.mode, rawmode = 'RGB', 'RGBX'
            else:
                self.mode, rawmode = 'RGB', 'BGRX'
        else:
            raise NotImplementedError("Unsupported Mode/Bit Depth")

        if palette_length:
            if palette_length > 1024:
                raise NotImplementedError("Unsupported Color Palette Length")

            if palette_type != 1:
                raise NotImplementedError("Unsupported Palette Type")

            offset = offset + palette_length
            self.palette = ImagePalette.raw("RGB;L",
                                            self.fp.read(palette_length))
            if self.mode == "L":
                self.mode = "P"
                rawmode = rawmode.replace('L', 'P')

        # 16 bit boundaries on stride
        stride = ((self.size[0] * depth + 15) // 16) * 2

        # file type: Type is the version (or flavor) of the bitmap
        # file. The following values are typically found in the Type
        # field:
        # 0000h	Old
        # 0001h	Standard
        # 0002h	Byte-encoded
        # 0003h	RGB format
        # 0004h	TIFF format
        # 0005h	IFF format
        # FFFFh	Experimental

        # Old and standard are the same, except for the length tag.
        # byte-encoded is run-length-encoded
        # RGB looks similar to standard, but RGB byte order
        # TIFF and IFF mean that they were converted from T/IFF
        # Experimental means that it's something else.
        # (http://www.fileformat.info/format/sunraster/egff.htm)

        if file_type in (0, 1, 3, 4, 5):
            self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride))
                         ]
        elif file_type == 2:
            self.tile = [("sun_rle", (0, 0) + self.size, offset, rawmode)]
        else:
            raise NotImplementedError('Unsupported Sun Raster file type')
Example #49
0
    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) == 254:
                    #
                    # comment extension
                    #
                    self.info["comment"] = block
                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
                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"
Example #50
0
    def _setup(self):
        "Setup this image object based on current tags"

        if 0xBC01 in self.tag:
            raise IOError("Windows Media Photo files not yet supported")

        getscalar = self.tag.getscalar

        # extract relevant tags
        self._compression = COMPRESSION_INFO[getscalar(COMPRESSION, 1)]
        self._planar_configuration = getscalar(PLANAR_CONFIGURATION, 1)

        # photometric is a required tag, but not everyone is reading
        # the specification
        photo = getscalar(PHOTOMETRIC_INTERPRETATION, 0)

        fillorder = getscalar(FILLORDER, 1)

        if Image.DEBUG:
            print("*** Summary ***")
            print("- compression:", self._compression)
            print("- photometric_interpretation:", photo)
            print("- planar_configuration:", self._planar_configuration)
            print("- fill_order:", fillorder)

        # size
        xsize = getscalar(IMAGEWIDTH)
        ysize = getscalar(IMAGELENGTH)
        self.size = xsize, ysize

        if Image.DEBUG:
            print("- size:", self.size)

        format = getscalar(SAMPLEFORMAT, 1)

        # mode: check photometric interpretation and bits per pixel
        key = (
            self.tag.prefix, photo, format, fillorder,
            self.tag.get(BITSPERSAMPLE, (1,)),
            self.tag.get(EXTRASAMPLES, ())
            )
        if Image.DEBUG:
            print("format key:", key)
        try:
            self.mode, rawmode = OPEN_INFO[key]
        except KeyError:
            if Image.DEBUG:
                print("- unsupported format")
            raise SyntaxError("unknown pixel mode")

        if Image.DEBUG:
            print("- raw mode:", rawmode)
            print("- pil mode:", self.mode)

        self.info["compression"] = self._compression

        xres = getscalar(X_RESOLUTION, (1, 1))
        yres = getscalar(Y_RESOLUTION, (1, 1))

        if xres and yres:
            xres = xres[0] / (xres[1] or 1)
            yres = yres[0] / (yres[1] or 1)
            resunit = getscalar(RESOLUTION_UNIT, 1)
            if resunit == 2: # dots per inch
                self.info["dpi"] = xres, yres
            elif resunit == 3: # dots per centimeter. convert to dpi
                self.info["dpi"] = xres * 2.54, yres * 2.54
            else: # No absolute unit of measurement
                self.info["resolution"] = xres, yres

        # build tile descriptors
        x = y = l = 0
        self.tile = []
        if STRIPOFFSETS in self.tag:
            # striped image
            h = getscalar(ROWSPERSTRIP, ysize)
            w = self.size[0]
            a = None
            for o in self.tag[STRIPOFFSETS]:
                if not a:
                    a = self._decoder(rawmode, l)
                self.tile.append(
                    (self._compression,
                    (0, min(y, ysize), w, min(y+h, ysize)),
                    o, a))
                y = y + h
                if y >= self.size[1]:
                    x = y = 0
                    l = l + 1
                    a = None
        elif TILEOFFSETS in self.tag:
            # tiled image
            w = getscalar(322)
            h = getscalar(323)
            a = None
            for o in self.tag[TILEOFFSETS]:
                if not a:
                    a = self._decoder(rawmode, l)
                # FIXME: this doesn't work if the image size
                # is not a multiple of the tile size...
                self.tile.append(
                    (self._compression,
                    (x, y, x+w, y+h),
                    o, a))
                x = x + w
                if x >= self.size[0]:
                    x, y = 0, y + h
                    if y >= self.size[1]:
                        x = y = 0
                        l = l + 1
                        a = None
        else:
            if Image.DEBUG:
                print("- unsupported data organization")
            raise SyntaxError("unknown data organization")

        # fixup palette descriptor

        if self.mode == "P":
            palette = [o8(a // 256) for a in self.tag[COLORMAP]]
            self.palette = ImagePalette.raw("RGB;L", b"".join(palette))
Example #51
0
    def _bitmap(self, header=0, offset=0):
        """ Read relevant info about the BMP """
        read, seek = self.fp.read, self.fp.seek
        if header:
            seek(header)
        file_info = {}
        file_info['header_size'] = i32(read(4))  # read bmp header size @offset 14 (this is part of the header size)
        file_info['direction'] = -1
        # --------------------- If requested, read header at a specific position
        header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4)  # read the rest of the bmp header, without its size
        # --------------------------------------------------- IBM OS/2 Bitmap v1
        # ------ This format has different offsets because of width/height types
        if file_info['header_size'] == 12:
            file_info['width'] = i16(header_data[0:2])
            file_info['height'] = i16(header_data[2:4])
            file_info['planes'] = i16(header_data[4:6])
            file_info['bits'] = i16(header_data[6:8])
            file_info['compression'] = self.RAW
            file_info['palette_padding'] = 3
        # ---------------------------------------------- Windows Bitmap v2 to v5
        elif file_info['header_size'] in (40, 64, 108, 124):  # v3, OS/2 v2, v4, v5
            if file_info['header_size'] >= 40:  # v3 and OS/2
                file_info['y_flip'] = i8(header_data[7]) == 0xff
                file_info['direction'] = 1 if file_info['y_flip'] else -1
                file_info['width'] = i32(header_data[0:4])
                file_info['height'] = i32(header_data[4:8]) if not file_info['y_flip'] else 2**32 - i32(header_data[4:8])
                file_info['planes'] = i16(header_data[8:10])
                file_info['bits'] = i16(header_data[10:12])
                file_info['compression'] = i32(header_data[12:16])
                file_info['data_size'] = i32(header_data[16:20])  # byte size of pixel data
                file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28]))
                file_info['colors'] = i32(header_data[28:32])
                file_info['palette_padding'] = 4
                self.info["dpi"] = tuple(
                    map(lambda x: int(math.ceil(x / 39.3701)),
                        file_info['pixels_per_meter']))
                if file_info['compression'] == self.BITFIELDS:
                    if len(header_data) >= 52:
                        for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']):
                            file_info[mask] = i32(header_data[36+idx*4:40+idx*4])
                    else:
                        # 40 byte headers only have the three components in the bitfields masks,
                        # ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
                        # See also https://github.com/python-pillow/Pillow/issues/1293
                        # There is a 4th component in the RGBQuad, in the alpha location, but it
                        # is listed as a reserved component, and it is not generally an alpha channel
                        file_info['a_mask'] = 0x0
                        for mask in ['r_mask', 'g_mask', 'b_mask']:
                            file_info[mask] = i32(read(4))
                    file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'])
                    file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask'])
        else:
            raise IOError("Unsupported BMP header type (%d)" % 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']
        # -------- 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
        if file_info['width'] * file_info['height'] > 2**31:
            raise IOError("Unsupported BMP Size: (%dx%d)" % self.size)
        # ----------------------- Check bit depth for unusual unsupported values
        self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None))
        if self.mode is None:
            raise IOError("Unsupported BMP pixel depth (%d)" % file_info['bits'])
        # ----------------- Process BMP with Bitfields compression (not palette)
        if file_info['compression'] == self.BITFIELDS:
            SUPPORTED = {
                32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0), (0xff000000, 0xff0000, 0xff00, 0x0) ],
                24: [(0xff0000, 0xff00, 0xff)],
                16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
            }
            MASK_MODES = {
                (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
                (32, (0xff000000, 0xff0000, 0xff00, 0x0)): "XBGR",
                (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
                (32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
                (24, (0xff0000, 0xff00, 0xff)): "BGR",
                (16, (0xf800, 0x7e0, 0x1f)): "BGR;16",
                (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"
            }
            if file_info['bits'] in SUPPORTED:
                if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]:
                    raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])]
                    self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode
                elif file_info['bits'] in (24, 16) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]:
                    raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])]
                else:
                    raise IOError("Unsupported BMP bitfields layout")
            else:
                raise IOError("Unsupported BMP bitfields layout")
        elif file_info['compression'] == self.RAW:
            if file_info['bits'] == 32 and header == 22:  # 32-bit .cur offset
                raw_mode, self.mode = "BGRA", "RGBA"
        else:
            raise IOError("Unsupported BMP compression (%d)" % file_info['compression'])
        # ---------------- Once the header is processed, process the palette/LUT
        if self.mode == "P":  # Paletted for 1, 4 and 8 bit images
            # ----------------------------------------------------- 1-bit images
            if not (0 < file_info['colors'] <= 65536):
                raise IOError("Unsupported BMP Palette size (%d)" % file_info['colors'])
            else:
                padding = file_info['palette_padding']
                palette = read(padding * file_info['colors'])
                greyscale = True
                indices = (0, 255) if file_info['colors'] == 2 else list(range(file_info['colors']))
                # ------------------ Check if greyscale and ignore palette if so
                for ind, val in enumerate(indices):
                    rgb = palette[ind*padding:ind*padding + 3]
                    if rgb != o8(val) * 3:
                        greyscale = False
                # -------- If all colors are grey, white or black, ditch palette
                if greyscale:
                    self.mode = "1" if file_info['colors'] == 2 else "L"
                    raw_mode = self.mode
                else:
                    self.mode = "P"
                    self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", palette)

        # ----------------------------- Finally set the tile data for the plugin
        self.info['compression'] = file_info['compression']
        self.tile = [('raw', (0, 0, file_info['width'], file_info['height']), offset or self.fp.tell(),
                      (raw_mode, ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), file_info['direction'])
                      )]
Example #52
0
    def _open(self):

        # process header
        s = self.fp.read(18)

        idlen = i8(s[0])

        colormaptype = i8(s[1])
        imagetype = i8(s[2])

        depth = i8(s[16])

        flags = i8(s[17])

        self.size = i16(s[12:]), i16(s[14:])

        # validate header fields
        if colormaptype not in (0, 1) or\
           self.size[0] <= 0 or self.size[1] <= 0 or\
           depth not in (1, 8, 16, 24, 32):
            raise SyntaxError("not a TGA file")

        # image mode
        if imagetype in (3, 11):
            self.mode = "L"
            if depth == 1:
                self.mode = "1"  # ???
        elif imagetype in (1, 9):
            self.mode = "P"
        elif imagetype in (2, 10):
            self.mode = "RGB"
            if depth == 32:
                self.mode = "RGBA"
        else:
            raise SyntaxError("unknown TGA mode")

        # orientation
        orientation = flags & 0x30
        if orientation == 0x20:
            orientation = 1
        elif not orientation:
            orientation = -1
        else:
            raise SyntaxError("unknown TGA orientation")

        self.info["orientation"] = orientation

        if imagetype & 8:
            self.info["compression"] = "tga_rle"

        if idlen:
            self.info["id_section"] = self.fp.read(idlen)

        if colormaptype:
            # read palette
            start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:])
            if mapdepth == 16:
                self.palette = ImagePalette.raw(
                    "BGR;16", b"\0"*2*start + self.fp.read(2*size))
            elif mapdepth == 24:
                self.palette = ImagePalette.raw(
                    "BGR", b"\0"*3*start + self.fp.read(3*size))
            elif mapdepth == 32:
                self.palette = ImagePalette.raw(
                    "BGRA", b"\0"*4*start + self.fp.read(4*size))

        # setup tile descriptor
        try:
            rawmode = MODES[(imagetype & 7, depth)]
            if imagetype & 8:
                # compressed
                self.tile = [("tga_rle", (0, 0)+self.size,
                              self.fp.tell(), (rawmode, orientation, depth))]
            else:
                self.tile = [("raw", (0, 0)+self.size,
                              self.fp.tell(), (rawmode, 0, orientation))]
        except KeyError:
            pass  # cannot decode
Example #53
0
    def _open(self):

        # Quick rejection: if there's not an LF among the first
        # 100 bytes, this is (probably) not a text header.

        if not b"\n" in self.fp.read(100):
            raise SyntaxError("not an IM file")
        self.fp.seek(0)

        n = 0

        # Default values
        self.info[MODE] = "L"
        self.info[SIZE] = (512, 512)
        self.info[FRAMES] = 1

        self.rawmode = "L"

        while True:

            s = self.fp.read(1)

            # Some versions of IFUNC uses \n\r instead of \r\n...
            if s == b"\r":
                continue

            if not s or s == b'\0' or s == b'\x1A':
                break

            # FIXME: this may read whole file if not a text file
            s = s + self.fp.readline()

            if len(s) > 100:
                raise SyntaxError("not an IM file")

            if s[-2:] == b'\r\n':
                s = s[:-2]
            elif s[-1:] == b'\n':
                s = s[:-1]

            try:
                m = split.match(s)
            except re.error as v:
                raise SyntaxError("not an IM file")

            if m:

                k, v = m.group(1,2)

                # Don't know if this is the correct encoding, but a decent guess
                # (I guess)
                k = k.decode('latin-1', 'replace')
                v = v.decode('latin-1', 'replace')

                # Convert value as appropriate
                if k in [FRAMES, SCALE, SIZE]:
                    v = v.replace("*", ",")
                    v = tuple(map(number, v.split(",")))
                    if len(v) == 1:
                        v = v[0]
                elif k == MODE and v in OPEN:
                    v, self.rawmode = OPEN[v]

                # Add to dictionary. Note that COMMENT tags are
                # combined into a list of strings.
                if k == COMMENT:
                    if k in self.info:
                        self.info[k].append(v)
                    else:
                        self.info[k] = [v]
                else:
                    self.info[k] = v

                if k in TAGS:
                    n = n + 1

            else:

                raise SyntaxError("Syntax error in IM header: " + s.decode('ascii', 'replace'))

        if not n:
            raise SyntaxError("Not an IM file")

        # Basic attributes
        self.size = self.info[SIZE]
        self.mode = self.info[MODE]

        # Skip forward to start of image data
        while s and s[0:1] != b'\x1A':
            s = self.fp.read(1)
        if not s:
            raise SyntaxError("File truncated")

        if LUT in self.info:
            # convert lookup table to palette or lut attribute
            palette = self.fp.read(768)
            greyscale = 1 # greyscale palette
            linear = 1 # linear greyscale palette
            for i in range(256):
                if palette[i] == palette[i+256] == palette[i+512]:
                    if i8(palette[i]) != i:
                        linear = 0
                else:
                    greyscale = 0
            if self.mode == "L" or self.mode == "LA":
                if greyscale:
                    if not linear:
                        self.lut = [i8(c) for c in palette[:256]]
                else:
                    if self.mode == "L":
                        self.mode = self.rawmode = "P"
                    elif self.mode == "LA":
                        self.mode = self.rawmode = "PA"
                    self.palette = ImagePalette.raw("RGB;L", palette)
            elif self.mode == "RGB":
                if not greyscale or not linear:
                    self.lut = [i8(c) for c in palette]

        self.frame = 0

        self.__offset = offs = self.fp.tell()

        self.__fp = self.fp # FIXME: hack

        if self.rawmode[:2] == "F;":

            # ifunc95 formats
            try:
                # use bit decoder (if necessary)
                bits = int(self.rawmode[2:])
                if bits not in [8, 16, 32]:
                    self.tile = [("bit", (0,0)+self.size, offs,
                                 (bits, 8, 3, 0, -1))]
                    return
            except ValueError:
                pass

        if self.rawmode in ["RGB;T", "RYB;T"]:
            # Old LabEye/3PC files.  Would be very surprised if anyone
            # ever stumbled upon such a file ;-)
            size = self.size[0] * self.size[1]
            self.tile = [("raw", (0,0)+self.size, offs, ("G", 0, -1)),
                         ("raw", (0,0)+self.size, offs+size, ("R", 0, -1)),
                         ("raw", (0,0)+self.size, offs+2*size, ("B", 0, -1))]
        else:
            # LabEye/IFUNC files
            self.tile = [("raw", (0,0)+self.size, offs, (self.rawmode, 0, -1))]
Example #54
0
    def _open(self):

        if not _accept(self.fp.read(9)):
            raise SyntaxError("not an XPM file")

        # skip forward to next string
        while True:
            s = self.fp.readline()
            if not s:
                raise SyntaxError("broken XPM file")
            m = xpm_head.match(s)
            if m:
                break

        self.size = int(m.group(1)), int(m.group(2))

        pal = int(m.group(3))
        bpp = int(m.group(4))

        if pal > 256 or bpp != 1:
            raise ValueError("cannot read this XPM file")

        #
        # load palette description

        palette = [b"\0\0\0"] * 256

        for i in range(pal):

            s = self.fp.readline()
            if s[-2:] == b'\r\n':
                s = s[:-2]
            elif s[-1:] in b'\r\n':
                s = s[:-1]

            c = i8(s[1])
            s = s[2:-2].split()

            for i in range(0, len(s), 2):

                if s[i] == b"c":

                    # process colour key
                    rgb = s[i + 1]
                    if rgb == b"None":
                        self.info["transparency"] = c
                    elif rgb[0:1] == b"#":
                        # FIXME: handle colour names (see ImagePalette.py)
                        rgb = int(rgb[1:], 16)
                        palette[c] = (o8((rgb >> 16) & 255) +
                                      o8((rgb >> 8) & 255) + o8(rgb & 255))
                    else:
                        # unknown colour
                        raise ValueError("cannot read this XPM file")
                    break

            else:

                # missing colour key
                raise ValueError("cannot read this XPM file")

        self.mode = "P"
        self.palette = ImagePalette.raw("RGB", b"".join(palette))

        self.tile = [("raw", (0, 0) + self.size, self.fp.tell(), ("P", 0, 1))]
Example #55
0
    def _open(self):

        # The Sun Raster file header is 32 bytes in length and has the following format:

        #     typedef struct _SunRaster
        #     {
        #         DWORD MagicNumber;      /* Magic (identification) number */
        #         DWORD Width;            /* Width of image in pixels */
        #         DWORD Height;           /* Height of image in pixels */
        #         DWORD Depth;            /* Number of bits per pixel */
        #         DWORD Length;           /* Size of image data in bytes */
        #         DWORD Type;             /* Type of raster file */
        #         DWORD ColorMapType;     /* Type of color map */
        #         DWORD ColorMapLength;   /* Size of the color map in bytes */
        #     } SUNRASTER;


        # HEAD
        s = self.fp.read(32)
        if i32(s) != 0x59a66a95:
            raise SyntaxError("not an SUN raster file")

        offset = 32

        self.size = i32(s[4:8]), i32(s[8:12])

        depth = i32(s[12:16])
        data_length = i32(s[16:20])  # unreliable, ignore. 
        file_type = i32(s[20:24])
        palette_type = i32(s[24:28]) # 0: None, 1: RGB, 2: Raw/arbitrary
        palette_length = i32(s[28:32])
        
        if depth == 1:
            self.mode, rawmode = "1", "1;I"
        elif depth == 4:
            self.mode, rawmode = "L", "L;4"
        elif depth == 8:
            self.mode = rawmode = "L"
        elif depth == 24:
            if file_type == 3:
                self.mode, rawmode = "RGB", "RGB"
            else:
                self.mode, rawmode = "RGB", "BGR"
        elif depth == 32:
            if file_type == 3:
                self.mode, rawmode = 'RGB', 'RGBX'
            else:
                self.mode, rawmode = 'RGB', 'BGRX'
        else:
            raise SyntaxError("Unsupported Mode/Bit Depth")    
        
        if palette_length:
            if palette_length > 1024:
                raise SyntaxError("Unsupported Color Palette Length")

            if palette_type != 1:
                raise SyntaxError("Unsupported Palette Type")
            
            offset = offset + palette_length
            self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length))
            if self.mode == "L":
                self.mode = "P"
                rawmode = rawmode.replace('L', 'P')
            
        # 16 bit boundaries on stride
        stride = ((self.size[0] * depth + 15) // 16) * 2  

        # file type: Type is the version (or flavor) of the bitmap
        # file. The following values are typically found in the Type
        # field:
        # 0000h	Old
        # 0001h	Standard
        # 0002h	Byte-encoded
        # 0003h	RGB format
        # 0004h	TIFF format
        # 0005h	IFF format
        # FFFFh	Experimental

        # Old and standard are the same, except for the length tag.
        # byte-encoded is run-length-encoded
        # RGB looks similar to standard, but RGB byte order
        # TIFF and IFF mean that they were converted from T/IFF
        # Experimental means that it's something else.
        # (http://www.fileformat.info/format/sunraster/egff.htm)

        if file_type in (0, 1, 3, 4, 5):
            self.tile = [("raw", (0, 0)+self.size, offset, (rawmode, stride))]
        elif file_type == 2:
            self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)]
        else:
            raise SyntaxError('Unsupported Sun Raster file type')