Forum Archive

Typicon image files have all-or-nothing transparency when loaded

0942v8653

When I load a Typicon file, e.g. the house picture, all the partial transparency (antialiasing) is removed:

bad house console

This is how it looks once I'm done with it:

bad house

and this is how it should look:

good house

Here is my code:

from PIL import Image as ImageP
import io
import base64
import clipboard
import ui

appsize = (120, 120)
typsize = (96, 96)
offset = [(appsize[i] - typsize[i])/2 for i in range(2)]

master = ImageP.new('RGBA', (120, 120), (50, 50, 50, 255))
home = ImageP.open("Typicons96_Home")
home.show()  # it is broken here, even before converting.

home = home.convert('RGBA')

r, g, b, a = home.split()
home = ImageP.merge("RGB", (r, g, b))
mask = ImageP.merge("L", (a,))
master.paste(home, tuple(offset), mask)

with io.BytesIO() as bIO:
    master.save(bIO, 'PNG')
    img = ui.Image.from_data(bIO.getvalue())
    bytes = img.to_png()
    clipboard.set(base64.b64encode(bytes))

Curiously, this seems to happen only with the Typicon files and nothing else.

0942v8653

Fixed it. I just load it as a ui.Image first and then convert it to a PIL image. At the moment I'm using the clipboard, which is a pretty hacky solution so I'm open to ideas….

omz

You can convert from a ui.Image to a PIL Image without using the clipboard like this:

import ui
import Image
from io import BytesIO

ui_img = ui.Image.named('Typicon96_Home')
data = BytesIO(ui_img.to_png())
img = Image.open(data)

img.show()
0942v8653

Thank you! I was using that to convert the other way around, I'm surprised I didn't think of it earlier.

My new code:

from PIL import Image as PILImage
from ui import Image as UIImage
import io

appsize = (120, 120)
typsize = (96, 96)
offset = [(appsize[i] - typsize[i])/2
          for i in range(2)]

def makegradient(c1, c2, size):
    img = PILImage.new('RGB', size, c1)
    d = tuple(c2[i]-c1[i] for i in range(3))
    pixels = img.load()
    h = appsize[1]
    for i in range(h):
        c = tuple(c1[a] + d[a]*i/h for a in range(3))
        for j in range(appsize[0]):
            pixels[j, i] = c
    return img

def composite(top, bottom, offset):
    bottom = bottom.copy()
    top = top.convert('RGBA')
    r, g, b, a = top.split()
    top = PILImage.merge("RGB", (r, g, b))
    mask = PILImage.merge("L", (a,))
    bottom.paste(top, tuple(offset), mask)
    return bottom

def makeicon(c1, c2, name):
    gradient = makegradient(c1, c2, appsize)

    # hack to support partial transparency
    uii = UIImage.named(name)
    data = io.BytesIO(uii.to_png())
    top = PILImage.open(data)

    icon = composite(top, gradient, offset)
    data.close()

    with io.BytesIO() as bIO:
        icon.save(bIO, 'PNG')
        img = UIImage.from_data(bIO.getvalue())
    return img