Repository: python-pillow/Pillow

Issue:
GIF not loading correctly
![Mi4_broken](https://user-images.githubusercontent.com/39859365/95001291-7170a700-05c0-11eb-909a-dbc2767089b7.gif)
![Mi4_working](https://user-images.githubusercontent.com/39859365/95001292-72093d80-05c0-11eb-89ea-580aad572db5.gif)


<!--
Thank you for reporting an issue.

Follow these guidelines to ensure your issue is handled properly.

If you have a ...

1.  General question: consider asking the question on Stack Overflow
    with the python-imaging-library tag:

    * https://stackoverflow.com/questions/tagged/python-imaging-library

    Do not ask a question in both places.

    If you think you have found a bug or have an unexplained exception
    then file a bug report here.

2.  Bug report: include a self-contained, copy-pastable example that
    generates the issue if possible. Be concise with code posted.
    Guidelines on how to provide a good bug report:

    * https://stackoverflow.com/help/mcve

    Bug reports which follow these guidelines are easier to diagnose,
    and are often handled much more quickly.

3.  Feature request: do a quick search of existing issues
    to make sure this has not been asked before.

We know asking good questions takes effort, and we appreciate your time.
Thank you.
-->

### What did you do?
Loaded a GIF image and saved each individual frame as a png.

### What did you expect to happen?
Correct frames to be saved

### What actually happened?
Weird pixel corruption in saved images.

Both 'broken' and 'working' gifs render absolutely fine in Firefox.
If I re-open the 'broken' gif in Aseprite, delete the unused colours from the palette and re-save, then Pillow loads it fine.

Having re-created the GIF from scratch, Pillow loads it fine.

Could it be something to do with there being some pixel indices that are not in the first 10 colours in the broken version?
But then if so, why would Firefox render them perfectly?
I dunno.  I have a workaround anyways, but I found it an intriguing little bug.

### What are your OS, Python and Pillow versions?

* OS: Xubuntu 20.04
* Python: 3.6
* Pillow: 5.2 and 7.2

<!--
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.

The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as Plone, Django, or Buildout, try to replicate the issue just using Pillow.
-->

```python
# this is code from an image compiler for a game, with some debug lines added.
# it's the debug lines that illustrate the bug
testIndex = 0   # debug line

def loadFramesFromFile(self, path):
        file = open(path, "rb")
        result = self.loadFrames(file)
        file.close()
        return result

    def loadFrames(self, byteStream):
        global testIndex  # debug line

        resultList = []
        image = Image.open(byteStream)
        image.load()

        frameIndex = 0

        while True:
            try:
                image.seek(frameIndex)

                image.save("TEST_" + str(testIndex) + ".png")  # debug line
                testIndex += 1  # debug line

                result = FrameData()
                result.width = image.width
                result.height = image.height
                result.pixels = list(image.getdata())
                result.colours = self.generateColourTable(image)
                if "transparency" in image.info:
                    result.transparentIndex = image.info["transparency"]
                else:
                    result.transparentIndex = None
                resultList.append(result)

                frameIndex += 1
            except EOFError:
                break

        return resultList
```


Discussion:
The Global Color Table Flag isn't set. Without it, https://www.w3.org/Graphics/GIF/spec-gif89a.txt
> No Global Color Table follows, the Background Color Index field is meaningless.

And yet, the disposal method is 2, 'Restore to background color'. So, we need to restore to the background color without knowing what that background color is.

I feel like this is a gap in the specification. If I try and read your image defaulting the missing background color to zero though, it loads correctly.
I don't suppose you are happy for the image to be included in our test suite, and distributed under the Pillow license?
I do not mind at all!  Thanks for looking into it. :)

On Wed, Apr 7 2021 at 04:46:18 -0700, Andrew Murray 
***@***.***> wrote:
> I don't suppose you are happy for the image to be included in our 
> test suite, and distributed under the Pillow license?
> 
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub 
> <https://github.com/python-pillow/Pillow/issues/4943#issuecomment-814848126>, 
> or unsubscribe 
> <https://github.com/notifications/unsubscribe-auth/AJQDJJMM4ZRCXL3M3O253PTTHRAYVANCNFSM4SDHXYMQ>.
> 



Resolving patch:
diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py
--- a/src/PIL/GifImagePlugin.py
+++ b/src/PIL/GifImagePlugin.py
@@ -270,14 +270,14 @@ def _seek(self, frame):
 
                 Image._decompression_bomb_check(dispose_size)
                 self.dispose = Image.core.fill(
-                    "P", dispose_size, self.info["background"]
+                    "P", dispose_size, self.info.get("background", 0)
                 )
             else:
                 # replace with previous contents
                 if self.im:
                     # only dispose the extent in this frame
                     self.dispose = self._crop(self.im, self.dispose_extent)
-        except (AttributeError, KeyError):
+        except AttributeError:
             pass
 
         if interlace is not None:


Test patch:
diff --git a/Tests/images/missing_background.gif b/Tests/images/missing_background.gif
new file mode 100644
Binary files /dev/null and b/Tests/images/missing_background.gif differ
diff --git a/Tests/images/missing_background_first_frame.gif b/Tests/images/missing_background_first_frame.gif
new file mode 100644
Binary files /dev/null and b/Tests/images/missing_background_first_frame.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
@@ -853,3 +853,11 @@ def test_extents():
         assert im.size == (100, 100)
         im.seek(1)
         assert im.size == (150, 150)
+
+
+def test_missing_background():
+    # The Global Color Table Flag isn't set, so there is no background color index,
+    # but the disposal method is "Restore to background color"
+    with Image.open("Tests/images/missing_background.gif") as im:
+        im.seek(1)
+        assert_image_equal_tofile(im, "Tests/images/missing_background_first_frame.gif")