mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-13 05:06:49 +03:00
Save multiple images at different bit depths if provided
This commit is contained in:
parent
268be11be6
commit
59780abd79
|
@ -85,6 +85,39 @@ def test_no_duplicates(tmp_path):
|
||||||
assert os.path.getsize(temp_file) == os.path.getsize(temp_file2)
|
assert os.path.getsize(temp_file) == os.path.getsize(temp_file2)
|
||||||
|
|
||||||
|
|
||||||
|
def test_different_bit_depths(tmp_path):
|
||||||
|
temp_file = str(tmp_path / "temp.ico")
|
||||||
|
temp_file2 = str(tmp_path / "temp2.ico")
|
||||||
|
|
||||||
|
im = hopper()
|
||||||
|
im.save(temp_file, "ico", bitmap_format="bmp", sizes=[(128, 128)])
|
||||||
|
|
||||||
|
hopper("1").save(
|
||||||
|
temp_file2,
|
||||||
|
"ico",
|
||||||
|
bitmap_format="bmp",
|
||||||
|
sizes=[(128, 128)],
|
||||||
|
append_images=[im],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert os.path.getsize(temp_file) != os.path.getsize(temp_file2)
|
||||||
|
|
||||||
|
# Test that only matching sizes of different bit depths are saved
|
||||||
|
temp_file3 = str(tmp_path / "temp3.ico")
|
||||||
|
temp_file4 = str(tmp_path / "temp4.ico")
|
||||||
|
|
||||||
|
im.save(temp_file3, "ico", bitmap_format="bmp", sizes=[(128, 128)])
|
||||||
|
im.save(
|
||||||
|
temp_file4,
|
||||||
|
"ico",
|
||||||
|
bitmap_format="bmp",
|
||||||
|
sizes=[(128, 128)],
|
||||||
|
append_images=[Image.new("P", (64, 64))],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert os.path.getsize(temp_file3) == os.path.getsize(temp_file4)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("1", "L", "P", "RGB", "RGBA"))
|
@pytest.mark.parametrize("mode", ("1", "L", "P", "RGB", "RGBA"))
|
||||||
def test_save_to_bytes_bmp(mode):
|
def test_save_to_bytes_bmp(mode):
|
||||||
output = io.BytesIO()
|
output = io.BytesIO()
|
||||||
|
|
|
@ -41,52 +41,65 @@ _MAGIC = b"\0\0\1\0"
|
||||||
|
|
||||||
def _save(im, fp, filename):
|
def _save(im, fp, filename):
|
||||||
fp.write(_MAGIC) # (2+2)
|
fp.write(_MAGIC) # (2+2)
|
||||||
|
bmp = im.encoderinfo.get("bitmap_format") == "bmp"
|
||||||
sizes = im.encoderinfo.get(
|
sizes = im.encoderinfo.get(
|
||||||
"sizes",
|
"sizes",
|
||||||
[(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)],
|
[(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)],
|
||||||
)
|
)
|
||||||
|
frames = []
|
||||||
|
provided_ims = [im] + im.encoderinfo.get("append_images", [])
|
||||||
width, height = im.size
|
width, height = im.size
|
||||||
sizes = filter(
|
for size in sorted(set(sizes)):
|
||||||
lambda x: False
|
if size[0] > width or size[1] > height or size[0] > 256 or size[1] > 256:
|
||||||
if (x[0] > width or x[1] > height or x[0] > 256 or x[1] > 256)
|
continue
|
||||||
else True,
|
|
||||||
sizes,
|
for provided_im in provided_ims:
|
||||||
)
|
if provided_im.size != size:
|
||||||
sizes = set(sizes)
|
continue
|
||||||
fp.write(o16(len(sizes))) # idCount(2)
|
frames.append(provided_im)
|
||||||
offset = fp.tell() + len(sizes) * 16
|
if bmp:
|
||||||
bmp = im.encoderinfo.get("bitmap_format") == "bmp"
|
bits = BmpImagePlugin.SAVE[provided_im.mode][1]
|
||||||
provided_images = {
|
bits_used = [bits]
|
||||||
im.size: im for im in [im] + im.encoderinfo.get("append_images", [])
|
for other_im in provided_ims:
|
||||||
}
|
if other_im.size != size:
|
||||||
for size in sizes:
|
continue
|
||||||
width, height = size
|
bits = BmpImagePlugin.SAVE[other_im.mode][1]
|
||||||
|
if bits not in bits_used:
|
||||||
|
# Another image has been supplied for this size
|
||||||
|
# with a different bit depth
|
||||||
|
frames.append(other_im)
|
||||||
|
bits_used.append(bits)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# TODO: invent a more convenient method for proportional scalings
|
||||||
|
frame = provided_im.copy()
|
||||||
|
frame.thumbnail(size, Image.Resampling.LANCZOS, reducing_gap=None)
|
||||||
|
frames.append(frame)
|
||||||
|
fp.write(o16(len(frames))) # idCount(2)
|
||||||
|
offset = fp.tell() + len(frames) * 16
|
||||||
|
for frame in frames:
|
||||||
|
width, height = frame.size
|
||||||
# 0 means 256
|
# 0 means 256
|
||||||
fp.write(o8(width if width < 256 else 0)) # bWidth(1)
|
fp.write(o8(width if width < 256 else 0)) # bWidth(1)
|
||||||
fp.write(o8(height if height < 256 else 0)) # bHeight(1)
|
fp.write(o8(height if height < 256 else 0)) # bHeight(1)
|
||||||
fp.write(b"\0") # bColorCount(1)
|
|
||||||
|
bits, colors = BmpImagePlugin.SAVE[frame.mode][1:] if bmp else (32, 0)
|
||||||
|
fp.write(o8(colors)) # bColorCount(1)
|
||||||
fp.write(b"\0") # bReserved(1)
|
fp.write(b"\0") # bReserved(1)
|
||||||
fp.write(b"\0\0") # wPlanes(2)
|
fp.write(b"\0\0") # wPlanes(2)
|
||||||
|
|
||||||
tmp = provided_images.get(size)
|
|
||||||
if not tmp:
|
|
||||||
# TODO: invent a more convenient method for proportional scalings
|
|
||||||
tmp = im.copy()
|
|
||||||
tmp.thumbnail(size, Image.Resampling.LANCZOS, reducing_gap=None)
|
|
||||||
bits = BmpImagePlugin.SAVE[tmp.mode][1] if bmp else 32
|
|
||||||
fp.write(o16(bits)) # wBitCount(2)
|
fp.write(o16(bits)) # wBitCount(2)
|
||||||
|
|
||||||
image_io = BytesIO()
|
image_io = BytesIO()
|
||||||
if bmp:
|
if bmp:
|
||||||
tmp.save(image_io, "dib")
|
frame.save(image_io, "dib")
|
||||||
|
|
||||||
if bits != 32:
|
if bits != 32:
|
||||||
and_mask = Image.new("1", tmp.size)
|
and_mask = Image.new("1", size)
|
||||||
ImageFile._save(
|
ImageFile._save(
|
||||||
and_mask, image_io, [("raw", (0, 0) + tmp.size, 0, ("1", 0, -1))]
|
and_mask, image_io, [("raw", (0, 0) + size, 0, ("1", 0, -1))]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
tmp.save(image_io, "png")
|
frame.save(image_io, "png")
|
||||||
image_io.seek(0)
|
image_io.seek(0)
|
||||||
image_bytes = image_io.read()
|
image_bytes = image_io.read()
|
||||||
if bmp:
|
if bmp:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user