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 / GifImagePlugin.py < prev    next >
Text File  |  2001-05-03  |  9KB  |  356 lines

  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # GIF file handling
  6. #
  7. # History:
  8. # 95-09-01 fl   Created
  9. # 96-12-14 fl   Added interlace support
  10. # 96-12-30 fl   Added animation support
  11. # 97-01-05 fl   Added write support, fixed local colour map bug
  12. # 97-02-23 fl   Make sure to load raster data in getdata()
  13. # 97-07-05 fl   Support external decoder
  14. # 98-07-09 fl   Handle all modes when saving
  15. # 98-07-15 fl   Renamed offset attribute to avoid name clash
  16. #
  17. # Copyright (c) Secret Labs AB 1997-98.
  18. # Copyright (c) Fredrik Lundh 1995-97.
  19. #
  20. # See the README file for information on usage and redistribution.
  21. #
  22.  
  23.  
  24. __version__ = "0.5"
  25.  
  26.  
  27. import Image, ImageFile, ImagePalette
  28.  
  29.  
  30. # --------------------------------------------------------------------
  31. # Helpers
  32.  
  33. def i16(c):
  34.     return ord(c[0]) + (ord(c[1])<<8)
  35.  
  36. def o16(i):
  37.     return chr(i&255) + chr(i>>8&255)
  38.  
  39.  
  40. # --------------------------------------------------------------------
  41. # Identify/read GIF files
  42.  
  43. def _accept(prefix):
  44.     return prefix[:6] in ["GIF87a", "GIF89a"]
  45.  
  46. class GifImageFile(ImageFile.ImageFile):
  47.  
  48.     format = "GIF"
  49.     format_description = "Compuserve GIF"
  50.  
  51.     def data(self):
  52.         s = self.fp.read(1)
  53.         if s and ord(s):
  54.             return self.fp.read(ord(s))
  55.         return None
  56.  
  57.     def _open(self):
  58.  
  59.         # Screen
  60.         s = self.fp.read(13)
  61.         if s[:6] not in ["GIF87a", "GIF89a"]:
  62.             raise SyntaxError, "not a GIF file"
  63.  
  64.         self.info["version"] = s[:6]
  65.  
  66.         self.size = i16(s[6:]), i16(s[8:])
  67.  
  68.         self.tile = []
  69.  
  70.         flags = ord(s[10])
  71.  
  72.         bits = (flags & 7) + 1
  73.  
  74.         if flags & 128:
  75.             # get global palette
  76.             self.info["background"] = ord(s[11])
  77.             self.global_palette = self.palette =\
  78.                 ImagePalette.raw("RGB", self.fp.read(3<<bits))
  79.  
  80.         self.__fp = self.fp # FIXME: hack
  81.         self.__offset = 0
  82.         self.dispose = None
  83.  
  84.         self.frame = -1
  85.         self.seek(0) # get ready to read first frame
  86.  
  87.     def seek(self, frame):
  88.  
  89.         # FIXME: can only seek to next frame; should add rewind capability
  90.         if frame != self.frame + 1:
  91.             raise ValueError, "cannot seek to frame %d" % frame
  92.         self.frame = frame
  93.  
  94.         self.tile = []
  95.  
  96.         self.fp = self.__fp
  97.         if self.__offset:
  98.             # backup to last frame
  99.             self.fp.seek(self.__offset)
  100.             while self.data():
  101.                 pass
  102.             self.__offset = 0
  103.  
  104.         if self.dispose:
  105.             self.im = self.dispose
  106.             self.dispose = None
  107.  
  108.         self.palette = self.global_palette
  109.  
  110.         while 1:
  111.  
  112.             s = self.fp.read(1)
  113.             if not s or s == ";":
  114.                 break
  115.  
  116.             elif s == "!":
  117.                 #
  118.                 # extensions
  119.                 #
  120.                 s = self.fp.read(1)
  121.                 block = self.data()
  122.                 if ord(s) == 249:
  123.                     #
  124.                     # graphic control extension
  125.                     #
  126.                     flags = ord(block[0])
  127.                     if flags & 1:
  128.                         self.info["transparency"] = ord(block[3])
  129.                     self.info["duration"] = i16(block[1:3]) * 10
  130.                     try:
  131.                         # disposal methods
  132.                         if flags & 8:
  133.                             # replace with background colour
  134.                             self.dispose = Image.core.fill("P", self.size,
  135.                                 self.info["background"])
  136.                         elif flags & 16:
  137.                             # replace with previous contents
  138.                             self.dispose = self.im.copy()
  139.                     except (AttributeError, KeyError):
  140.                         pass
  141.                 elif ord(s) == 255:
  142.                     #
  143.                     # application extension
  144.                     #
  145.                     self.info["extension"] = block, self.fp.tell()
  146.                     if block[:11] == "NETSCAPE2.0":
  147.                         self.info["loop"] = 1 # FIXME
  148.                 while self.data():
  149.                     pass
  150.  
  151.             elif s == ",":
  152.                 #
  153.                 # local image
  154.                 #
  155.                 s = self.fp.read(9)
  156.  
  157.                 # extent
  158.                 x0, y0 = i16(s[0:]), i16(s[2:])
  159.                 x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:])
  160.                 flags = ord(s[8])
  161.  
  162.                 interlace = (flags & 64) != 0
  163.  
  164.                 if flags & 128:
  165.                     bits = (flags & 7) + 1
  166.                     self.palette =\
  167.                         ImagePalette.raw("RGB", self.fp.read(3<<bits))
  168.  
  169.                 # image data
  170.                 bits = ord(self.fp.read(1))
  171.                 self.__offset = self.fp.tell()
  172.                 self.tile = [("gif",
  173.                              (x0, y0, x1, y1),
  174.                              self.__offset,
  175.                              (bits, interlace))]
  176.                 break
  177.  
  178.             else:
  179.                 pass
  180.                 # raise IOError, "illegal GIF tag `%x`" % ord(s)
  181.  
  182.         if not self.tile:
  183.             self.__fp = None
  184.             raise EOFError, "no more images in GIF file"
  185.  
  186.         self.mode = "L"
  187.         if self.palette:
  188.             self.mode = "P"
  189.  
  190.     def tell(self):
  191.         return self.frame
  192.  
  193.  
  194. # --------------------------------------------------------------------
  195. # Write GIF files
  196.  
  197. try:
  198.     import _imaging_gif
  199. except ImportError:
  200.     _imaging_gif = None
  201.  
  202. RAWMODE = {
  203.     "1": "L",
  204.     "L": "L",
  205.     "P": "P",
  206. }
  207.  
  208. def _save(im, fp, filename):
  209.  
  210.     if _imaging_gif:
  211.         # call external driver
  212.         try:
  213.             _imaging_gif.save(im, fp, filename)
  214.             return
  215.         except IOError:
  216.             pass # write uncompressed file
  217.  
  218.     try:
  219.         rawmode = RAWMODE[im.mode]
  220.         imOut = im
  221.     except KeyError:
  222.         # convert on the fly (EXPERIMENTAL -- I'm not sure PIL
  223.         # should automatically convert images on save...)
  224.         if Image.getmodebase(im.mode) == "RGB":
  225.             imOut = im.convert("P")
  226.             rawmode = "P"
  227.         else:
  228.             imOut = im.convert("L")
  229.             rawmode = "L"
  230.  
  231.     # header
  232.     for s in getheader(imOut):
  233.         fp.write(s)
  234.  
  235.     flags = 0
  236.  
  237.     try:
  238.         interlace = im.encoderinfo["interlace"]
  239.     except:
  240.         # default is on (since we're writing uncompressed images)
  241.         interlace = 1
  242.         flags = flags | 64
  243.  
  244.     # local image header
  245.     fp.write("," +
  246.              o16(0) + o16(0) +          # bounding box
  247.              o16(im.size[0]) +          # size
  248.              o16(im.size[1]) +
  249.              chr(flags) +               # flags
  250.              chr(8))                    # bits
  251.  
  252.     imOut.encoderconfig = (8, interlace)
  253.  
  254.     ImageFile._save(imOut, fp, [("gif", (0,0)+im.size, 0, rawmode)])
  255.  
  256.     fp.write("\0") # end of image data
  257.  
  258.     fp.write(";") # end of file
  259.  
  260.     try:
  261.         fp.flush()
  262.     except: pass
  263.  
  264. def _save_netpbm(im, fp, filename):
  265.  
  266.     #
  267.     # If you need real GIF compression and/or RGB quantization, you
  268.     # can use the external NETPBM/PBMPLUS utilities.  See comments
  269.     # below for information on how to enable this.
  270.  
  271.     import os
  272.     file = im._dump()
  273.     if im.mode != "RGB":
  274.         os.system("ppmtogif %s >%s" % (file, filename))
  275.     else:
  276.         os.system("ppmquant 256 %s | ppmtogif >%s" % (file, filename))
  277.     try: os.unlink(file)
  278.     except: pass
  279.  
  280.  
  281. # --------------------------------------------------------------------
  282. # GIF utilities
  283.  
  284. def getheader(im):
  285.     """Return a list of strings representing a GIF header"""
  286.  
  287.     s = [
  288.         "GIF87a" +              # magic
  289.         o16(im.size[0]) +       # size
  290.         o16(im.size[1]) +
  291.         chr(7 + 128) +          # flags: bits + palette
  292.         chr(0) +                # background
  293.         chr(0)                  # reserved/aspect
  294.     ]
  295.  
  296.     # global palette
  297.     if im.mode == "P":
  298.         # colour palette
  299.         s.append(im.im.getpalette("RGB"))
  300.     else:
  301.         # greyscale
  302.         for i in range(256):
  303.             s.append(chr(i) * 3)
  304.  
  305.     return s
  306.  
  307. def getdata(im, offset = (0, 0), **params):
  308.     """Return a list of strings representing this image.
  309.        The first string is a local image header, the rest contains
  310.        encoded image data."""
  311.  
  312.     class collector:
  313.         data = []
  314.         def write(self, data):
  315.             self.data.append(data)
  316.  
  317.     im.load() # make sure raster data is available
  318.  
  319.     fp = collector()
  320.  
  321.     try:
  322.         im.encoderinfo = params
  323.  
  324.         # local image header
  325.         fp.write("," +
  326.                  o16(offset[0]) +       # offset
  327.                  o16(offset[1]) +
  328.                  o16(im.size[0]) +      # size
  329.                  o16(im.size[1]) +
  330.                  chr(0) +               # flags
  331.                  chr(8))                # bits
  332.  
  333.         ImageFile._save(im, fp, [("gif", (0,0)+im.size, 0, RAWMODE[im.mode])])
  334.  
  335.         fp.write("\0") # end of image data
  336.  
  337.     finally:
  338.         del im.encoderinfo
  339.  
  340.     return fp.data
  341.  
  342.  
  343. # --------------------------------------------------------------------
  344. # Registry
  345.  
  346. Image.register_open(GifImageFile.format, GifImageFile, _accept)
  347. Image.register_save(GifImageFile.format, _save)
  348. Image.register_extension(GifImageFile.format, ".gif")
  349. Image.register_mime(GifImageFile.format, "image/gif")
  350.  
  351. #
  352. # Uncomment the following line if you wish to use NETPBM/PBMPLUS
  353. # instead of the built-in "uncompressed" GIF encoder
  354.  
  355. # Image.register_save(GifImageFile.format, _save_netpbm)
  356.