# # The Python Imaging Library. # $Id: ImImagePlugin.py 2285 2005-02-07 23:52:14Z fredrik $ # # IFUNC IM file handling for PIL # # history: # 1995-09-01 fl Created. # 1997-01-03 fl Save palette images # 1997-01-08 fl Added sequence support # 1997-01-23 fl Added P and RGB save support # 1997-05-31 fl Read floating point images # 1997-06-22 fl Save floating point images # 1997-08-27 fl Read and save 1-bit images # 1998-06-25 fl Added support for RGB+LUT images # 1998-07-02 fl Added support for YCC images # 1998-07-15 fl Renamed offset attribute to avoid name clash # 1998-12-29 fl Added I;16 support # 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) # 2003-09-26 fl Added LA/PA support # # Copyright (c) 1997-2003 by Secret Labs AB. # Copyright (c) 1995-2001 by Fredrik Lundh. # # See the README file for information on usage and redistribution. # __version__ = "0.7" import re, string import Image, ImageFile, ImagePalette # -------------------------------------------------------------------- # Standard tags COMMENT = "Comment" DATE = "Date" EQUIPMENT = "Digitalization equipment" FRAMES = "File size (no of images)" LUT = "Lut" NAME = "Name" SCALE = "Scale (x,y)" SIZE = "Image size (x*y)" MODE = "Image type" TAGS = { COMMENT:0, DATE:0, EQUIPMENT:0, FRAMES:0, LUT:0, NAME:0, SCALE:0, SIZE:0, MODE:0 } OPEN = { # ifunc93/p3cfunc formats "0 1 image": ("1", "1"), "L 1 image": ("1", "1"), "Greyscale image": ("L", "L"), "Grayscale image": ("L", "L"), "RGB image": ("RGB", "RGB;L"), "RLB image": ("RGB", "RLB"), "RYB image": ("RGB", "RLB"), "B1 image": ("1", "1"), "B2 image": ("P", "P;2"), "B4 image": ("P", "P;4"), "X 24 image": ("RGB", "RGB"), "L 32 S image": ("I", "I;32"), "L 32 F image": ("F", "F;32"), # old p3cfunc formats "RGB3 image": ("RGB", "RGB;T"), "RYB3 image": ("RGB", "RYB;T"), # extensions "LA image": ("LA", "LA;L"), "RGBA image": ("RGBA", "RGBA;L"), "RGBX image": ("RGBX", "RGBX;L"), "CMYK image": ("CMYK", "CMYK;L"), "YCC image": ("YCbCr", "YCbCr;L"), } # ifunc95 extensions for i in ["8", "8S", "16", "16S", "32", "32F"]: OPEN["L %s image" % i] = ("F", "F;%s" % i) OPEN["L*%s image" % i] = ("F", "F;%s" % i) for i in ["16", "16B"]: OPEN["L %s image" % i] = ("I;%s" % i, "I;%s" % i) OPEN["L*%s image" % i] = ("I;%s" % i, "I;%s" % i) for i in ["32S"]: OPEN["L %s image" % i] = ("I", "I;%s" % i) OPEN["L*%s image" % i] = ("I", "I;%s" % i) for i in range(2, 33): OPEN["L*%s image" % i] = ("F", "F;%s" % i) # -------------------------------------------------------------------- # Read IM directory split = re.compile(r"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") def number(s): try: return int(s) except ValueError: return float(s) ## # Image plugin for the IFUNC IM file format. class ImImageFile(ImageFile.ImageFile): format = "IM" format_description = "IFUNC Image Memory" 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 "\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 1: s = self.fp.read(1) # Some versions of IFUNC uses \n\r instead of \r\n... if s == "\r": continue if not s or s[0] == chr(0) or s[0] == chr(26): 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:] == '\r\n': s = s[:-2] elif s[-1:] == '\n': s = s[:-1] try: m = split.match(s) except re.error, v: raise SyntaxError, "not an IM file" if m: k, v = m.group(1,2) # Convert value as appropriate if k in [FRAMES, SCALE, SIZE]: v = string.replace(v, "*", ",") v = tuple(map(number, string.split(v, ","))) if len(v) == 1: v = v[0] elif k == MODE and OPEN.has_key(v): v, self.rawmode = OPEN[v] # Add to dictionary. Note that COMMENT tags are # combined into a list of strings. if k == COMMENT: if self.info.has_key(k): self.info[k].append(v) else: self.info[k] = [v] else: self.info[k] = v if TAGS.has_key(k): n = n + 1 else: raise SyntaxError, "Syntax error in IM header: " + s 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] != chr(26): s = self.fp.read(1) if not s: raise SyntaxError, "File truncated" if self.info.has_key(LUT): # 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 palette[i] != chr(i): linear = 0 else: greyscale = 0 if self.mode == "L" or self.mode == "LA": if greyscale: if not linear: self.lut = map(ord, 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 = map(ord, 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))] def seek(self, frame): if frame < 0 or frame >= self.info[FRAMES]: raise EOFError, "seek outside sequence" if self.frame == frame: return self.frame = frame if self.mode == "1": bits = 1 else: bits = 8 * len(self.mode) size = ((self.size[0] * bits + 7) / 8) * self.size[1] offs = self.__offset + frame * size self.fp = self.__fp self.tile = [("raw", (0,0)+self.size, offs, (self.rawmode, 0, -1))] def tell(self): return self.frame # # -------------------------------------------------------------------- # Save IM files SAVE = { # mode: (im type, raw mode) "1": ("0 1", "1"), "L": ("Greyscale", "L"), "LA": ("LA", "LA;L"), "P": ("Greyscale", "P"), "PA": ("LA", "PA;L"), "I": ("L 32S", "I;32S"), "I;16": ("L 16", "I;16"), "I;16B": ("L 16B", "I;16B"), "F": ("L 32F", "F;32F"), "RGB": ("RGB", "RGB;L"), "RGBA": ("RGBA", "RGBA;L"), "RGBX": ("RGBX", "RGBX;L"), "CMYK": ("CMYK", "CMYK;L"), "YCbCr": ("YCC", "YCbCr;L") } def _save(im, fp, filename, check=0): try: type, rawmode = SAVE[im.mode] except KeyError: raise ValueError, "Cannot save %s images as IM" % im.mode try: frames = im.encoderinfo["frames"] except KeyError: frames = 1 if check: return check fp.write("Image type: %s image\r\n" % type) if filename: fp.write("Name: %s\r\n" % filename) fp.write("Image size (x*y): %d*%d\r\n" % im.size) fp.write("File size (no of images): %d\r\n" % frames) if im.mode == "P": fp.write("Lut: 1\r\n") fp.write("\000" * (511-fp.tell()) + "\032") if im.mode == "P": fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, -1))]) # # -------------------------------------------------------------------- # Registry Image.register_open("IM", ImImageFile) Image.register_save("IM", _save) Image.register_extension("IM", ".im")