Repository: python-pillow/Pillow

Issue:
Last frame of gif has second last frame merged to it
### What did you do?
I iterated through gif frames and added copies of the frames converted to rgba to a list of the following 4 frame gif ![gif](https://raw.githubusercontent.com/s0hvaperuna/pillow-images/master/eevee.gif "gif")

This was the code I used and what produced the invalid result
```py
frames = []
while True:
    frames.append(im.copy().convert('RGBA'))
    try:
        im.seek(im.tell() + 1)
    except EOFError:
        break
```
### What did you expect to happen?
Expected that all the frames would look normal without them merging
### What actually happened?
The last 2 images merged ![Invalid frame](https://raw.githubusercontent.com/s0hvaperuna/pillow-images/master/frame_3_invalid.png  "Invalid frame") as in the following pic when the expected result would be ![Correct frame](https://raw.githubusercontent.com/s0hvaperuna/pillow-images/master/frame_3_correct.png "Correct frame")
The second last frame was kept normal but the last frame included the second last frame merged to it
I was able to get the desired result by using the following modification of my original loop
```py
frames = []
while True:
    frames.append(im.copy().convert('RGBA'))
    try:
        im.seek(im.tell() + 1)
    except EOFError:
        frames[-1] = im.copy().convert('RGBA')
        break
```
### What versions of Pillow and Python are you using?
Python 3.6.4
Pillow 5.1.0


Discussion:
GIFs have a value called a 'disposal method'. For the last frame in this image, it is set to 'Do not dispose. The graphic is to be left in place.'

So from what I can see, Pillow is behaving correctly. Clearly standard image viewers have other ideas about what to do in this situation though, and I don't know what logic they are using.
This is resolved by #3434.

I'm also going to move discussion of https://github.com/python-pillow/Pillow/issues/3665#issuecomment-471973989 here, which looks like the same problem, and is also solved by #3434.

> ```python
> import requests
> from PIL import Image
> response = requests.get('https://66.media.tumblr.com/tumblr_lzkp40b0Sl1qhwcy0.gif')
> im = Image.open(BytesIO(response.content))
> im.save('1.png', 'PNG')
> im.seek(1)
> im.load()
> im.save('2.png', 'PNG')
> ```
> The `2.png` has second frame pasted onto first.
> Expected result is second frame pasted into background (transparent).

Resolving patch:
diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py
--- a/src/PIL/GifImagePlugin.py
+++ b/src/PIL/GifImagePlugin.py
@@ -301,13 +301,14 @@ def 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:
+        if self._prev_im and self._prev_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._crop(self.im, 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()
+        self._prev_disposal_method = self.disposal_method
 
     def _close__fp(self):
         try:


Test patch:
diff --git a/Tests/images/dispose_none_load_end.gif b/Tests/images/dispose_none_load_end.gif
new file mode 100644
Binary files /dev/null and b/Tests/images/dispose_none_load_end.gif differ
diff --git a/Tests/images/dispose_none_load_end_second.gif b/Tests/images/dispose_none_load_end_second.gif
new file mode 100644
Binary files /dev/null and b/Tests/images/dispose_none_load_end_second.gif differ
diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py
--- a/Tests/test_file_gif.py
+++ b/Tests/test_file_gif.py
@@ -307,6 +307,20 @@ def test_dispose_none():
             pass
 
 
+def test_dispose_none_load_end():
+    # Test image created with:
+    #
+    # im = Image.open("transparent.gif")
+    # im_rotated = im.rotate(180)
+    # im.save("dispose_none_load_end.gif",
+    #         save_all=True, append_images=[im_rotated], disposal=[1,2])
+    with Image.open("Tests/images/dispose_none_load_end.gif") as img:
+        img.seek(1)
+
+        with Image.open("Tests/images/dispose_none_load_end_second.gif") as expected:
+            assert_image_equal(img, expected)
+
+
 def test_dispose_background():
     with Image.open("Tests/images/dispose_bgnd.gif") as img:
         try: