# # The Python Imaging Library. # $Id$ # # GIF file handling # # History: # 95-09-01 fl Created # 96-12-14 fl Added interlace support # 96-12-30 fl Added animation support # 97-01-05 fl Added write support, fixed local colour map bug # 97-02-23 fl Make sure to load raster data in getdata() # 97-07-05 fl Support external decoder # 98-07-09 fl Handle all modes when saving # 98-07-15 fl Renamed offset attribute to avoid name clash # # Copyright (c) Secret Labs AB 1997-98. # Copyright (c) Fredrik Lundh 1995-97. # # See the README file for information on usage and redistribution. # __version__ = "0.5" import Image, ImageFile, ImagePalette # -------------------------------------------------------------------- # Helpers def i16(c): return ord(c[0]) + (ord(c[1])<<8) def o16(i): return chr(i&255) + chr(i>>8&255) # -------------------------------------------------------------------- # Identify/read GIF files def _accept(prefix): return prefix[:6] in ["GIF87a", "GIF89a"] class GifImageFile(ImageFile.ImageFile): format = "GIF" format_description = "Compuserve GIF" def data(self): s = self.fp.read(1) if s and ord(s): return self.fp.read(ord(s)) return None def _open(self): # Screen s = self.fp.read(13) if s[:6] not in ["GIF87a", "GIF89a"]: raise SyntaxError, "not a GIF file" self.info["version"] = s[:6] self.size = i16(s[6:]), i16(s[8:]) self.tile = [] flags = ord(s[10]) bits = (flags & 7) + 1 if flags & 128: # get global palette self.info["background"] = ord(s[11]) self.global_palette = self.palette =\ ImagePalette.raw("RGB", self.fp.read(3<%s" % (file, filename)) else: os.system("ppmquant 256 %s | ppmtogif >%s" % (file, filename)) try: os.unlink(file) except: pass # -------------------------------------------------------------------- # GIF utilities def getheader(im): """Return a list of strings representing a GIF header""" s = [ "GIF87a" + # magic o16(im.size[0]) + # size o16(im.size[1]) + chr(7 + 128) + # flags: bits + palette chr(0) + # background chr(0) # reserved/aspect ] # global palette if im.mode == "P": # colour palette s.append(im.im.getpalette("RGB")) else: # greyscale for i in range(256): s.append(chr(i) * 3) return s def getdata(im, offset = (0, 0), **params): """Return a list of strings representing this image. The first string is a local image header, the rest contains encoded image data.""" class collector: data = [] def write(self, data): self.data.append(data) im.load() # make sure raster data is available fp = collector() try: im.encoderinfo = params # local image header fp.write("," + o16(offset[0]) + # offset o16(offset[1]) + o16(im.size[0]) + # size o16(im.size[1]) + chr(0) + # flags chr(8)) # bits ImageFile._save(im, fp, [("gif", (0,0)+im.size, 0, RAWMODE[im.mode])]) fp.write("\0") # end of image data finally: del im.encoderinfo return fp.data # -------------------------------------------------------------------- # Registry Image.register_open(GifImageFile.format, GifImageFile, _accept) Image.register_save(GifImageFile.format, _save) Image.register_extension(GifImageFile.format, ".gif") Image.register_mime(GifImageFile.format, "image/gif") # # Uncomment the following line if you wish to use NETPBM/PBMPLUS # instead of the built-in "uncompressed" GIF encoder # Image.register_save(GifImageFile.format, _save_netpbm)