Skip to content

Commit 84a6d2b

Browse files
committed
Skip truncated mask if LOAD_TRUNCATED_IMAGES is enabled
1 parent 9b4fae7 commit 84a6d2b

File tree

2 files changed

+56
-17
lines changed

2 files changed

+56
-17
lines changed

Tests/test_file_ico.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import pytest
88

9-
from PIL import IcoImagePlugin, Image, ImageDraw
9+
from PIL import IcoImagePlugin, Image, ImageDraw, ImageFile
1010

1111
from .helper import assert_image_equal, assert_image_equal_tofile, hopper
1212

@@ -241,3 +241,29 @@ def test_draw_reloaded(tmp_path: Path) -> None:
241241

242242
with Image.open(outfile) as im:
243243
assert_image_equal_tofile(im, "Tests/images/hopper_draw.ico")
244+
245+
246+
def test_truncated_mask() -> None:
247+
# 1 bpp
248+
with open("Tests/images/hopper_mask.ico", "rb") as fp:
249+
data = fp.read()
250+
251+
ImageFile.LOAD_TRUNCATED_IMAGES = True
252+
data = data[:-3]
253+
254+
try:
255+
with Image.open(io.BytesIO(data)) as im:
256+
with Image.open("Tests/images/hopper_mask.png") as expected:
257+
assert im.mode == "1"
258+
259+
# 32 bpp
260+
output = io.BytesIO()
261+
expected = hopper("RGBA")
262+
expected.save(output, "ico", bitmap_format="bmp")
263+
264+
data = output.getvalue()[:-1]
265+
266+
with Image.open(io.BytesIO(data)) as im:
267+
assert im.mode == "RGB"
268+
finally:
269+
ImageFile.LOAD_TRUNCATED_IMAGES = False

src/PIL/IcoImagePlugin.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,19 @@ def frame(self, idx: int) -> Image.Image:
235235
alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]
236236

237237
# convert to an 8bpp grayscale image
238-
mask = Image.frombuffer(
239-
"L", # 8bpp
240-
im.size, # (w, h)
241-
alpha_bytes, # source chars
242-
"raw", # raw decoder
243-
("L", 0, -1), # 8bpp inverted, unpadded, reversed
244-
)
238+
try:
239+
mask = Image.frombuffer(
240+
"L", # 8bpp
241+
im.size, # (w, h)
242+
alpha_bytes, # source chars
243+
"raw", # raw decoder
244+
("L", 0, -1), # 8bpp inverted, unpadded, reversed
245+
)
246+
except ValueError:
247+
if ImageFile.LOAD_TRUNCATED_IMAGES:
248+
mask = None
249+
else:
250+
raise
245251
else:
246252
# get AND image from end of bitmap
247253
w = im.size[0]
@@ -259,19 +265,26 @@ def frame(self, idx: int) -> Image.Image:
259265
mask_data = self.buf.read(total_bytes)
260266

261267
# convert raw data to image
262-
mask = Image.frombuffer(
263-
"1", # 1 bpp
264-
im.size, # (w, h)
265-
mask_data, # source chars
266-
"raw", # raw decoder
267-
("1;I", int(w / 8), -1), # 1bpp inverted, padded, reversed
268-
)
268+
try:
269+
mask = Image.frombuffer(
270+
"1", # 1 bpp
271+
im.size, # (w, h)
272+
mask_data, # source chars
273+
"raw", # raw decoder
274+
("1;I", int(w / 8), -1), # 1bpp inverted, padded, reversed
275+
)
276+
except ValueError:
277+
if ImageFile.LOAD_TRUNCATED_IMAGES:
278+
mask = None
279+
else:
280+
raise
269281

270282
# now we have two images, im is XOR image and mask is AND image
271283

272284
# apply mask image as alpha channel
273-
im = im.convert("RGBA")
274-
im.putalpha(mask)
285+
if mask:
286+
im = im.convert("RGBA")
287+
im.putalpha(mask)
275288

276289
return im
277290

0 commit comments

Comments
 (0)