home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
pypil112.zip
/
PIL-1.1.2.zip
/
Lib
/
site-packages
/
PIL
/
ImageFile.py
< prev
next >
Wrap
Text File
|
2001-05-03
|
11KB
|
379 lines
#
# The Python Imaging Library.
# $Id$
#
# base class for image file handlers
#
# history:
# 1995-09-09 fl Created
# 1996-03-11 fl Fixed load mechanism.
# 1996-04-15 fl Added pcx/xbm decoders.
# 1996-04-30 fl Added encoders.
# 1996-12-14 fl Added load helpers
# 1997-01-11 fl Use encode_to_file where possible
# 1997-08-27 fl Flush output in _save
# 1998-03-05 fl Use memory mapping for some modes
# 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B"
# 1999-05-31 fl Added image parser
# 2000-10-12 fl Set readonly flag on memory-mapped images
#
# Copyright (c) 1997-2000 by Secret Labs AB
# Copyright (c) 1995-2000 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
import Image
import traceback, sys
MAXBLOCK = 65536
# raw modes that may be memory mapped. NOTE: if you change this, you
# may have to modify the stride calculation in map.c too!
MAPMODES = ("L", "P", "RGBX", "RGBA", "CMYK", "I;16", "I;16B")
#
# --------------------------------------------------------------------
# Helpers
def _tilesort(t1, t2):
# sort on offset
return cmp(t1[2], t2[2])
#
# --------------------------------------------------------------------
# ImageFile base class
class ImageFile(Image.Image):
"Base class for image file format handlers."
def __init__(self, fp=None, filename=None):
Image.Image.__init__(self)
self.tile = None
self.readonly = 1 # until we know better
self.decoderconfig = ()
self.decodermaxblock = MAXBLOCK
if type(fp) == type(""):
# filename
self.fp = open(fp, "rb")
self.filename = fp
else:
# stream
self.fp = fp
self.filename = filename
try:
self._open()
except IndexError, v: # end of data
if Image.DEBUG > 1:
traceback.print_exc()
raise SyntaxError, v
except TypeError, v: # end of data (ord)
if Image.DEBUG > 1:
traceback.print_exc()
raise SyntaxError, v
except KeyError, v: # unsupported mode
if Image.DEBUG > 1:
traceback.print_exc()
raise SyntaxError, v
if not self.mode or self.size[0] <= 0:
raise SyntaxError, "not identified by this driver"
def draft(self, mode, size):
"Set draft mode"
pass
def verify(self):
"Check file integrity"
# raise exception if something's wrong. must be called
# directly after open, and closes file when finished.
self.fp = None
def load(self):
"Load image data based on tile list"
Image.Image.load(self)
if self.tile is None:
raise IOError, "cannot load this image"
if not self.tile:
return
self.map = None
readonly = 0
if self.filename and len(self.tile) == 1:
# try memory mapping
d, e, o, a = self.tile[0]
if d == "raw" and a[0] == self.mode and a[0] in MAPMODES:
try:
self.map = Image.core.map(self.filename)
self.map.seek(o)
self.im = self.map.readimage(
self.mode, self.size, a[1], a[2]
)
readonly = 1
except (AttributeError, IOError):
self.map = None
self.load_prepare()
if not self.map:
# sort tiles in file order
self.tile.sort(_tilesort)
try:
# FIXME: This is a hack to handle TIFF's JpegTables tag.
prefix = self.tile_prefix
except AttributeError:
prefix = ""
for d, e, o, a in self.tile:
d = Image._getdecoder(self.mode, d, a, self.decoderconfig)
self.load_seek(o)
try:
d.setimage(self.im, e)
except ValueError:
continue
b = prefix
t = len(b)
while 1:
s = self.load_read(self.decodermaxblock)
if not s:
self.tile = []
raise IOError, "image file is truncated, %d bytes left in buffer" % len(b)
b = b + s
n, e = d.decode(b)
if n < 0:
break
b = b[n:]
t = t + n
self.tile = []
self.readonly = readonly
self.fp = None # might be shared
if not self.map and e < 0:
raise IOError, "decoder error %d when reading image file" % e
# post processing
if hasattr(self, "tile_post_rotate"):
# FIXME: This is a hack to handle rotated PCD's
self.im = self.im.rotate(self.tile_post_rotate)
self.size = self.im.size
self.load_end()
def load_prepare(self):
# create image memory if necessary
if not self.im or\
self.im.mode != self.mode or self.im.size != self.size:
self.im = Image.core.new(self.mode, self.size)
# create palette (optional)
if self.mode == "P":
Image.Image.load(self)
def load_end(self):
# may be overridden
pass
def load_seek(self, pos):
# may be overridden for contained formats
self.fp.seek(pos)
def load_read(self, bytes):
# may be overridden for blocked formats (e.g. PNG)
return self.fp.read(bytes)
class _ParserFile:
# parser support class.
def __init__(self, data):
self.data = data
self.offset = 0
def close(self):
self.data = self.offset = None
def tell(self):
return self.offset
def seek(self, offset, whence=0):
if whence == 0:
self.offset = offset
elif whence == 1:
self.offset = self.offset + offset
else:
# force error in Image.open
raise IOError, "illegal argument to seek"
def read(self, bytes=0):
pos = self.offset
if bytes:
data = self.data[pos:pos+bytes]
else:
data = self.data[pos:]
self.offset = pos + len(data)
return data
def readline(self):
# FIXME: this is slow!
s = ""
while 1:
c = self.read(1)
if not c:
break
s = s + c
if c == "\n":
break
return s
class Parser:
# incremental image parser. implements the consumer
# interface.
image = None
data = None
decoder = None
finished = 0
def reset(self):
assert self.data is None, "cannot reuse parsers"
def feed(self, data):
# collect data
if self.finished:
return
if self.data is None:
self.data = data
else:
self.data = self.data + data
# parse what we have
if self.decoder:
if self.offset > 0:
# skip header
skip = min(len(self.data), self.offset)
self.data = self.data[skip:]
self.offset = self.offset - skip
if self.offset > 0 or not self.data:
return
n, e = self.decoder.decode(self.data)
if n < 0:
# end of stream
self.data = None
self.finished = 1
if e < 0:
# decoding error
self.image = None
raise IOError,\
"decoder error %d when reading image file" % e
else:
# end of image
return
self.data = self.data[n:]
else:
# attempt to open this file
try:
try:
fp = _ParserFile(self.data)
im = Image.open(fp)
finally:
fp.close() # explicitly close the virtual file
except IOError:
pass # not enough data
else:
# sanity check
if len(im.tile) != 1:
raise IOError, "cannot parse this image"
# initialize decoder
im.load_prepare()
d, e, o, a = im.tile[0]
im.tile = []
self.decoder = Image._getdecoder(
im.mode, d, a, im.decoderconfig
)
self.decoder.setimage(im.im, e)
# calculate decoder offset
self.offset = o
if self.offset <= len(self.data):
self.data = self.data[self.offset:]
self.offset = 0
self.image = im
def close(self):
# finish decoding
if self.decoder:
# get rid of what's left in the buffers
self.feed("")
self.data = self.decoder = None
if not self.finished:
raise IOError, "image was incomplete"
if not self.image:
raise IOError, "cannot parse this image"
return self.image
#
# --------------------------------------------------------------------
# Save image body
def _save(im, fp, tile):
"Helper to save image based on tile list"
im.load()
if not hasattr(im, "encoderconfig"):
im.encoderconfig = ()
tile.sort(_tilesort)
bufsize = max(MAXBLOCK, im.size[0] * 4) # see RawEncode.c
try:
fh = fp.fileno()
fp.flush()
except AttributeError:
# compress to Python file-compatible object
for e, b, o, a in tile:
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
if o > 0:
fp.seek(o, 0)
e.setimage(im.im, b)
while 1:
l, s, d = e.encode(bufsize)
fp.write(d)
if s:
break
if s < 0:
raise IOError, "encoder error %d when writing image file" % s
else:
# slight speedup: compress to real file object
for e, b, o, a in tile:
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
if o > 0:
fp.seek(o, 0)
e.setimage(im.im, b)
s = e.encode_to_file(fh, bufsize)
if s < 0:
raise IOError, "encoder error %d when writing image file" % s
try:
fp.flush()
except: pass