mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 09:57:43 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			250 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#
 | 
						|
# The Python Imaging Library.
 | 
						|
# $Id$
 | 
						|
#
 | 
						|
# BMP file handler
 | 
						|
#
 | 
						|
# Windows (and OS/2) native bitmap storage format.
 | 
						|
#
 | 
						|
# history:
 | 
						|
# 1995-09-01 fl   Created
 | 
						|
# 1996-04-30 fl   Added save
 | 
						|
# 1997-08-27 fl   Fixed save of 1-bit images
 | 
						|
# 1998-03-06 fl   Load P images as L where possible
 | 
						|
# 1998-07-03 fl   Load P images as 1 where possible
 | 
						|
# 1998-12-29 fl   Handle small palettes
 | 
						|
# 2002-12-30 fl   Fixed load of 1-bit palette images
 | 
						|
# 2003-04-21 fl   Fixed load of 1-bit monochrome images
 | 
						|
# 2003-04-23 fl   Added limited support for BI_BITFIELDS compression
 | 
						|
#
 | 
						|
# Copyright (c) 1997-2003 by Secret Labs AB
 | 
						|
# Copyright (c) 1995-2003 by Fredrik Lundh
 | 
						|
#
 | 
						|
# See the README file for information on usage and redistribution.
 | 
						|
#
 | 
						|
 | 
						|
 | 
						|
__version__ = "0.7"
 | 
						|
 | 
						|
 | 
						|
from PIL import Image, ImageFile, ImagePalette, _binary
 | 
						|
 | 
						|
i8 = _binary.i8
 | 
						|
i16 = _binary.i16le
 | 
						|
i32 = _binary.i32le
 | 
						|
o8 = _binary.o8
 | 
						|
o16 = _binary.o16le
 | 
						|
o32 = _binary.o32le
 | 
						|
 | 
						|
#
 | 
						|
# --------------------------------------------------------------------
 | 
						|
# Read BMP file
 | 
						|
 | 
						|
BIT2MODE = {
 | 
						|
    # bits => mode, rawmode
 | 
						|
    1: ("P", "P;1"),
 | 
						|
    4: ("P", "P;4"),
 | 
						|
    8: ("P", "P"),
 | 
						|
    16: ("RGB", "BGR;15"),
 | 
						|
    24: ("RGB", "BGR"),
 | 
						|
    32: ("RGB", "BGRX")
 | 
						|
}
 | 
						|
 | 
						|
def _accept(prefix):
 | 
						|
    return prefix[:2] == b"BM"
 | 
						|
 | 
						|
##
 | 
						|
# Image plugin for the Windows BMP format.
 | 
						|
 | 
						|
class BmpImageFile(ImageFile.ImageFile):
 | 
						|
 | 
						|
    format = "BMP"
 | 
						|
    format_description = "Windows Bitmap"
 | 
						|
 | 
						|
    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:])
 | 
						|
            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 (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
 | 
						|
 | 
						|
    def _open(self):
 | 
						|
 | 
						|
        # HEAD
 | 
						|
        s = self.fp.read(14)
 | 
						|
        if s[:2] != b"BM":
 | 
						|
            raise SyntaxError("Not a BMP file")
 | 
						|
        offset = i32(s[10:])
 | 
						|
 | 
						|
        self._bitmap(offset=offset)
 | 
						|
 | 
						|
 | 
						|
class DibImageFile(BmpImageFile):
 | 
						|
 | 
						|
    format = "DIB"
 | 
						|
    format_description = "Windows Bitmap"
 | 
						|
 | 
						|
    def _open(self):
 | 
						|
        self._bitmap()
 | 
						|
 | 
						|
#
 | 
						|
# --------------------------------------------------------------------
 | 
						|
# Write BMP file
 | 
						|
 | 
						|
SAVE = {
 | 
						|
    "1": ("1", 1, 2),
 | 
						|
    "L": ("L", 8, 256),
 | 
						|
    "P": ("P", 8, 256),
 | 
						|
    "RGB": ("BGR", 24, 0),
 | 
						|
}
 | 
						|
 | 
						|
def _save(im, fp, filename, check=0):
 | 
						|
 | 
						|
    try:
 | 
						|
        rawmode, bits, colors = SAVE[im.mode]
 | 
						|
    except KeyError:
 | 
						|
        raise IOError("cannot write mode %s as BMP" % im.mode)
 | 
						|
 | 
						|
    if check:
 | 
						|
        return check
 | 
						|
 | 
						|
    stride = ((im.size[0]*bits+7)//8+3)&(~3)
 | 
						|
    header = 40 # or 64 for OS/2 version 2
 | 
						|
    offset = 14 + header + colors * 4
 | 
						|
    image  = stride * im.size[1]
 | 
						|
 | 
						|
    # bitmap header
 | 
						|
    fp.write(b"BM" +                    # file type (magic)
 | 
						|
             o32(offset+image) +        # file size
 | 
						|
             o32(0) +                   # reserved
 | 
						|
             o32(offset))               # image data offset
 | 
						|
 | 
						|
    # bitmap info header
 | 
						|
    fp.write(o32(header) +              # info header size
 | 
						|
             o32(im.size[0]) +          # width
 | 
						|
             o32(im.size[1]) +          # height
 | 
						|
             o16(1) +                   # planes
 | 
						|
             o16(bits) +                # depth
 | 
						|
             o32(0) +                   # compression (0=uncompressed)
 | 
						|
             o32(image) +               # size of bitmap
 | 
						|
             o32(1) + o32(1) +          # resolution
 | 
						|
             o32(colors) +              # colors used
 | 
						|
             o32(colors))               # colors important
 | 
						|
 | 
						|
    fp.write(b"\0" * (header - 40))    # padding (for OS/2 format)
 | 
						|
 | 
						|
    if im.mode == "1":
 | 
						|
        for i in (0, 255):
 | 
						|
            fp.write(o8(i) * 4)
 | 
						|
    elif im.mode == "L":
 | 
						|
        for i in range(256):
 | 
						|
            fp.write(o8(i) * 4)
 | 
						|
    elif im.mode == "P":
 | 
						|
        fp.write(im.im.getpalette("RGB", "BGRX"))
 | 
						|
 | 
						|
    ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, stride, -1))])
 | 
						|
 | 
						|
#
 | 
						|
# --------------------------------------------------------------------
 | 
						|
# Registry
 | 
						|
 | 
						|
Image.register_open(BmpImageFile.format, BmpImageFile, _accept)
 | 
						|
Image.register_save(BmpImageFile.format, _save)
 | 
						|
 | 
						|
Image.register_extension(BmpImageFile.format, ".bmp")
 |