pimoroni / inky

Combined library for V2/V3 Inky pHAT and Inky wHAT.
https://shop.pimoroni.com/?q=inky
MIT License
594 stars 122 forks source link

inky impression swaps white and black #85

Closed bablokb closed 1 year ago

bablokb commented 3 years ago

I recently wanted to migrate my application from Inky-wHat to Inky-Impression and noticed that you swapped the color-codes for white and black. In a pure python-world, that is ok because you can use symbolic constants. But I have a json-file to configure fonts and colors and this really hurts.

Very strange since all versions up to the impression use 0 for white and 1 for black. Any reason for this incompatibility?

Gadgetoid commented 3 years ago

These are the real-world numbers that represent white/black on this particular e-ink display. Prior displays had a weirder, separate bit-packed buffer for white/black white/yellow (or red) so the mapping of number to colour was comparatively arbitary. Here I've opted to stay close to the metal, so to speak, and use the 4-bit values that actually get sent to the display.

The underlying buffer is just a numpy array, though, so you could replace colour values out at runtime if you needed.

bablokb commented 3 years ago

I will add a mapping dict to my code (e.g.

{ "black": display.BLACK, ... }

that will solve the issue but will change the format of the json causing some incompatibilities, but users will have to live with it (I don't think there are many users yet, ins a fairly new project).

Dhertz commented 3 years ago

@Gadgetoid I am also having a similar problem converting code from the wHat - except I paste images from disk into a new PIL image that is black and white:

image = Image.new('P', (width, height))
icon = Image.open('icon.png')
image.paste(icon, (0,  0))

This code results in the correct image (as seen with an image editor) displayed on a wHat, but an inverted image on an Impression.

bablokb commented 3 years ago

You could try to setup your image as RGB for the impression. The impression-code will do the mapping to a palette image automatically hopefully preserving black&white. I use the colour-attribute of the display-object (returned by auto()) to distinguish the hardware (the impression will have the value multi).

Gadgetoid commented 3 years ago

For the black/white swap you could run a pass after setting your image to swap Black and White in Inky's buffer, like so:

black = display.buf == display.BLACK
white = display.buf == display.WHITE
display.buf[black] = display.WHITE
display.buf[white] = display.BLACK

If this works, I'll add a display.swap method for Impressions, which you can pass any two colours into, ie:

display.swap(display.BLACK, display.WHITE)
Dhertz commented 3 years ago

@Gadgetoid this is what I ended up using:

    def load_image(path):
        i = Image.open(path)
        if display.colour == 'multi':
            data = np.array(i)
            black = data == display.BLACK
            white = data == display.WHITE
            green = data == display.GREEN
            data[black], data[white], data[green] = display.WHITE, display.BLACK, display.YELLOW
            i = Image.fromarray(data)
        return i   
llego commented 3 years ago

It seems to me that this is still broken. A PNG that I try to paste in has inverted black and white. The text in the example below, however, has the correct color...

img = Image.new("P", (inky_display.WIDTH, inky_display.HEIGHT))
draw = ImageDraw.Draw(img)

for y in range(inky_display.height):
    for x in range(inky_display.width):
        img.putpixel((x, y), inky_display.WHITE)

font = ImageFont.truetype(FredokaOne, 48)

message = u"{}°".format(temperature) 
w, h = font.getsize(message)
x = int((inky_display.WIDTH / 2) - (w / 2))
y = int((inky_display.HEIGHT / 2) - (h / 2))
draw.text((x, y), message, inky_display.BLACK, font)

img.paste(icon, (x, y + 20))

inky_display.set_image(img)
inky_display.show()