Handle transparency between frames for animated GIFs

Show the previous frame for transparent pixels when the disposal method is 'do not dispose'.

This fixes issue 634.
This commit is contained in:
Lars Jørgen Solberg 2014-07-03 18:48:12 +02:00
parent 08a9bdbcd6
commit f88a355c6c

View File

@ -99,6 +99,8 @@ class GifImageFile(ImageFile.ImageFile):
self.dispose_extent = [0, 0, 0, 0] #x0, y0, x1, y1 self.dispose_extent = [0, 0, 0, 0] #x0, y0, x1, y1
self.__frame = -1 self.__frame = -1
self.__fp.seek(self.__rewind) self.__fp.seek(self.__rewind)
self._prev_im = None
self.disposal_method = 0
if frame != self.__frame + 1: if frame != self.__frame + 1:
raise ValueError("cannot seek to frame %d" % frame) raise ValueError("cannot seek to frame %d" % frame)
@ -120,7 +122,6 @@ class GifImageFile(ImageFile.ImageFile):
from copy import copy from copy import copy
self.palette = copy(self.global_palette) self.palette = copy(self.global_palette)
disposal_method = 0
while True: while True:
s = self.fp.read(1) s = self.fp.read(1)
@ -143,8 +144,8 @@ class GifImageFile(ImageFile.ImageFile):
self.info["duration"] = i16(block[1:3]) * 10 self.info["duration"] = i16(block[1:3]) * 10
# disposal method - find the value of bits 4 - 6 # disposal method - find the value of bits 4 - 6
disposal_method = 0b00011100 & flags self.disposal_method = 0b00011100 & flags
disposal_method = disposal_method >> 2 self.disposal_method = self.disposal_method >> 2
elif i8(s) == 255: elif i8(s) == 255:
# #
# application extension # application extension
@ -190,10 +191,10 @@ class GifImageFile(ImageFile.ImageFile):
# raise IOError, "illegal GIF tag `%x`" % i8(s) # raise IOError, "illegal GIF tag `%x`" % i8(s)
try: try:
if disposal_method < 2: if self.disposal_method < 2:
# do not dispose or none specified # do not dispose or none specified
self.dispose = None self.dispose = None
elif disposal_method == 2: elif self.disposal_method == 2:
# replace with background colour # replace with background colour
self.dispose = Image.core.fill("P", self.size, self.dispose = Image.core.fill("P", self.size,
self.info["background"]) self.info["background"])
@ -220,6 +221,18 @@ class GifImageFile(ImageFile.ImageFile):
def tell(self): def tell(self):
return self.__frame return self.__frame
def load_end(self):
ImageFile.ImageFile.load_end(self)
# if the disposal method is 'do not dispose', transparent
# pixels should show the content of the previous frame
if self._prev_im and self.disposal_method == 1:
# we do this by pasting the updated area onto the previous
# frame which we then use as the current image content
updated = self.im.crop(self.dispose_extent)
self._prev_im.paste(updated, self.dispose_extent, updated.convert('RGBA'))
self.im = self._prev_im
self._prev_im = self.im.copy()
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Write GIF files # Write GIF files