diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 0ce19e95449..e6210bc58c5 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -113,6 +113,7 @@ def test_l_mode_transparency_after_rgb() -> None: def test_palette_not_needed_for_second_frame() -> None: with Image.open("Tests/images/palette_not_needed_for_second_frame.gif") as im: im.seek(1) + assert im.palette is None assert_image_similar(im, hopper("L").convert("RGB"), 8) @@ -131,10 +132,12 @@ def test_strategy(monkeypatch: pytest.MonkeyPatch) -> None: ) with Image.open("Tests/images/iss634.gif") as im: assert im.mode == "RGB" + assert im.palette is None assert_image_equal(im, expected_rgb_always) with Image.open("Tests/images/chi.gif") as im: assert im.mode == "RGBA" + assert im.palette is None assert_image_equal(im, expected_rgb_always_rgba) monkeypatch.setattr( @@ -1375,7 +1378,7 @@ def test_palette_save_all_P(tmp_path: Path) -> None: ) with Image.open(out) as im: - # Assert that the frames are correct, and each frame has the same palette + # Assert that the frames are correct assert isinstance(im, GifImagePlugin.GifImageFile) assert_image_equal(im.convert("RGB"), frames[0].convert("RGB")) assert im.palette is not None @@ -1383,8 +1386,7 @@ def test_palette_save_all_P(tmp_path: Path) -> None: assert im.palette.palette == im.global_palette.palette im.seek(1) - assert_image_equal(im.convert("RGB"), frames[1].convert("RGB")) - assert im.palette.palette == im.global_palette.palette + assert_image_equal(im, frames[1].convert("RGB")) def test_palette_save_ImagePalette(tmp_path: Path) -> None: diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index abd829ae230..3d289c8557e 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -123,7 +123,7 @@ def _open(self) -> None: p = self.fp.read(3 << bits) if self._is_palette_needed(p): palette = ImagePalette.raw("RGB", p) - self.global_palette = self.palette = palette + self.global_palette = palette self._fp = self.fp # FIXME: hack self.__rewind = self.fp.tell() @@ -319,22 +319,20 @@ def _seek(self, frame: int, update_image: bool = True) -> None: self._frame_palette = palette if palette is not None else self.global_palette self._frame_transparency = frame_transparency if frame == 0: + self.palette = None if self._frame_palette: if LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: self._mode = "RGBA" if frame_transparency is not None else "RGB" else: self._mode = "P" - else: - self._mode = "L" + if palette: + self.palette = palette + elif self.global_palette: + from copy import copy - if palette: - self.palette = palette - elif self.global_palette: - from copy import copy - - self.palette = copy(self.global_palette) + self.palette = copy(self.global_palette) else: - self.palette = None + self._mode = "L" elif self.mode == "P" and ( LOADING_STRATEGY != LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY or palette @@ -347,6 +345,7 @@ def _seek(self, frame: int, update_image: bool = True) -> None: else: self._mode = "RGB" self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) + self.palette = None def _rgb(color: int) -> tuple[int, int, int]: if self._frame_palette: @@ -376,12 +375,12 @@ def _rgb(color: int) -> tuple[int, int, int]: dispose_mode = "P" color = self.info.get("transparency", frame_transparency) if color is not None: - if self.mode in ("RGB", "RGBA"): + if self.mode in {"RGB", "RGBA"}: dispose_mode = "RGBA" color = _rgb(color) + (0,) else: color = self.info.get("background", 0) - if self.mode in ("RGB", "RGBA"): + if self.mode in {"RGB", "RGBA"}: dispose_mode = "RGB" color = _rgb(color) self.dispose = Image.core.fill(dispose_mode, dispose_size, color) @@ -397,7 +396,7 @@ def _rgb(color: int) -> tuple[int, int, int]: Image._decompression_bomb_check(dispose_size) dispose_mode = "P" color = frame_transparency - if self.mode in ("RGB", "RGBA"): + if self.mode in {"RGB", "RGBA"}: dispose_mode = "RGBA" color = _rgb(frame_transparency) + (0,) self.dispose = Image.core.fill( @@ -412,7 +411,7 @@ def _rgb(color: int) -> tuple[int, int, int]: if frame == 0: if LOADING_STRATEGY != LoadingStrategy.RGB_ALWAYS: self.info["transparency"] = frame_transparency - elif self.mode not in ("RGB", "RGBA"): + elif self.mode not in {"RGB", "RGBA"}: transparency = frame_transparency self.tile = [ ImageFile._Tile( @@ -435,11 +434,13 @@ def load_prepare(self) -> None: temp_mode = "P" if self._frame_palette else "L" self._prev_im = None if self.__frame == 0: - if self._frame_transparency is not None: - self.im = Image.core.fill( - temp_mode, self.size, self._frame_transparency - ) - elif self.mode in ("RGB", "RGBA"): + self.im = Image.core.fill( + temp_mode, self.size, self._frame_transparency or 0 + ) + if temp_mode == "P": + assert self._frame_palette + self.im.putpalette("RGB", *self._frame_palette.getdata()) + elif self.mode in {"RGB", "RGBA"}: self._prev_im = self.im if self._frame_palette: self.im = Image.core.fill("P", self.size, self._frame_transparency or 0) @@ -495,7 +496,7 @@ def load_end(self) -> None: self.im = self._prev_im self._mode = self.im.mode - if frame_im.mode in ("LA", "RGBA"): + if frame_im.mode in {"LA", "RGBA"}: self.im.paste(frame_im, self.dispose_extent, frame_im) else: self.im.paste(frame_im, self.dispose_extent) @@ -870,7 +871,7 @@ def _get_optimize(im: Image.Image, info: dict[str, Any]) -> list[int] | None: :returns: list of indexes of palette entries in use, or None """ if ( - im.mode in ("P", "L") + im.mode in {"L", "P"} and info and info.get("optimize") and im.width != 0