Refactor get_palette to do one thing, and not mutate the image in place

This commit is contained in:
wiredfool 2017-02-23 06:14:04 -08:00 committed by Andrew Murray
parent 1e9afb3ecb
commit cee397ab00

View File

@ -312,6 +312,8 @@ def _write_single_frame(im, fp, palette):
else: else:
im_out = _convert_mode(im, True) im_out = _convert_mode(im, True)
im_out = _normalize_palette(im_out, palette, im.encoderinfo)
for s in _get_global_header(im_out, palette, im.encoderinfo): for s in _get_global_header(im_out, palette, im.encoderinfo):
fp.write(s) fp.write(s)
@ -338,6 +340,7 @@ def _write_multiple_frames(im, fp, palette):
for imSequence in [im]+im.encoderinfo.get("append_images", []): for imSequence in [im]+im.encoderinfo.get("append_images", []):
for im_frame in ImageSequence.Iterator(imSequence): for im_frame in ImageSequence.Iterator(imSequence):
im_frame = _convert_mode(im_frame) im_frame = _convert_mode(im_frame)
im_frame = _normalize_palette(im_frame, palette, encoderinfo)
encoderinfo = im.encoderinfo.copy() encoderinfo = im.encoderinfo.copy()
if isinstance(duration, (list, tuple)): if isinstance(duration, (list, tuple)):
@ -347,8 +350,12 @@ def _write_multiple_frames(im, fp, palette):
if im_frames: if im_frames:
# delta frame # delta frame
previous = im_frames[-1] previous = im_frames[-1]
delta = ImageChops.subtract_modulo(im_frame, if _get_palette_bytes(im_frame) == _get_palette_bytes(previous['im']):
previous['im']) delta = ImageChops.subtract_modulo(im_frame,
previous['im'])
else:
delta = ImageChops.subtract_modulo(im_frame.convert('RGB'),
previous['im'].convert('RGB'))
bbox = delta.getbbox() bbox = delta.getbbox()
if not bbox: if not bbox:
# This frame is identical to the previous frame # This frame is identical to the previous frame
@ -362,6 +369,7 @@ def _write_multiple_frames(im, fp, palette):
'bbox':bbox, 'bbox':bbox,
'encoderinfo':encoderinfo 'encoderinfo':encoderinfo
}) })
if len(im_frames) > 1: if len(im_frames) > 1:
for frame_data in im_frames: for frame_data in im_frames:
im_frame = frame_data['im'] im_frame = frame_data['im']
@ -588,16 +596,17 @@ def _get_header_palette(palette_bytes):
palette_bytes += o8(0) * 3 * actual_target_size_diff palette_bytes += o8(0) * 3 * actual_target_size_diff
return palette_bytes return palette_bytes
def _get_palette_bytes(im, palette, info): def _normalize_palette(im, palette, info):
""" """
Gets the palette for inclusion in the gif header, if optimization is Normalizes the palette for image.
requested or required, the palette is rewritten and the image is - Sets the palette to the incoming palette, if provided.
mutatated in place. - Ensures that there's a palette for L mode images
- Optimizes the palette if necessary/desired.
:param im: Image object :param im: Image object
:param palette: bytes object containing the source palette, or .... :param palette: bytes object containing the source palette, or ....
:param info: encoderinfo :param info: encoderinfo
:returns: Bytes, len<=768 suitable for inclusion in gif header :returns: Image object
""" """
if im.mode == "P": if im.mode == "P":
if palette and isinstance(palette, bytes): if palette and isinstance(palette, bytes):
@ -609,23 +618,24 @@ def _get_palette_bytes(im, palette, info):
source_palette = palette[:768] source_palette = palette[:768]
else: else:
source_palette = bytearray(i//3 for i in range(768)) source_palette = bytearray(i//3 for i in range(768))
im.palette = ImagePalette.ImagePalette("RGB",
palette_bytes = None palette=source_palette)
used_palette_colors = _get_optimize(im, info) used_palette_colors = _get_optimize(im, info)
if used_palette_colors is not None: if used_palette_colors is not None:
m_im = im.remap_palette(used_palette_colors, source_palette) return im.remap_palette(used_palette_colors, source_palette)
palette_bytes = m_im.palette.palette
# oh gawd, this is modifying the image in place so I can pass by ref. im.palette.palette = source_palette
# REFACTOR SOONEST return im
im.im = m_im.im
im.palette = m_im.palette
else:
palette_bytes = source_palette
# returning palette, _not_ padded to 768 bytes like our internal ones. def _get_palette_bytes(im, *args):
return palette_bytes """
Gets the palette for inclusion in the gif header
:param im: Image object
:returns: Bytes, len<=768 suitable for inclusion in gif header
"""
return im.palette.palette
def _get_global_header(im, palette, info): def _get_global_header(im, palette, info):
"""Return a list of strings representing a GIF header""" """Return a list of strings representing a GIF header"""
@ -671,6 +681,7 @@ def getheader(im, palette=None, info=[]):
if not "background" in info and "background" in im.info: if not "background" in info and "background" in im.info:
info["background"] = im.info["background"] info["background"] = im.info["background"]
im = _normalize_palette(im, palette, info)
header = _get_global_header(im, palette, info) header = _get_global_header(im, palette, info)
return header, used_palette_colors return header, used_palette_colors