pimoroni / pimoroni-pico

Libraries and examples to support Pimoroni Pico add-ons in C++ and MicroPython.
https://shop.pimoroni.com/collections/pico
MIT License
1.31k stars 495 forks source link

How I dealt with inconsistent letter spacing using the font8x12.py font file with an i75 RGB LED panel #238

Open PaulskPt opened 2 years ago

PaulskPt commented 2 years ago

When I was creating a small project to display MS Flightsimulator airplane track data onto an i75 64x32 RGB LED panel, controlled by an i75 hub controller (PIM584). I wanted also to print static texts to the panel at the moment of startup. I did not find a suitable example to display texts statically. In this repo there is the scroll text example: pimoroni-pico/micropython/examples/interstate75/i75_64x64_scrolling_text.py which I used to modify into a function to display texts statically. While working on this project I discovered that, using the font8x12.py, letter spacings were not equal, causing ugly gaps, which I did not like. I think that this inconsistent letter spacing already existed for quite some time. See the image on: https://learn.adafruit.com/adafruit-protomatter-rgb-matrix-library , copied and annotated below:

Inconsistent_letter_spacing_on_Adafruit_LED_Matrix_Panel

I looked at the font8x12.py file. I saw that the characters defined in this file, the majority had a lot of 'columns' with value 0 (0x000), as you can see in the next image:

font8x12 py_

I decided to cut all these 0 bytes. See the result in the next image:

font8x12_v2 py_

Next I added some algorithm into the function that handles the displaying of texts, among it a length count to determine the number of columns being read from the font file. Together with other text handling I managed to create a suitable text displaying function. See some results of static texts below:

Result #1a

IMG_3777

btw: the letter M is very wide compared to the other letters. I just cut one column in the middle.

Result #1b

IMG_3795

And after cutting 2 more columns in the letter M definition (I think this look better balanced compared to the other letters):

Result #1c

IMG_3797

Result #2a

IMG_3778

Result #2b

IMG_3796

Result #2c

IMG_3798

After one column cut and alteration of the most right column, the letter G now looks as:

Result #2d

IMG_3808

Now most letters are 4 columns wide.

Result #3

IMG_3779

Finally I gave this lowercase letter m a 'haircut'. This looks better isn't it?

Result #4

IMG_3809

I put various images and explanation on the Pimoroni Discord channel, #ask-for-help. I put them there because, at some point in the development, I struggled with two problems while displaying track data (only numbers - not using the new functions I am talking about here).

In my project I load both the font8x12.py (modified version) as the font10x14.py. To print a static text, the following function is called: def text(self, text, fonttyp): This function calls: def display_text(self, text, fonttyp, y, t):. I am thinking to combine these two functions. I also think the latter function can be optimised. The majority of functions in my script I put in a class called HdgRibbon (btw: a name that stems from another project using a Raspberry Pi Zero2W with a Pimoroni Displayhat mini).

That's all folks!

Gadgetoid commented 2 years ago

Nice work. I think I may have missed a trick somewhere in the Python version of the text rendering, since in C++ these 0x00 chars are included (since variable length arrays can be... contrived) and the text rendering gives you the choice of either Fixed or Variable ... both of which have their benefits. Variable just skips the 0x00 columns for any given letter and offsets the next letter by the number of columns actually drawn (plus one for spacing).

In Python you just get fixed-width... which isn't great.

Since wider letterforms are great for glance reading and accessibility, I'd guess the best course of action is to take your modified font examples and turn them into "condensed" versions of the existing ones. This way we'll end up with:

That said, since all the font colums are represented as 16-bit values and we only use 12 or 14 of those bits it might be possible to flag certain colums as being uncondensed only with the 15th bit.

For example, this is the wide m:

[0x3c0, 0x020, 0x020, 0x1c0, 0x020, 0x020, 0x3c0, 0x000]  # "m"

Versus the same letter with columns marked for removal in the "condensed" version (by adding hex(1 << 15):

[0x3c0, 0x8020, 0x020, 0x1c0, 0x8020, 0x020, 0x3c0, 0x000]  # "m"

Then we just need font rendering that cuts out any column where column & 0x8000 == 0x8000 for condensed mode, and any column where column == 0x0000 for variable-width mode.

The reason my wavy/scrolling text demos use fixed-width chars is because it simplifies figuring out which letter we should start drawing from. It's... not very friendly code in retrospect.

The final function prototype would be something like:

def display_text(text, x, y, font, condensed=False, variable=False):
    pass

It's easier just to use x and y coordinates for static text, since we know we're always going to draw the whole piece of text from left to right starting from that coordinate and none of the calculations from stepping through letter-by-letter and discarding columns will be wasted. The function can then either run until it hits the edge of the screen and stop, or even try to break the text onto a new line starting again at x, y + letter_height + 1.

PaulskPt commented 2 years ago

Thank you for your comment, explanations and new ideas. Yep, that's a nice solution to be able to use "normal" and "condensed" fonts in that suggested way. In this moment I have a small 'pause'. I bought a new desktop PC (custom assembled) and am now in the process of installing / setting up the new one. Takes days, if not some weeks of work to put the new machine 'to my hand'.

Gadgetoid commented 2 years ago

I bought a new desktop PC (custom assembled) and am now in the process of installing / setting up the new one. Takes days, if not some weeks of work to put the new machine 'to my hand'.

Definitely not jealous :laughing:

And very familiar with how long it takes to get comfortable. Have fun!

PaulskPt commented 2 years ago

Thanks!