home *** CD-ROM | disk | FTP | other *** search
- "Template for image file reader/writers in Python"
- #
- # Windows BMP image reader/writer module, python version
- #
- import struct
- import imgformat
- import imgcolormap
- import os
-
- # test little/bigendian
- _little_endian = (struct.unpack('h', '\001\000')[0] & 0xff)
-
- _FILEHEADERSIZE=14
- _IMAGEHEADERSIZE=40
-
- error = imgformat.error
-
- class reader:
- "Object that reads the image."
-
- def __init__(self, file):
- """Initialize. You should read the header and fill in attributes
- such as width, height, format_choices, format and colormap"""
-
- if type(file) == type(''):
- self._filename = filename
- self._fp = open(filename, 'rb')
- else:
- self._filename = "<open file>"
- self._fp = file
- self._readfileheader()
- self._readimageheader()
- if self.bpp == 8:
- self._readcolormap()
- self.format = imgformat.colormap_b2t
- elif self.bpp == 24:
- if _little_endian:
- self.format = imgformat.bmprgble_b2t
- else:
- self.format = imgformat.bmprgbbe_b2t
- self.format_choices = (self.format, )
- self._fp.seek(self._dataoffset)
- if self._datasize == 0:
- rowlen = self.width*self.bpp/8
- rowlen = (rowlen+3) & ~3
- self._datasize = rowlen*self.height
- ## print 'DBG: computed datasize', self._datasize
-
- def _readfileheader(self):
- d = self._fp.read(2)
- if d <> 'BM':
- raise error, 'Not a BMP file'
- d = self._fp.read(8)
- self._dataoffset = self._readlong()
-
- def _readimageheader(self):
- wantedhsize = 24
- hsize = self._readlong()
- if hsize < wantedhsize:
- raise error, ('Unexpected header size:', hsize)
- self.width = self._readlong()
- self.height = self._readlong()
- self._fp.read(2) # Skip Numberofimageplanes
- self.bpp = self._readshort()
- if self.bpp not in (8, 24):
- raise error, ('Unsupported number of bits per pixel:', self.bpp)
- cmethod = self._readlong()
- if cmethod <> 0:
- raise error, ('Unsupported compression method:', cmethod)
- self._datasize = self._readlong()
- self._fp.read(hsize-wantedhsize)
-
- def _readcolormap(self):
- ncolors = 1 << self.bpp
- r = g = b = ''
- for i in range(ncolors):
- b = b + self._fp.read(1)
- g = g + self._fp.read(1)
- r = r + self._fp.read(1)
- self._fp.read(1)
- self.colormap = imgcolormap.new3(r, g, b)
-
- def read(self):
- """Read the image data"""
- rv = self._fp.read(self._datasize)
- if len(rv) <> self._datasize:
- raise error, 'Incorrect size read: expected %d got %d'%(self._datasize, len(rv))
- return rv
-
- def _readlong(self):
- """Read a little-endian long, assumed to be positive"""
- d = ord(self._fp.read(1)) & 0xff
- d = d | ((ord(self._fp.read(1)) & 0xff) << 8)
- d = d | ((ord(self._fp.read(1)) & 0xff) << 16)
- d = d | ((ord(self._fp.read(1)) & 0xff) << 24)
- return d
-
- def _readshort(self):
- """Read a little-endian short, assumed to be positive"""
- d = ord(self._fp.read(1)) & 0xff
- d = d | ((ord(self._fp.read(1)) & 0xff) << 8)
- return d
-
- def args(self):
- rv = {}
- for k in self.__dict__.keys():
- if k[0] <> '_':
- rv[k] = self.__dict__[k]
- return rv
-
-
- def write(self, data):
- raise error, 'Cannot write() to reader'
-
- class writer:
- "Object that writes to an image file"
-
- def __init__(self, file):
- if type(file) == type(''):
- self._filename = filename
- self._fp = None
- else:
- self._filename = "<open file>"
- self._fp = file
- if _little_endian:
- rgbformat = imgformat.bmprgble_b2t
- else:
- rgbformat = imgformat.bmprgbbe_b2t
- self.format = imgformat.colormap_b2t
- self.format_choices = (self.format, rgbformat)
-
- def args(self):
- return self.__dict__
-
- def _get(self, attr):
- try:
- return getattr(self, attr)
- except AttributeError:
- raise error, "Required attribute '%s' missing"%attr
-
- def read(self):
- raise error, 'Cannot read() from writer'
-
- def write(self, data):
- """Write the image file, according to attribute format"""
-
- w = self._get('width')
- h = self._get('height')
- if self.format == imgformat.colormap_b2t:
- colormap = self._get('colormap')
- self._bpp = 8
- else:
- colormap = None
- self._bpp = 24
-
- if not self._fp:
- self._fp = open(self._filename, 'wb')
- if os.name == 'mac':
- import macfs
- fss = macfs.FSSpec(self._filename)
- fss.SetCreatorType('????', 'BMP ')
-
- if colormap:
- self._colormapsize = len(colormap)
- else:
- self._colormapsize = 0
- self._imgoffset = _FILEHEADERSIZE + _IMAGEHEADERSIZE + \
- self._colormapsize*4
- self._datasize = len(data)
- self._writefileheader()
- self._writeimageheader()
- if colormap:
- self._writecolormap(colormap)
- self._fp.write(data)
- self._fp.close()
-
- def _writefileheader(self):
- self._fp.write('BM')
- self._writelong(self._imgoffset + self._datasize)
- self._writeshort(0)
- self._writeshort(0)
- self._writelong(self._imgoffset)
-
- def _writeimageheader(self):
- self._writelong(40)
- self._writelong(self.width)
- self._writelong(self.height)
- self._writeshort(1) # Number of image planes
- self._writeshort(self._bpp)
- self._writelong(0) # compression method
- self._writelong(self._datasize)
- self._writelong(0) # Horizontal resolution
- self._writelong(0) # Vertical resolution
- self._writelong(self._colormapsize)
- self._writelong(0) # Number of "significant" colors
-
- def _writecolormap(self, colormap):
- for r, g, b in colormap:
- ## print 'DBG map', r, g, b
- self._fp.write(chr(b)+chr(g)+chr(r)+chr(0))
-
- def _writeshort(self, d):
- self._fp.write(chr(d & 0xff))
- self._fp.write(chr((d>>8)&0xff))
-
- def _writelong(self, d):
- self._fp.write(chr(d & 0xff))
- self._fp.write(chr((d>>8)&0xff))
- self._fp.write(chr((d>>16)&0xff))
- self._fp.write(chr((d>>24)&0xff))
-