2
# The Python Imaging Library.
8
# 1995-09-01 fl Created
9
# 1996-12-14 fl Added interlace support
10
# 1996-12-30 fl Added animation support
11
# 1997-01-05 fl Added write support, fixed local colour map bug
12
# 1997-02-23 fl Make sure to load raster data in getdata()
13
# 1997-07-05 fl Support external decoder (0.4)
14
# 1998-07-09 fl Handle all modes when saving (0.5)
15
# 1998-07-15 fl Renamed offset attribute to avoid name clash
16
# 2001-04-16 fl Added rewind support (seek to frame 0) (0.6)
17
# 2001-04-17 fl Added palette optimization (0.7)
18
# 2002-06-06 fl Added transparency support for save (0.8)
19
# 2004-02-24 fl Disable interlacing for small images
21
# Copyright (c) 1997-2004 by Secret Labs AB
22
# Copyright (c) 1995-2004 by Fredrik Lundh
24
# See the README file for information on usage and redistribution.
31
import Image, ImageFile, ImagePalette
34
# --------------------------------------------------------------------
38
return ord(c[0]) + (ord(c[1])<<8)
41
return chr(i&255) + chr(i>>8&255)
44
# --------------------------------------------------------------------
45
# Identify/read GIF files
48
return prefix[:6] in ["GIF87a", "GIF89a"]
51
# Image plugin for GIF images. This plugin supports both GIF87 and
54
class GifImageFile(ImageFile.ImageFile):
57
format_description = "Compuserve GIF"
64
return self.fp.read(ord(s))
71
if s[:6] not in ["GIF87a", "GIF89a"]:
72
raise SyntaxError, "not a GIF file"
74
self.info["version"] = s[:6]
76
self.size = i16(s[6:]), i16(s[8:])
82
bits = (flags & 7) + 1
86
self.info["background"] = ord(s[11])
87
# check if palette contains colour indices
88
p = self.fp.read(3<<bits)
89
for i in range(0, len(p), 3):
90
if not (chr(i/3) == p[i] == p[i+1] == p[i+2]):
91
p = ImagePalette.raw("RGB", p)
92
self.global_palette = self.palette = p
95
self.__fp = self.fp # FIXME: hack
96
self.__rewind = self.fp.tell()
97
self.seek(0) # get ready to read first frame
99
def seek(self, frame):
106
self.__fp.seek(self.__rewind)
108
if frame != self.__frame + 1:
109
raise ValueError, "cannot seek to frame %d" % frame
116
# backup to last frame
117
self.fp.seek(self.__offset)
123
self.im = self.dispose
126
self.palette = self.global_palette
131
if not s or s == ";":
142
# graphic control extension
144
flags = ord(block[0])
146
self.info["transparency"] = ord(block[3])
147
self.info["duration"] = i16(block[1:3]) * 10
151
# replace with background colour
152
self.dispose = Image.core.fill("P", self.size,
153
self.info["background"])
155
# replace with previous contents
156
self.dispose = self.im.copy()
157
except (AttributeError, KeyError):
161
# application extension
163
self.info["extension"] = block, self.fp.tell()
164
if block[:11] == "NETSCAPE2.0":
166
if len(block) >= 3 and ord(block[0]) == 1:
167
self.info["loop"] = i16(block[1:3])
178
x0, y0 = i16(s[0:]), i16(s[2:])
179
x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:])
182
interlace = (flags & 64) != 0
185
bits = (flags & 7) + 1
187
ImagePalette.raw("RGB", self.fp.read(3<<bits))
190
bits = ord(self.fp.read(1))
191
self.__offset = self.fp.tell()
200
# raise IOError, "illegal GIF tag `%x`" % ord(s)
204
raise EOFError, "no more images in GIF file"
214
# --------------------------------------------------------------------
228
def _save(im, fp, filename):
231
# call external driver
233
_imaging_gif.save(im, fp, filename)
236
pass # write uncompressed file
239
rawmode = RAWMODE[im.mode]
242
# convert on the fly (EXPERIMENTAL -- I'm not sure PIL
243
# should automatically convert images on save...)
244
if Image.getmodebase(im.mode) == "RGB":
245
imOut = im.convert("P")
248
imOut = im.convert("L")
252
for s in getheader(imOut, im.encoderinfo):
258
interlace = im.encoderinfo["interlace"]
262
# workaround for @PIL153
263
if min(im.size) < 16:
270
transparency = im.encoderinfo["transparency"]
274
# transparency extension block
276
chr(249) + # extension intro
278
chr(1) + # transparency info present
280
chr(int(transparency)) # transparency index
285
o16(0) + o16(0) + # bounding box
286
o16(im.size[0]) + # size
291
imOut.encoderconfig = (8, interlace)
293
ImageFile._save(imOut, fp, [("gif", (0,0)+im.size, 0, rawmode)])
295
fp.write("\0") # end of image data
297
fp.write(";") # end of file
303
def _save_netpbm(im, fp, filename):
306
# If you need real GIF compression and/or RGB quantization, you
307
# can use the external NETPBM/PBMPLUS utilities. See comments
308
# below for information on how to enable this.
313
os.system("ppmtogif %s >%s" % (file, filename))
315
os.system("ppmquant 256 %s | ppmtogif >%s" % (file, filename))
320
# --------------------------------------------------------------------
323
def getheader(im, info=None):
324
"""Return a list of strings representing a GIF header"""
326
optimize = info and info.get("optimize", 0)
330
o16(im.size[0]) + # size
332
chr(7 + 128) + # flags: bits + palette
333
chr(0) + # background
334
chr(0) # reserved/aspect
338
# minimize color palette
341
for count in im.histogram():
351
s.append(im.im.getpalette("RGB")[:maxcolor*3])
354
for i in range(maxcolor):
359
def getdata(im, offset = (0, 0), **params):
360
"""Return a list of strings representing this image.
361
The first string is a local image header, the rest contains
362
encoded image data."""
366
def write(self, data):
367
self.data.append(data)
369
im.load() # make sure raster data is available
374
im.encoderinfo = params
378
o16(offset[0]) + # offset
380
o16(im.size[0]) + # size
385
ImageFile._save(im, fp, [("gif", (0,0)+im.size, 0, RAWMODE[im.mode])])
387
fp.write("\0") # end of image data
395
# --------------------------------------------------------------------
398
Image.register_open(GifImageFile.format, GifImageFile, _accept)
399
Image.register_save(GifImageFile.format, _save)
400
Image.register_extension(GifImageFile.format, ".gif")
401
Image.register_mime(GifImageFile.format, "image/gif")
404
# Uncomment the following line if you wish to use NETPBM/PBMPLUS
405
# instead of the built-in "uncompressed" GIF encoder
407
# Image.register_save(GifImageFile.format, _save_netpbm)