From 99e401123bab56bf9c64314b506750a4ea6a9e79 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 13 Aug 2022 19:46:07 +1000 Subject: [PATCH 1/6] Corrected palette size when saving --- Tests/test_file_tga.py | 12 ++++++++++++ src/PIL/TgaImagePlugin.py | 5 +++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index 0c8c9f304..fff127421 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -123,6 +123,18 @@ def test_save(tmp_path): assert test_im.size == (100, 100) +def test_small_palette(tmp_path): + im = Image.new("P", (1, 1)) + colors = [0, 0, 0] + im.putpalette(colors) + + out = str(tmp_path / "temp.tga") + im.save(out) + + with Image.open(out) as reloaded: + assert reloaded.getpalette() == colors + + def test_save_wrong_mode(tmp_path): im = hopper("PA") out = str(tmp_path / "temp.tga") diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index 59b89e988..7f5075317 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -193,7 +193,8 @@ def _save(im, fp, filename): warnings.warn("id_section has been trimmed to 255 characters") if colormaptype: - colormapfirst, colormaplength, colormapentry = 0, 256, 24 + palette = im.im.getpalette("RGB", "BGR") + colormapfirst, colormaplength, colormapentry = 0, len(palette) // 3, 24 else: colormapfirst, colormaplength, colormapentry = 0, 0, 0 @@ -225,7 +226,7 @@ def _save(im, fp, filename): fp.write(id_section) if colormaptype: - fp.write(im.im.getpalette("RGB", "BGR")) + fp.write(palette) if rle: ImageFile._save( From 5d4fbdfab4fa5dc05f4b3de3304fffdeddb9ff4f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 13 Aug 2022 19:46:46 +1000 Subject: [PATCH 2/6] Simplified code --- src/PIL/TgaImagePlugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index 7f5075317..cd454b755 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -194,9 +194,9 @@ def _save(im, fp, filename): if colormaptype: palette = im.im.getpalette("RGB", "BGR") - colormapfirst, colormaplength, colormapentry = 0, len(palette) // 3, 24 + colormaplength, colormapentry = len(palette) // 3, 24 else: - colormapfirst, colormaplength, colormapentry = 0, 0, 0 + colormaplength, colormapentry = 0, 0 if im.mode in ("LA", "RGBA"): flags = 8 @@ -211,7 +211,7 @@ def _save(im, fp, filename): o8(id_len) + o8(colormaptype) + o8(imagetype) - + o16(colormapfirst) + + o16(0) # colormapfirst + o16(colormaplength) + o8(colormapentry) + o16(0) From 8b2d70d17a4791d68df6c10d8337d769290c6528 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 13 Sep 2022 09:10:03 +1000 Subject: [PATCH 3/6] Corrected BMP palette size when saving --- Tests/test_file_bmp.py | 12 ++++++++++++ src/PIL/BmpImagePlugin.py | 20 ++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index d58666b44..4c964fbea 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -51,6 +51,18 @@ def test_save_to_bytes(): assert reloaded.format == "BMP" +def test_small_palette(tmp_path): + im = Image.new("P", (1, 1)) + colors = [0, 0, 0, 125, 125, 125, 255, 255, 255] + im.putpalette(colors) + + out = str(tmp_path / "temp.bmp") + im.save(out) + + with Image.open(out) as reloaded: + assert reloaded.getpalette() == colors + + def test_save_too_large(tmp_path): outfile = str(tmp_path / "temp.bmp") with Image.new("RGB", (1, 1)) as im: diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 7bb73fc93..1041ab763 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -375,6 +375,16 @@ def _save(im, fp, filename, bitmap_header=True): header = 40 # or 64 for OS/2 version 2 image = stride * im.size[1] + if im.mode == "1": + palette = b"".join(o8(i) * 4 for i in (0, 255)) + elif im.mode == "L": + palette = b"".join(o8(i) * 4 for i in range(256)) + elif im.mode == "P": + palette = im.im.getpalette("RGB", "BGRX") + colors = len(palette) // 4 + else: + palette = None + # bitmap header if bitmap_header: offset = 14 + header + colors * 4 @@ -405,14 +415,8 @@ def _save(im, fp, filename, bitmap_header=True): fp.write(b"\0" * (header - 40)) # padding (for OS/2 format) - if im.mode == "1": - for i in (0, 255): - fp.write(o8(i) * 4) - elif im.mode == "L": - for i in range(256): - fp.write(o8(i) * 4) - elif im.mode == "P": - fp.write(im.im.getpalette("RGB", "BGRX")) + if palette: + fp.write(palette) ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))]) From 964e0aa0790a7d3d9dadb03b3045de6c7e124a6e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 16 Sep 2022 23:32:58 +1000 Subject: [PATCH 4/6] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1d4103a7c..8c2993abc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Corrected BMP and TGA palette size when saving #6500 + [radarhere] + - Do not call load() before draft() in Image.thumbnail #6539 [radarhere] From 8b90588b9712f8105618504026646847c8814e75 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 17 Sep 2022 16:03:23 +1000 Subject: [PATCH 5/6] Updated harfbuzz to 5.2.0 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 94e5dd871..f4515468f 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -281,9 +281,9 @@ deps = { "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/5.1.0.zip", - "filename": "harfbuzz-5.1.0.zip", - "dir": "harfbuzz-5.1.0", + "url": "https://github.com/harfbuzz/harfbuzz/archive/5.2.0.zip", + "filename": "harfbuzz-5.2.0.zip", + "dir": "harfbuzz-5.2.0", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From 0cafaca7e8c5845272a2994f025aca201b479556 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 17 Sep 2022 18:52:16 +1000 Subject: [PATCH 6/6] Corrected dictionary name --- docs/reference/ExifTags.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/ExifTags.rst b/docs/reference/ExifTags.rst index 4567d4d3e..794fa238f 100644 --- a/docs/reference/ExifTags.rst +++ b/docs/reference/ExifTags.rst @@ -10,7 +10,7 @@ provide constants and clear-text names for various well-known EXIF tags. .. py:data:: TAGS :type: dict - The TAG dictionary maps 16-bit integer EXIF tag enumerations to + The TAGS dictionary maps 16-bit integer EXIF tag enumerations to descriptive string names. For instance: >>> from PIL.ExifTags import TAGS