We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
WebPImagePlugin.py - ResourceWarning: unclosed file
### What did you do?

```console
> python -Wd
Python 3.7.0 (default, Jul 15 2018, 10:44:58)
[GCC 8.1.1 20180531] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from PIL import Image
>>> with Image.open('tshirt.webp') as img:
...     img.save('tshirt.png', 'PNG')
...
/home/pajadam/.venv/hype/lib/python3.7/site-packages/PIL/WebPImagePlugin.py:156: ResourceWarning: unclosed file <_io.BufferedReader name='tshirt.webp'>
  self.fp = BytesIO(data)
>>>
```

### What did you expect to happen?

No warnings, especially while using context manager. 

### What actually happened?

Following warning occurs:

```
/home/pajadam/.venv/hype/lib/python3.7/site-packages/PIL/WebPImagePlugin.py:156: ResourceWarning: unclosed file <_io.BufferedReader name='tshirt.webp'>
  self.fp = BytesIO(data)
```

### What versions of Pillow and Python are you using?

- `Pillow==5.2`
- `Python 3.7`

Minimal example:
```python
from PIL import Image

with Image.open('tshirt.webp') as img:
     img.save('tshirt.png', 'PNG')
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/PIL/WebPImagePlugin.py`
Content:
```
1 from . import Image, ImageFile, _webp
2 from io import BytesIO
3 
4 
5 _VALID_WEBP_MODES = {
6     "RGBX": True,
7     "RGBA": True,
8     }
9 
10 _VALID_WEBP_LEGACY_MODES = {
11     "RGB": True,
12     "RGBA": True,
13     }
14 
15 _VP8_MODES_BY_IDENTIFIER = {
16     b"VP8 ": "RGB",
17     b"VP8X": "RGBA",
18     b"VP8L": "RGBA",  # lossless
19     }
20 
21 
22 def _accept(prefix):
23     is_riff_file_format = prefix[:4] == b"RIFF"
24     is_webp_file = prefix[8:12] == b"WEBP"
25     is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER
26 
27     return is_riff_file_format and is_webp_file and is_valid_vp8_mode
28 
29 
30 class WebPImageFile(ImageFile.ImageFile):
31 
32     format = "WEBP"
33     format_description = "WebP image"
34 
35     def _open(self):
36         if not _webp.HAVE_WEBPANIM:
37             # Legacy mode
38             data, width, height, self.mode, icc_profile, exif = \
39                 _webp.WebPDecode(self.fp.read())
40             if icc_profile:
41                 self.info["icc_profile"] = icc_profile
42             if exif:
43                 self.info["exif"] = exif
44             self.size = width, height
45             self.fp = BytesIO(data)
46             self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
47             self._n_frames = 1
48             return
49 
50         # Use the newer AnimDecoder API to parse the (possibly) animated file,
51         # and access muxed chunks like ICC/EXIF/XMP.
52         self._decoder = _webp.WebPAnimDecoder(self.fp.read())
53 
54         # Get info from decoder
55         width, height, loop_count, bgcolor, frame_count, mode = \
56             self._decoder.get_info()
57         self.size = width, height
58         self.info["loop"] = loop_count
59         bg_a, bg_r, bg_g, bg_b = \
60             (bgcolor >> 24) & 0xFF, \
61             (bgcolor >> 16) & 0xFF, \
62             (bgcolor >> 8) & 0xFF, \
63             bgcolor & 0xFF
64         self.info["background"] = (bg_r, bg_g, bg_b, bg_a)
65         self._n_frames = frame_count
66         self.mode = mode
67         self.tile = []
68 
69         # Attempt to read ICC / EXIF / XMP chunks from file
70         icc_profile = self._decoder.get_chunk("ICCP")
71         exif = self._decoder.get_chunk("EXIF")
72         xmp = self._decoder.get_chunk("XMP ")
73         if icc_profile:
74             self.info["icc_profile"] = icc_profile
75         if exif:
76             self.info["exif"] = exif
77         if xmp:
78             self.info["xmp"] = xmp
79 
80         # Initialize seek state
81         self._reset(reset=False)
82         self.seek(0)
83 
84     def _getexif(self):
85         from .JpegImagePlugin import _getexif
86         return _getexif(self)
87 
88     @property
89     def n_frames(self):
90         return self._n_frames
91 
92     @property
93     def is_animated(self):
94         return self._n_frames > 1
95 
96     def seek(self, frame):
97         if not _webp.HAVE_WEBPANIM:
98             return super(WebPImageFile, self).seek(frame)
99 
100         # Perform some simple checks first
101         if frame >= self._n_frames:
102             raise EOFError("attempted to seek beyond end of sequence")
103         if frame < 0:
104             raise EOFError("negative frame index is not valid")
105 
106         # Set logical frame to requested position
107         self.__logical_frame = frame
108 
109     def _reset(self, reset=True):
110         if reset:
111             self._decoder.reset()
112         self.__physical_frame = 0
113         self.__loaded = -1
114         self.__timestamp = 0
115 
116     def _get_next(self):
117         # Get next frame
118         ret = self._decoder.get_next()
119         self.__physical_frame += 1
120 
121         # Check if an error occurred
122         if ret is None:
123             self._reset()   # Reset just to be safe
124             self.seek(0)
125             raise EOFError("failed to decode next frame in WebP file")
126 
127         # Compute duration
128         data, timestamp = ret
129         duration = timestamp - self.__timestamp
130         self.__timestamp = timestamp
131 
132         # libwebp gives frame end, adjust to start of frame
133         timestamp -= duration
134         return data, timestamp, duration
135 
136     def _seek(self, frame):
137         if self.__physical_frame == frame:
138             return              # Nothing to do
139         if frame < self.__physical_frame:
140             self._reset()       # Rewind to beginning
141         while self.__physical_frame < frame:
142             self._get_next()    # Advance to the requested frame
143 
144     def load(self):
145         if _webp.HAVE_WEBPANIM:
146             if self.__loaded != self.__logical_frame:
147                 self._seek(self.__logical_frame)
148 
149                 # We need to load the image data for this frame
150                 data, timestamp, duration = self._get_next()
151                 self.info["timestamp"] = timestamp
152                 self.info["duration"] = duration
153                 self.__loaded = self.__logical_frame
154 
155                 # Set tile
156                 self.fp = BytesIO(data)
157                 self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
158 
159         return super(WebPImageFile, self).load()
160 
161     def tell(self):
162         if not _webp.HAVE_WEBPANIM:
163             return super(WebPImageFile, self).tell()
164 
165         return self.__logical_frame
166 
167 
168 def _save_all(im, fp, filename):
169     encoderinfo = im.encoderinfo.copy()
170     append_images = list(encoderinfo.get("append_images", []))
171 
172     # If total frame count is 1, then save using the legacy API, which
173     # will preserve non-alpha modes
174     total = 0
175     for ims in [im]+append_images:
176         total += 1 if not hasattr(ims, "n_frames") else ims.n_frames
177     if total == 1:
178         _save(im, fp, filename)
179         return
180 
181     background = encoderinfo.get("background", (0, 0, 0, 0))
182     duration = im.encoderinfo.get("duration", 0)
183     loop = im.encoderinfo.get("loop", 0)
184     minimize_size = im.encoderinfo.get("minimize_size", False)
185     kmin = im.encoderinfo.get("kmin", None)
186     kmax = im.encoderinfo.get("kmax", None)
187     allow_mixed = im.encoderinfo.get("allow_mixed", False)
188     verbose = False
189     lossless = im.encoderinfo.get("lossless", False)
190     quality = im.encoderinfo.get("quality", 80)
191     method = im.encoderinfo.get("method", 0)
192     icc_profile = im.encoderinfo.get("icc_profile", "")
193     exif = im.encoderinfo.get("exif", "")
194     xmp = im.encoderinfo.get("xmp", "")
195     if allow_mixed:
196         lossless = False
197 
198     # Sensible keyframe defaults are from gif2webp.c script
199     if kmin is None:
200         kmin = 9 if lossless else 3
201     if kmax is None:
202         kmax = 17 if lossless else 5
203 
204     # Validate background color
205     if (not isinstance(background, (list, tuple)) or len(background) != 4 or
206             not all(v >= 0 and v < 256 for v in background)):
207         raise IOError("Background color is not an RGBA tuple clamped "
208                       "to (0-255): %s" % str(background))
209 
210     # Convert to packed uint
211     bg_r, bg_g, bg_b, bg_a = background
212     background = (bg_a << 24) | (bg_r << 16) | (bg_g << 8) | (bg_b << 0)
213 
214     # Setup the WebP animation encoder
215     enc = _webp.WebPAnimEncoder(
216         im.size[0], im.size[1],
217         background,
218         loop,
219         minimize_size,
220         kmin, kmax,
221         allow_mixed,
222         verbose
223     )
224 
225     # Add each frame
226     frame_idx = 0
227     timestamp = 0
228     cur_idx = im.tell()
229     try:
230         for ims in [im]+append_images:
231             # Get # of frames in this image
232             if not hasattr(ims, "n_frames"):
233                 nfr = 1
234             else:
235                 nfr = ims.n_frames
236 
237             for idx in range(nfr):
238                 ims.seek(idx)
239                 ims.load()
240 
241                 # Make sure image mode is supported
242                 frame = ims
243                 if ims.mode not in _VALID_WEBP_MODES:
244                     alpha = ims.mode == 'P' and 'A' in ims.im.getpalettemode()
245                     frame = ims.convert('RGBA' if alpha else 'RGBX')
246 
247                 # Append the frame to the animation encoder
248                 enc.add(
249                     frame.tobytes(),
250                     timestamp,
251                     frame.size[0], frame.size[1],
252                     frame.mode,
253                     lossless,
254                     quality,
255                     method
256                 )
257 
258                 # Update timestamp and frame index
259                 if isinstance(duration, (list, tuple)):
260                     timestamp += duration[frame_idx]
261                 else:
262                     timestamp += duration
263                 frame_idx += 1
264 
265     finally:
266         im.seek(cur_idx)
267 
268     # Force encoder to flush frames
269     enc.add(
270         None,
271         timestamp,
272         0, 0, "", lossless, quality, 0
273     )
274 
275     # Get the final output from the encoder
276     data = enc.assemble(icc_profile, exif, xmp)
277     if data is None:
278         raise IOError("cannot write file as WebP (encoder returned None)")
279 
280     fp.write(data)
281 
282 
283 def _save(im, fp, filename):
284     lossless = im.encoderinfo.get("lossless", False)
285     quality = im.encoderinfo.get("quality", 80)
286     icc_profile = im.encoderinfo.get("icc_profile", "")
287     exif = im.encoderinfo.get("exif", "")
288     xmp = im.encoderinfo.get("xmp", "")
289 
290     if im.mode not in _VALID_WEBP_LEGACY_MODES:
291         alpha = im.mode == 'P' and 'A' in im.im.getpalettemode()
292         im = im.convert('RGBA' if alpha else 'RGB')
293 
294     data = _webp.WebPEncode(
295         im.tobytes(),
296         im.size[0],
297         im.size[1],
298         lossless,
299         float(quality),
300         im.mode,
301         icc_profile,
302         exif,
303         xmp
304     )
305     if data is None:
306         raise IOError("cannot write file as WebP (encoder returned None)")
307 
308     fp.write(data)
309 
310 
311 Image.register_open(WebPImageFile.format, WebPImageFile, _accept)
312 Image.register_save(WebPImageFile.format, _save)
313 if _webp.HAVE_WEBPANIM:
314     Image.register_save_all(WebPImageFile.format, _save_all)
315 Image.register_extension(WebPImageFile.format, ".webp")
316 Image.register_mime(WebPImageFile.format, "image/webp")
317
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py
--- a/src/PIL/WebPImagePlugin.py
+++ b/src/PIL/WebPImagePlugin.py
@@ -153,6 +153,8 @@
                 self.__loaded = self.__logical_frame
 
                 # Set tile
+                if self.fp:
+                    self.fp.close()
                 self.fp = BytesIO(data)
                 self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]

{"golden_diff": "diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py\n--- a/src/PIL/WebPImagePlugin.py\n+++ b/src/PIL/WebPImagePlugin.py\n@@ -153,6 +153,8 @@\n                 self.__loaded = self.__logical_frame\n \n                 # Set tile\n+                if self.fp:\n+                    self.fp.close()\n                 self.fp = BytesIO(data)\n                 self.tile = [(\"raw\", (0, 0) + self.size, 0, self.mode)]\n", "issue": "WebPImagePlugin.py - ResourceWarning: unclosed file\n### What did you do?\r\n\r\n```console\r\n> python -Wd\r\nPython 3.7.0 (default, Jul 15 2018, 10:44:58)\r\n[GCC 8.1.1 20180531] on linux\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n>>> from PIL import Image\r\n>>> with Image.open('tshirt.webp') as img:\r\n...     img.save('tshirt.png', 'PNG')\r\n...\r\n/home/pajadam/.venv/hype/lib/python3.7/site-packages/PIL/WebPImagePlugin.py:156: ResourceWarning: unclosed file <_io.BufferedReader name='tshirt.webp'>\r\n  self.fp = BytesIO(data)\r\n>>>\r\n```\r\n\r\n### What did you expect to happen?\r\n\r\nNo warnings, especially while using context manager. \r\n\r\n### What actually happened?\r\n\r\nFollowing warning occurs:\r\n\r\n```\r\n/home/pajadam/.venv/hype/lib/python3.7/site-packages/PIL/WebPImagePlugin.py:156: ResourceWarning: unclosed file <_io.BufferedReader name='tshirt.webp'>\r\n  self.fp = BytesIO(data)\r\n```\r\n\r\n### What versions of Pillow and Python are you using?\r\n\r\n- `Pillow==5.2`\r\n- `Python 3.7`\r\n\r\nMinimal example:\r\n```python\r\nfrom PIL import Image\r\n\r\nwith Image.open('tshirt.webp') as img:\r\n     img.save('tshirt.png', 'PNG')\r\n```\r\n\n", "before_files": [{"content": "from . import Image, ImageFile, _webp\nfrom io import BytesIO\n\n\n_VALID_WEBP_MODES = {\n    \"RGBX\": True,\n    \"RGBA\": True,\n    }\n\n_VALID_WEBP_LEGACY_MODES = {\n    \"RGB\": True,\n    \"RGBA\": True,\n    }\n\n_VP8_MODES_BY_IDENTIFIER = {\n    b\"VP8 \": \"RGB\",\n    b\"VP8X\": \"RGBA\",\n    b\"VP8L\": \"RGBA\",  # lossless\n    }\n\n\ndef _accept(prefix):\n    is_riff_file_format = prefix[:4] == b\"RIFF\"\n    is_webp_file = prefix[8:12] == b\"WEBP\"\n    is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER\n\n    return is_riff_file_format and is_webp_file and is_valid_vp8_mode\n\n\nclass WebPImageFile(ImageFile.ImageFile):\n\n    format = \"WEBP\"\n    format_description = \"WebP image\"\n\n    def _open(self):\n        if not _webp.HAVE_WEBPANIM:\n            # Legacy mode\n            data, width, height, self.mode, icc_profile, exif = \\\n                _webp.WebPDecode(self.fp.read())\n            if icc_profile:\n                self.info[\"icc_profile\"] = icc_profile\n            if exif:\n                self.info[\"exif\"] = exif\n            self.size = width, height\n            self.fp = BytesIO(data)\n            self.tile = [(\"raw\", (0, 0) + self.size, 0, self.mode)]\n            self._n_frames = 1\n            return\n\n        # Use the newer AnimDecoder API to parse the (possibly) animated file,\n        # and access muxed chunks like ICC/EXIF/XMP.\n        self._decoder = _webp.WebPAnimDecoder(self.fp.read())\n\n        # Get info from decoder\n        width, height, loop_count, bgcolor, frame_count, mode = \\\n            self._decoder.get_info()\n        self.size = width, height\n        self.info[\"loop\"] = loop_count\n        bg_a, bg_r, bg_g, bg_b = \\\n            (bgcolor >> 24) & 0xFF, \\\n            (bgcolor >> 16) & 0xFF, \\\n            (bgcolor >> 8) & 0xFF, \\\n            bgcolor & 0xFF\n        self.info[\"background\"] = (bg_r, bg_g, bg_b, bg_a)\n        self._n_frames = frame_count\n        self.mode = mode\n        self.tile = []\n\n        # Attempt to read ICC / EXIF / XMP chunks from file\n        icc_profile = self._decoder.get_chunk(\"ICCP\")\n        exif = self._decoder.get_chunk(\"EXIF\")\n        xmp = self._decoder.get_chunk(\"XMP \")\n        if icc_profile:\n            self.info[\"icc_profile\"] = icc_profile\n        if exif:\n            self.info[\"exif\"] = exif\n        if xmp:\n            self.info[\"xmp\"] = xmp\n\n        # Initialize seek state\n        self._reset(reset=False)\n        self.seek(0)\n\n    def _getexif(self):\n        from .JpegImagePlugin import _getexif\n        return _getexif(self)\n\n    @property\n    def n_frames(self):\n        return self._n_frames\n\n    @property\n    def is_animated(self):\n        return self._n_frames > 1\n\n    def seek(self, frame):\n        if not _webp.HAVE_WEBPANIM:\n            return super(WebPImageFile, self).seek(frame)\n\n        # Perform some simple checks first\n        if frame >= self._n_frames:\n            raise EOFError(\"attempted to seek beyond end of sequence\")\n        if frame < 0:\n            raise EOFError(\"negative frame index is not valid\")\n\n        # Set logical frame to requested position\n        self.__logical_frame = frame\n\n    def _reset(self, reset=True):\n        if reset:\n            self._decoder.reset()\n        self.__physical_frame = 0\n        self.__loaded = -1\n        self.__timestamp = 0\n\n    def _get_next(self):\n        # Get next frame\n        ret = self._decoder.get_next()\n        self.__physical_frame += 1\n\n        # Check if an error occurred\n        if ret is None:\n            self._reset()   # Reset just to be safe\n            self.seek(0)\n            raise EOFError(\"failed to decode next frame in WebP file\")\n\n        # Compute duration\n        data, timestamp = ret\n        duration = timestamp - self.__timestamp\n        self.__timestamp = timestamp\n\n        # libwebp gives frame end, adjust to start of frame\n        timestamp -= duration\n        return data, timestamp, duration\n\n    def _seek(self, frame):\n        if self.__physical_frame == frame:\n            return              # Nothing to do\n        if frame < self.__physical_frame:\n            self._reset()       # Rewind to beginning\n        while self.__physical_frame < frame:\n            self._get_next()    # Advance to the requested frame\n\n    def load(self):\n        if _webp.HAVE_WEBPANIM:\n            if self.__loaded != self.__logical_frame:\n                self._seek(self.__logical_frame)\n\n                # We need to load the image data for this frame\n                data, timestamp, duration = self._get_next()\n                self.info[\"timestamp\"] = timestamp\n                self.info[\"duration\"] = duration\n                self.__loaded = self.__logical_frame\n\n                # Set tile\n                self.fp = BytesIO(data)\n                self.tile = [(\"raw\", (0, 0) + self.size, 0, self.mode)]\n\n        return super(WebPImageFile, self).load()\n\n    def tell(self):\n        if not _webp.HAVE_WEBPANIM:\n            return super(WebPImageFile, self).tell()\n\n        return self.__logical_frame\n\n\ndef _save_all(im, fp, filename):\n    encoderinfo = im.encoderinfo.copy()\n    append_images = list(encoderinfo.get(\"append_images\", []))\n\n    # If total frame count is 1, then save using the legacy API, which\n    # will preserve non-alpha modes\n    total = 0\n    for ims in [im]+append_images:\n        total += 1 if not hasattr(ims, \"n_frames\") else ims.n_frames\n    if total == 1:\n        _save(im, fp, filename)\n        return\n\n    background = encoderinfo.get(\"background\", (0, 0, 0, 0))\n    duration = im.encoderinfo.get(\"duration\", 0)\n    loop = im.encoderinfo.get(\"loop\", 0)\n    minimize_size = im.encoderinfo.get(\"minimize_size\", False)\n    kmin = im.encoderinfo.get(\"kmin\", None)\n    kmax = im.encoderinfo.get(\"kmax\", None)\n    allow_mixed = im.encoderinfo.get(\"allow_mixed\", False)\n    verbose = False\n    lossless = im.encoderinfo.get(\"lossless\", False)\n    quality = im.encoderinfo.get(\"quality\", 80)\n    method = im.encoderinfo.get(\"method\", 0)\n    icc_profile = im.encoderinfo.get(\"icc_profile\", \"\")\n    exif = im.encoderinfo.get(\"exif\", \"\")\n    xmp = im.encoderinfo.get(\"xmp\", \"\")\n    if allow_mixed:\n        lossless = False\n\n    # Sensible keyframe defaults are from gif2webp.c script\n    if kmin is None:\n        kmin = 9 if lossless else 3\n    if kmax is None:\n        kmax = 17 if lossless else 5\n\n    # Validate background color\n    if (not isinstance(background, (list, tuple)) or len(background) != 4 or\n            not all(v >= 0 and v < 256 for v in background)):\n        raise IOError(\"Background color is not an RGBA tuple clamped \"\n                      \"to (0-255): %s\" % str(background))\n\n    # Convert to packed uint\n    bg_r, bg_g, bg_b, bg_a = background\n    background = (bg_a << 24) | (bg_r << 16) | (bg_g << 8) | (bg_b << 0)\n\n    # Setup the WebP animation encoder\n    enc = _webp.WebPAnimEncoder(\n        im.size[0], im.size[1],\n        background,\n        loop,\n        minimize_size,\n        kmin, kmax,\n        allow_mixed,\n        verbose\n    )\n\n    # Add each frame\n    frame_idx = 0\n    timestamp = 0\n    cur_idx = im.tell()\n    try:\n        for ims in [im]+append_images:\n            # Get # of frames in this image\n            if not hasattr(ims, \"n_frames\"):\n                nfr = 1\n            else:\n                nfr = ims.n_frames\n\n            for idx in range(nfr):\n                ims.seek(idx)\n                ims.load()\n\n                # Make sure image mode is supported\n                frame = ims\n                if ims.mode not in _VALID_WEBP_MODES:\n                    alpha = ims.mode == 'P' and 'A' in ims.im.getpalettemode()\n                    frame = ims.convert('RGBA' if alpha else 'RGBX')\n\n                # Append the frame to the animation encoder\n                enc.add(\n                    frame.tobytes(),\n                    timestamp,\n                    frame.size[0], frame.size[1],\n                    frame.mode,\n                    lossless,\n                    quality,\n                    method\n                )\n\n                # Update timestamp and frame index\n                if isinstance(duration, (list, tuple)):\n                    timestamp += duration[frame_idx]\n                else:\n                    timestamp += duration\n                frame_idx += 1\n\n    finally:\n        im.seek(cur_idx)\n\n    # Force encoder to flush frames\n    enc.add(\n        None,\n        timestamp,\n        0, 0, \"\", lossless, quality, 0\n    )\n\n    # Get the final output from the encoder\n    data = enc.assemble(icc_profile, exif, xmp)\n    if data is None:\n        raise IOError(\"cannot write file as WebP (encoder returned None)\")\n\n    fp.write(data)\n\n\ndef _save(im, fp, filename):\n    lossless = im.encoderinfo.get(\"lossless\", False)\n    quality = im.encoderinfo.get(\"quality\", 80)\n    icc_profile = im.encoderinfo.get(\"icc_profile\", \"\")\n    exif = im.encoderinfo.get(\"exif\", \"\")\n    xmp = im.encoderinfo.get(\"xmp\", \"\")\n\n    if im.mode not in _VALID_WEBP_LEGACY_MODES:\n        alpha = im.mode == 'P' and 'A' in im.im.getpalettemode()\n        im = im.convert('RGBA' if alpha else 'RGB')\n\n    data = _webp.WebPEncode(\n        im.tobytes(),\n        im.size[0],\n        im.size[1],\n        lossless,\n        float(quality),\n        im.mode,\n        icc_profile,\n        exif,\n        xmp\n    )\n    if data is None:\n        raise IOError(\"cannot write file as WebP (encoder returned None)\")\n\n    fp.write(data)\n\n\nImage.register_open(WebPImageFile.format, WebPImageFile, _accept)\nImage.register_save(WebPImageFile.format, _save)\nif _webp.HAVE_WEBPANIM:\n    Image.register_save_all(WebPImageFile.format, _save_all)\nImage.register_extension(WebPImageFile.format, \".webp\")\nImage.register_mime(WebPImageFile.format, \"image/webp\")\n", "path": "src/PIL/WebPImagePlugin.py"}], "after_files": [{"content": "from . import Image, ImageFile, _webp\nfrom io import BytesIO\n\n\n_VALID_WEBP_MODES = {\n    \"RGBX\": True,\n    \"RGBA\": True,\n    }\n\n_VALID_WEBP_LEGACY_MODES = {\n    \"RGB\": True,\n    \"RGBA\": True,\n    }\n\n_VP8_MODES_BY_IDENTIFIER = {\n    b\"VP8 \": \"RGB\",\n    b\"VP8X\": \"RGBA\",\n    b\"VP8L\": \"RGBA\",  # lossless\n    }\n\n\ndef _accept(prefix):\n    is_riff_file_format = prefix[:4] == b\"RIFF\"\n    is_webp_file = prefix[8:12] == b\"WEBP\"\n    is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER\n\n    return is_riff_file_format and is_webp_file and is_valid_vp8_mode\n\n\nclass WebPImageFile(ImageFile.ImageFile):\n\n    format = \"WEBP\"\n    format_description = \"WebP image\"\n\n    def _open(self):\n        if not _webp.HAVE_WEBPANIM:\n            # Legacy mode\n            data, width, height, self.mode, icc_profile, exif = \\\n                _webp.WebPDecode(self.fp.read())\n            if icc_profile:\n                self.info[\"icc_profile\"] = icc_profile\n            if exif:\n                self.info[\"exif\"] = exif\n            self.size = width, height\n            self.fp = BytesIO(data)\n            self.tile = [(\"raw\", (0, 0) + self.size, 0, self.mode)]\n            self._n_frames = 1\n            return\n\n        # Use the newer AnimDecoder API to parse the (possibly) animated file,\n        # and access muxed chunks like ICC/EXIF/XMP.\n        self._decoder = _webp.WebPAnimDecoder(self.fp.read())\n\n        # Get info from decoder\n        width, height, loop_count, bgcolor, frame_count, mode = \\\n            self._decoder.get_info()\n        self.size = width, height\n        self.info[\"loop\"] = loop_count\n        bg_a, bg_r, bg_g, bg_b = \\\n            (bgcolor >> 24) & 0xFF, \\\n            (bgcolor >> 16) & 0xFF, \\\n            (bgcolor >> 8) & 0xFF, \\\n            bgcolor & 0xFF\n        self.info[\"background\"] = (bg_r, bg_g, bg_b, bg_a)\n        self._n_frames = frame_count\n        self.mode = mode\n        self.tile = []\n\n        # Attempt to read ICC / EXIF / XMP chunks from file\n        icc_profile = self._decoder.get_chunk(\"ICCP\")\n        exif = self._decoder.get_chunk(\"EXIF\")\n        xmp = self._decoder.get_chunk(\"XMP \")\n        if icc_profile:\n            self.info[\"icc_profile\"] = icc_profile\n        if exif:\n            self.info[\"exif\"] = exif\n        if xmp:\n            self.info[\"xmp\"] = xmp\n\n        # Initialize seek state\n        self._reset(reset=False)\n        self.seek(0)\n\n    def _getexif(self):\n        from .JpegImagePlugin import _getexif\n        return _getexif(self)\n\n    @property\n    def n_frames(self):\n        return self._n_frames\n\n    @property\n    def is_animated(self):\n        return self._n_frames > 1\n\n    def seek(self, frame):\n        if not _webp.HAVE_WEBPANIM:\n            return super(WebPImageFile, self).seek(frame)\n\n        # Perform some simple checks first\n        if frame >= self._n_frames:\n            raise EOFError(\"attempted to seek beyond end of sequence\")\n        if frame < 0:\n            raise EOFError(\"negative frame index is not valid\")\n\n        # Set logical frame to requested position\n        self.__logical_frame = frame\n\n    def _reset(self, reset=True):\n        if reset:\n            self._decoder.reset()\n        self.__physical_frame = 0\n        self.__loaded = -1\n        self.__timestamp = 0\n\n    def _get_next(self):\n        # Get next frame\n        ret = self._decoder.get_next()\n        self.__physical_frame += 1\n\n        # Check if an error occurred\n        if ret is None:\n            self._reset()   # Reset just to be safe\n            self.seek(0)\n            raise EOFError(\"failed to decode next frame in WebP file\")\n\n        # Compute duration\n        data, timestamp = ret\n        duration = timestamp - self.__timestamp\n        self.__timestamp = timestamp\n\n        # libwebp gives frame end, adjust to start of frame\n        timestamp -= duration\n        return data, timestamp, duration\n\n    def _seek(self, frame):\n        if self.__physical_frame == frame:\n            return              # Nothing to do\n        if frame < self.__physical_frame:\n            self._reset()       # Rewind to beginning\n        while self.__physical_frame < frame:\n            self._get_next()    # Advance to the requested frame\n\n    def load(self):\n        if _webp.HAVE_WEBPANIM:\n            if self.__loaded != self.__logical_frame:\n                self._seek(self.__logical_frame)\n\n                # We need to load the image data for this frame\n                data, timestamp, duration = self._get_next()\n                self.info[\"timestamp\"] = timestamp\n                self.info[\"duration\"] = duration\n                self.__loaded = self.__logical_frame\n\n                # Set tile\n                if self.fp:\n                    self.fp.close()\n                self.fp = BytesIO(data)\n                self.tile = [(\"raw\", (0, 0) + self.size, 0, self.mode)]\n\n        return super(WebPImageFile, self).load()\n\n    def tell(self):\n        if not _webp.HAVE_WEBPANIM:\n            return super(WebPImageFile, self).tell()\n\n        return self.__logical_frame\n\n\ndef _save_all(im, fp, filename):\n    encoderinfo = im.encoderinfo.copy()\n    append_images = list(encoderinfo.get(\"append_images\", []))\n\n    # If total frame count is 1, then save using the legacy API, which\n    # will preserve non-alpha modes\n    total = 0\n    for ims in [im]+append_images:\n        total += 1 if not hasattr(ims, \"n_frames\") else ims.n_frames\n    if total == 1:\n        _save(im, fp, filename)\n        return\n\n    background = encoderinfo.get(\"background\", (0, 0, 0, 0))\n    duration = im.encoderinfo.get(\"duration\", 0)\n    loop = im.encoderinfo.get(\"loop\", 0)\n    minimize_size = im.encoderinfo.get(\"minimize_size\", False)\n    kmin = im.encoderinfo.get(\"kmin\", None)\n    kmax = im.encoderinfo.get(\"kmax\", None)\n    allow_mixed = im.encoderinfo.get(\"allow_mixed\", False)\n    verbose = False\n    lossless = im.encoderinfo.get(\"lossless\", False)\n    quality = im.encoderinfo.get(\"quality\", 80)\n    method = im.encoderinfo.get(\"method\", 0)\n    icc_profile = im.encoderinfo.get(\"icc_profile\", \"\")\n    exif = im.encoderinfo.get(\"exif\", \"\")\n    xmp = im.encoderinfo.get(\"xmp\", \"\")\n    if allow_mixed:\n        lossless = False\n\n    # Sensible keyframe defaults are from gif2webp.c script\n    if kmin is None:\n        kmin = 9 if lossless else 3\n    if kmax is None:\n        kmax = 17 if lossless else 5\n\n    # Validate background color\n    if (not isinstance(background, (list, tuple)) or len(background) != 4 or\n            not all(v >= 0 and v < 256 for v in background)):\n        raise IOError(\"Background color is not an RGBA tuple clamped \"\n                      \"to (0-255): %s\" % str(background))\n\n    # Convert to packed uint\n    bg_r, bg_g, bg_b, bg_a = background\n    background = (bg_a << 24) | (bg_r << 16) | (bg_g << 8) | (bg_b << 0)\n\n    # Setup the WebP animation encoder\n    enc = _webp.WebPAnimEncoder(\n        im.size[0], im.size[1],\n        background,\n        loop,\n        minimize_size,\n        kmin, kmax,\n        allow_mixed,\n        verbose\n    )\n\n    # Add each frame\n    frame_idx = 0\n    timestamp = 0\n    cur_idx = im.tell()\n    try:\n        for ims in [im]+append_images:\n            # Get # of frames in this image\n            if not hasattr(ims, \"n_frames\"):\n                nfr = 1\n            else:\n                nfr = ims.n_frames\n\n            for idx in range(nfr):\n                ims.seek(idx)\n                ims.load()\n\n                # Make sure image mode is supported\n                frame = ims\n                if ims.mode not in _VALID_WEBP_MODES:\n                    alpha = ims.mode == 'P' and 'A' in ims.im.getpalettemode()\n                    frame = ims.convert('RGBA' if alpha else 'RGBX')\n\n                # Append the frame to the animation encoder\n                enc.add(\n                    frame.tobytes(),\n                    timestamp,\n                    frame.size[0], frame.size[1],\n                    frame.mode,\n                    lossless,\n                    quality,\n                    method\n                )\n\n                # Update timestamp and frame index\n                if isinstance(duration, (list, tuple)):\n                    timestamp += duration[frame_idx]\n                else:\n                    timestamp += duration\n                frame_idx += 1\n\n    finally:\n        im.seek(cur_idx)\n\n    # Force encoder to flush frames\n    enc.add(\n        None,\n        timestamp,\n        0, 0, \"\", lossless, quality, 0\n    )\n\n    # Get the final output from the encoder\n    data = enc.assemble(icc_profile, exif, xmp)\n    if data is None:\n        raise IOError(\"cannot write file as WebP (encoder returned None)\")\n\n    fp.write(data)\n\n\ndef _save(im, fp, filename):\n    lossless = im.encoderinfo.get(\"lossless\", False)\n    quality = im.encoderinfo.get(\"quality\", 80)\n    icc_profile = im.encoderinfo.get(\"icc_profile\", \"\")\n    exif = im.encoderinfo.get(\"exif\", \"\")\n    xmp = im.encoderinfo.get(\"xmp\", \"\")\n\n    if im.mode not in _VALID_WEBP_LEGACY_MODES:\n        alpha = im.mode == 'P' and 'A' in im.im.getpalettemode()\n        im = im.convert('RGBA' if alpha else 'RGB')\n\n    data = _webp.WebPEncode(\n        im.tobytes(),\n        im.size[0],\n        im.size[1],\n        lossless,\n        float(quality),\n        im.mode,\n        icc_profile,\n        exif,\n        xmp\n    )\n    if data is None:\n        raise IOError(\"cannot write file as WebP (encoder returned None)\")\n\n    fp.write(data)\n\n\nImage.register_open(WebPImageFile.format, WebPImageFile, _accept)\nImage.register_save(WebPImageFile.format, _save)\nif _webp.HAVE_WEBPANIM:\n    Image.register_save_all(WebPImageFile.format, _save_all)\nImage.register_extension(WebPImageFile.format, \".webp\")\nImage.register_mime(WebPImageFile.format, \"image/webp\")\n", "path": "src/PIL/WebPImagePlugin.py"}]}