mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-02-05 06:00:58 +03:00
commit
5c11e27a97
|
@ -253,7 +253,8 @@ def _save(im, fp, filename):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
palette = None
|
palette = None
|
||||||
|
|
||||||
for s in getheader(imOut, palette, im.encoderinfo):
|
header, usedPaletteColors = getheader(imOut, palette, im.encoderinfo)
|
||||||
|
for s in header:
|
||||||
fp.write(s)
|
fp.write(s)
|
||||||
|
|
||||||
flags = 0
|
flags = 0
|
||||||
|
@ -275,13 +276,27 @@ def _save(im, fp, filename):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
transparency = int(transparency)
|
||||||
|
# optimize the block away if transparent color is not used
|
||||||
|
transparentColorExists = True
|
||||||
|
# adjust the transparency index after optimize
|
||||||
|
if usedPaletteColors is not None and len(usedPaletteColors) < 256:
|
||||||
|
for i in range(len(usedPaletteColors)):
|
||||||
|
if usedPaletteColors[i] == transparency:
|
||||||
|
transparency = i
|
||||||
|
transparentColorExists = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
transparentColorExists = False
|
||||||
|
|
||||||
# transparency extension block
|
# transparency extension block
|
||||||
|
if transparentColorExists:
|
||||||
fp.write(b"!" +
|
fp.write(b"!" +
|
||||||
o8(249) + # extension intro
|
o8(249) + # extension intro
|
||||||
o8(4) + # length
|
o8(4) + # length
|
||||||
o8(1) + # transparency info present
|
o8(1) + # transparency info present
|
||||||
o16(0) + # duration
|
o16(0) + # duration
|
||||||
o8(int(transparency)) # transparency index
|
o8(transparency) # transparency index
|
||||||
+ o8(0))
|
+ o8(0))
|
||||||
|
|
||||||
# local image header
|
# local image header
|
||||||
|
@ -293,7 +308,6 @@ def _save(im, fp, filename):
|
||||||
o8(8)) # bits
|
o8(8)) # bits
|
||||||
|
|
||||||
imOut.encoderconfig = (8, interlace)
|
imOut.encoderconfig = (8, interlace)
|
||||||
|
|
||||||
ImageFile._save(imOut, fp, [("gif", (0,0)+im.size, 0, rawmode)])
|
ImageFile._save(imOut, fp, [("gif", (0,0)+im.size, 0, rawmode)])
|
||||||
|
|
||||||
fp.write(b"\0") # end of image data
|
fp.write(b"\0") # end of image data
|
||||||
|
@ -304,6 +318,7 @@ def _save(im, fp, filename):
|
||||||
fp.flush()
|
fp.flush()
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
|
|
||||||
def _save_netpbm(im, fp, filename):
|
def _save_netpbm(im, fp, filename):
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -329,42 +344,81 @@ def getheader(im, palette=None, info=None):
|
||||||
|
|
||||||
optimize = info and info.get("optimize", 0)
|
optimize = info and info.get("optimize", 0)
|
||||||
|
|
||||||
s = [
|
# start of header
|
||||||
|
header = [
|
||||||
b"GIF87a" + # magic
|
b"GIF87a" + # magic
|
||||||
o16(im.size[0]) + # size
|
o16(im.size[0]) + # size
|
||||||
o16(im.size[1]) +
|
o16(im.size[1])
|
||||||
o8(7 + 128) + # flags: bits + palette
|
|
||||||
o8(0) + # background
|
|
||||||
o8(0) # reserved/aspect
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# if the user adds a palette, use it
|
||||||
|
usedPaletteColors = None
|
||||||
|
|
||||||
|
if palette is not None and isinstance(palette, bytes):
|
||||||
|
paletteBytes = palette[:768]
|
||||||
|
else:
|
||||||
|
usedPaletteColors = []
|
||||||
|
|
||||||
if optimize:
|
if optimize:
|
||||||
# minimize color palette
|
# minimize color palette if wanted
|
||||||
i = 0
|
i = 0
|
||||||
maxcolor = 0
|
|
||||||
for count in im.histogram():
|
for count in im.histogram():
|
||||||
if count:
|
if count:
|
||||||
maxcolor = i
|
usedPaletteColors.append(i)
|
||||||
i = i + 1
|
i += 1
|
||||||
else:
|
|
||||||
maxcolor = 256
|
|
||||||
|
|
||||||
# global palette
|
countUsedPaletteColors = len(usedPaletteColors)
|
||||||
|
|
||||||
|
# create the global palette
|
||||||
if im.mode == "P":
|
if im.mode == "P":
|
||||||
# colour palette
|
# colour palette
|
||||||
if palette is not None and isinstance(palette, bytes):
|
if countUsedPaletteColors > 0 and countUsedPaletteColors < 256:
|
||||||
paletteBytes = palette
|
paletteBytes = b"";
|
||||||
else:
|
# pick only the used colors from the palette
|
||||||
paletteBytes =im.im.getpalette("RGB")[:maxcolor*3]
|
for i in usedPaletteColors:
|
||||||
|
paletteBytes += im.im.getpalette("RGB")[i*3:i*3+3]
|
||||||
s.append(paletteBytes)
|
else :
|
||||||
|
paletteBytes = im.im.getpalette("RGB")[:768]
|
||||||
else:
|
else:
|
||||||
# greyscale
|
# greyscale
|
||||||
for i in range(maxcolor):
|
if countUsedPaletteColors > 0 and countUsedPaletteColors < 256:
|
||||||
s.append(o8(i) * 3)
|
paletteBytes = b"";
|
||||||
|
# add only the used grayscales to the palette
|
||||||
|
for i in usedPaletteColors:
|
||||||
|
paletteBytes += o8(i)*3
|
||||||
|
else :
|
||||||
|
paletteBytes = bytearray([i//3 for i in range(768)])
|
||||||
|
|
||||||
|
# TODO improve this, maybe add numpy support
|
||||||
|
# replace the palette color id of all pixel with the new id
|
||||||
|
if countUsedPaletteColors > 0 and countUsedPaletteColors < 256:
|
||||||
|
imageBytes = bytearray(im.tobytes())
|
||||||
|
for i in range(len(imageBytes)):
|
||||||
|
for newI in range(countUsedPaletteColors):
|
||||||
|
if imageBytes[i] == usedPaletteColors[newI]:
|
||||||
|
imageBytes[i] = newI
|
||||||
|
break
|
||||||
|
|
||||||
|
im.frombytes(bytes(imageBytes))
|
||||||
|
|
||||||
|
# calculate the palette size for the header
|
||||||
|
import math
|
||||||
|
colorTableSize = int(math.ceil(math.log(len(paletteBytes)//3, 2)))-1
|
||||||
|
if colorTableSize < 0: colorTableSize = 0
|
||||||
|
header.append(o8(colorTableSize + 128)) # size of global color table + global color table flag
|
||||||
|
header.append(o8(0) + o8(0)) # background + reserved/aspect
|
||||||
|
# end of screen descriptor header
|
||||||
|
|
||||||
|
# add the missing amount of bytes
|
||||||
|
# the palette has to be 2<<n in size
|
||||||
|
actualTargetSizeDiff = (2<<colorTableSize) - len(paletteBytes)//3
|
||||||
|
if actualTargetSizeDiff > 0:
|
||||||
|
paletteBytes += o8(0) * 3 * actualTargetSizeDiff
|
||||||
|
|
||||||
|
# global color palette
|
||||||
|
header.append(paletteBytes)
|
||||||
|
return header, usedPaletteColors
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
def getdata(im, offset = (0, 0), **params):
|
def getdata(im, offset = (0, 0), **params):
|
||||||
"""Return a list of strings representing this image.
|
"""Return a list of strings representing this image.
|
||||||
|
|
|
@ -9,7 +9,8 @@ if "gif_encoder" not in codecs or "gif_decoder" not in codecs:
|
||||||
|
|
||||||
# sample gif stream
|
# sample gif stream
|
||||||
file = "Images/lena.gif"
|
file = "Images/lena.gif"
|
||||||
data = open(file, "rb").read()
|
with open(file, "rb") as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
def test_sanity():
|
def test_sanity():
|
||||||
im = Image.open(file)
|
im = Image.open(file)
|
||||||
|
@ -25,4 +26,4 @@ def test_optimize():
|
||||||
im.save(file, "GIF", optimize=optimize)
|
im.save(file, "GIF", optimize=optimize)
|
||||||
return len(file.getvalue())
|
return len(file.getvalue())
|
||||||
assert_equal(test(0), 800)
|
assert_equal(test(0), 800)
|
||||||
assert_equal(test(1), 32)
|
assert_equal(test(1), 38)
|
||||||
|
|
5
setup.py
5
setup.py
|
@ -285,10 +285,9 @@ class pil_build_ext(build_ext):
|
||||||
elif _find_library_file(self, "tk" + TCL_VERSION):
|
elif _find_library_file(self, "tk" + TCL_VERSION):
|
||||||
feature.tk = "tk" + TCL_VERSION
|
feature.tk = "tk" + TCL_VERSION
|
||||||
|
|
||||||
if (
|
if (_find_include_file(self, "webp/encode.h") and
|
||||||
_find_include_file(self, "webp/encode.h") and
|
|
||||||
_find_include_file(self, "webp/decode.h")):
|
_find_include_file(self, "webp/decode.h")):
|
||||||
if _find_library_file(self, "webp"):
|
if _find_library_file(self, "webp"): # in googles precompiled zip it is call "libwebp"
|
||||||
feature.webp = "webp"
|
feature.webp = "webp"
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
Loading…
Reference in New Issue
Block a user