Pillow icon indicating copy to clipboard operation
Pillow copied to clipboard

Alpha channel ignored in tkinter using PIL.Image.open and PIL.ImageTk.PhotoImage for PNGs in palette mode

Open BenediktO opened this issue 3 years ago • 2 comments

What did you do?

I opened a PNG image (palette mode with alpha channel) using pillow and passed it to tkinter.

What did you expect to happen?

It should work just like using tkinter.PhotoImage.

What actually happened?

The alpha channel is ignored.

What are your OS, Python and Pillow versions?

  • OS: 5.10.0-17-amd64 #1 SMP Debian 5.10.136-1 (2022-08-13) x86_64 GNU/Linux
  • Python: Python 3.9.2
  • Pillow: 9.0.1

I noticed an error when I wanted to include a PNG image in palette mode with alpha channel into a tkinter application. Since I wanted to resize it before displaying it, I used Pillow. However if I execute the code below (reproduce.zip) I see this: Bildschirmfoto 2022-09-03 18:53:25

It shows the following 3 images:

  • The first image is created using Image.open and ImageTk.PhotoImage, the transparent part is black.
  • The second image shows the same image which has been converted to RGBA mode and then saved (also with Pillow), which works.
  • The third image shows the tkinter.PhotoImage version which also works fine.

Uncommenting the explicit conversion to RGBA also fixes the problem, which is my current workaround. However I think this should not be necessary.

from tkinter import Tk, Label, PhotoImage

from PIL import Image, ImageTk


root = Tk()
# via Pillow
pil_image = Image.open('loading.png')
print(pil_image.mode)
#pil_image = pil_image.convert('RGBA')
image1 = ImageTk.PhotoImage(pil_image)
Label(root, image=image1).grid(row=0, column=0, padx=5, pady=5)

# using RGBA mode
pil_image_rgba = Image.open('loading_RGBA.png')
print(pil_image_rgba.mode)
image2 = ImageTk.PhotoImage(pil_image_rgba)
Label(root, image=image2).grid(row=0, column=1, padx=5, pady=5)

# using tkinter.PhotoImage
image3 = PhotoImage(file='loading.png')
Label(root, image=image3).grid(row=0, column=2, padx=5, pady=5)
root.mainloop()

BenediktO avatar Sep 03 '22 17:09 BenediktO

Looking at ImageTk.PhotoImage, if it is passed an image with a palette, it automatically converts the image to the mode of the palette before passing it to tkinter: https://github.com/python-pillow/Pillow/blob/b6348d9b2d14d17c28da769d0483b8d486274efb/src/PIL/ImageTk.py#L108-L112

However, your first image is loaded with a palette with mode RGB, not RGBA as you might expect. This was discussed in #6348, and #6352 added a new function image.apply_transparency() to convert the palette of PNG images from RGB to RGBA in Pillow 9.2.0.

from tkinter import Tk, Label, PhotoImage

from PIL import Image, ImageTk


root = Tk()
pil_image = Image.open('loading.png')
print(pil_image.mode, pil_image.palette.mode)  # P RGB
pil_image.apply_transparency()
print(pil_image.mode, pil_image.palette.mode)  # P RGBA
image1 = ImageTk.PhotoImage(pil_image)
Label(root, image=image1).grid(row=0, column=0, padx=5, pady=5)

image

nulano avatar Sep 04 '22 20:09 nulano

I've created PR #6559 to resolve this by automatically applying apply_transparency() in ImageTk.PhotoImage.

radarhere avatar Sep 05 '22 03:09 radarhere