Skip to content

Commit c2feaa2

Browse files
committed
fixed saving not a current frame when saving from Pillow.
1 parent 35982b2 commit c2feaa2

File tree

3 files changed

+50
-42
lines changed

3 files changed

+50
-42
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ All notable changes to this project will be documented in this file.
2121

2222
### Fixed
2323

24-
- Minor usage fixes.
25-
- Speed optimizations.
24+
- (HeifImagePlugin) - `save` bug, when first frame was saved instead of current.
25+
- Minor usage fixes and optimizations.
2626

2727
## [0.2.5 - 2022-05-30]
2828

pillow_heif/heif.py

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -542,48 +542,53 @@ def add_from_pillow(self, pil_image: Image.Image, load_one=False, ignore_primary
542542
:param load_one: should be only one frame loaded. Default=``False``
543543
:param ignore_primary: force ``PrimaryImage=False`` flag to all added images."""
544544

545-
for frame in ImageSequence.Iterator(pil_image):
546-
if frame.width > 0 and frame.height > 0:
547-
additional_info = {}
548-
supported_info_keys = (
549-
"exif",
550-
"xmp",
551-
"metadata",
552-
"primary",
553-
"icc_profile",
554-
"icc_profile_type",
555-
"nclx_profile",
556-
)
557-
for k in supported_info_keys:
558-
if k in frame.info:
559-
additional_info[k] = frame.info[k]
560-
if ignore_primary:
561-
additional_info["primary"] = False
562-
if "xmp" not in additional_info and "XML:com.adobe.xmp" in frame.info:
563-
additional_info["xmp"] = frame.info["XML:com.adobe.xmp"]
564-
if "xmp" in additional_info and isinstance(additional_info["xmp"], str):
565-
additional_info["xmp"] = additional_info["xmp"].encode("utf-8")
566-
original_orientation = set_orientation(additional_info)
567-
if frame.mode == "P":
568-
mode = "RGBA" if frame.info.get("transparency") else "RGB"
569-
frame = frame.convert(mode=mode)
570-
elif frame.mode == "LA":
571-
frame = frame.convert(mode="RGBA")
572-
elif frame.mode == "L":
573-
frame = frame.convert(mode="RGB")
574-
575-
if original_orientation is not None and original_orientation != 1:
576-
frame = ImageOps.exif_transpose(frame)
577-
# check image.bits / pallete.rawmode to detect > 8 bit or maybe something else?
578-
_bit_depth = 8
579-
added_image = self._add_frombytes(
580-
_bit_depth, frame.mode, frame.size, frame.tobytes(), add_info={**additional_info}
581-
)
582-
added_image.copy_thumbnails(frame.info.get("thumbnails", []), **kwargs)
583-
if load_one:
584-
break
545+
if load_one:
546+
self.__add_frame_from_pillow(pil_image, ignore_primary, **kwargs)
547+
else:
548+
for frame in ImageSequence.Iterator(pil_image):
549+
self.__add_frame_from_pillow(frame, ignore_primary, **kwargs)
585550
return self
586551

552+
def __add_frame_from_pillow(self, frame: Image.Image, ignore_primary: bool, **kwargs) -> None:
553+
if frame.width <= 0 or frame.height <= 0:
554+
return
555+
additional_info = {}
556+
supported_info_keys = (
557+
"exif",
558+
"xmp",
559+
"metadata",
560+
"primary",
561+
"icc_profile",
562+
"icc_profile_type",
563+
"nclx_profile",
564+
)
565+
for k in supported_info_keys:
566+
if k in frame.info:
567+
additional_info[k] = frame.info[k]
568+
if ignore_primary:
569+
additional_info["primary"] = False
570+
if "xmp" not in additional_info and "XML:com.adobe.xmp" in frame.info:
571+
additional_info["xmp"] = frame.info["XML:com.adobe.xmp"]
572+
if "xmp" in additional_info and isinstance(additional_info["xmp"], str):
573+
additional_info["xmp"] = additional_info["xmp"].encode("utf-8")
574+
original_orientation = set_orientation(additional_info)
575+
if frame.mode == "P":
576+
mode = "RGBA" if frame.info.get("transparency") else "RGB"
577+
frame = frame.convert(mode=mode)
578+
elif frame.mode == "LA":
579+
frame = frame.convert(mode="RGBA")
580+
elif frame.mode == "L":
581+
frame = frame.convert(mode="RGB")
582+
583+
if original_orientation is not None and original_orientation != 1:
584+
frame = ImageOps.exif_transpose(frame)
585+
# check image.bits / pallete.rawmode to detect > 8 bit or maybe something else?
586+
_bit_depth = 8
587+
added_image = self._add_frombytes(
588+
_bit_depth, frame.mode, frame.size, frame.tobytes(), add_info={**additional_info}
589+
)
590+
added_image.copy_thumbnails(frame.info.get("thumbnails", []), **kwargs)
591+
587592
def add_from_heif(self, heif_image, load_one=False, ignore_primary=True, **kwargs):
588593
"""Add image(s) to container.
589594

tests/opener_encoder_test.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ def test_gif():
7979
out_heic = BytesIO()
8080
gif_pillow.save(out_heic, format="HEIF")
8181
imagehash.compare_hashes([gif_pillow, out_heic], hash_type="dhash")
82+
# save second gif frame
83+
ImageSequence.Iterator(gif_pillow)[1].save(out_heic, format="HEIF")
84+
imagehash.compare_hashes([gif_pillow, out_heic], hash_type="dhash")
8285
# convert all frames of gif(pillow_heif does not skip identical frames and saves all frames like in source)
8386
out_all_heic = BytesIO()
8487
gif_pillow.save(out_all_heic, format="HEIF", save_all=True, quality=80)

0 commit comments

Comments
 (0)