peterhinch / micropython-font-to-py

A Python 3 utility to convert fonts to Python source capable of being frozen as bytecode
MIT License
385 stars 69 forks source link

Avoid premature linebreaks from the spacing between characters #32

Closed enigmaniac closed 3 years ago

enigmaniac commented 3 years ago

Implementing a solution to avoid lines from wrapping or clipping prematurely, if the only pixels falling off the edge of the display would be the blank intra-character spacing to the right of the last character.

Particularly relevant for displays with a very small number of pixels (LED matrixes, flipdots, etc), where goal is to fit as many characters as possible into the space available.

Approach allows the user to specify how many pixels of whitespace exist to the right of characters using set_clip(). Those whitespace pixels are ignored when checking if the string fits in the width of the screen: --> The new self.char_space attribute is subtracted from the line length when determining whether to wrap, clip, etc.

self.char_space = 0 by default, to maintain compatability with existing approach.

peterhinch commented 3 years ago

Please can you explain the use case for putting whitespace between every character. Perhaps naively I had assumed that fonts were designed so as not to need this.

enigmaniac commented 3 years ago

Yes, in the font implementation for Micropython, the whitespace already exists in the character glyph itself. The issue is that we need to account for this whitespace when deciding when to wrap/clip lines.

Easiest to explain with an image:

image

Imagine an 8x20 pixel display (outline in green) The red boxes define the individual glyphs. The space between characters is built into the glyph -- it's the extra column of blank pixels to the right of each number.

But when it comes to the last glyph 5 -- in the current implementation, the full glyph, including the whitespace, doesn't fit within the bounds of the available screen. So it will wrap or clip.

However, the printable part of glpyh does fit within the green bounding box, as the only part which doesn't fit is whitespace. So we can actually safely display this glyph, and shouldn't be clipping/wrapping.

The proposed edit fixes this bug. We allow folks to specify how much whitespace is built into the right of each glyph (eg 1px), and then use that to give an extra margin when calculating if we need to clip/wrap.

peterhinch commented 3 years ago

OK, I get it now :)

The space between characters is built into the glyph

Is it the same for every glyph? How can the user determine the number?

Please could you update the docs with the explanation as given above, also the main use cases you mentioned, and I'll merge it.

peterhinch commented 3 years ago

Having thought about this some more I think the logic of this patch is not optimal for two reasons.

  1. It requires us to be sure, for every possible font, that the number of trailing blank columns will always be the same for every glyph in that font.
  2. If condition 1 is met, it requires the user either to know or somehow to determine that number.

What does "know" is the code rendering the glyph. In principle, if a glyph would cause a wrap or clip, the code could determine the number of printable columns and re-assess whether to wrap/clip. The UI would then be unchanged. However we have to consider code size: the Writer classes are used in nano-gui which I recently put on a severe diet so that it is usable on an ESP8266.

A compromise might be to ignore the wrap case. Wrapping implies a multi-line display. The consequence of this bug is that (admittedly fairly frequently) a line will wrap one word too soon. In some years nobody has noticed this.

The clip case is relevant to small single line displays. A solution might be to clip in such a way that clipping no longer occurs at a character boundary. The last glyph on the line would be displayed until it ran out of space, typically showing fewer than its full column count. I think that would be visually acceptable on single and multi line displays. It would automatically solve this issue. Again the UI would be unchanged. I haven't yet had time to figure out how to code this or the likely impact on size and complexity.

Your thoughts would be welcome.

peterhinch commented 3 years ago

I have pushed a branch col_clip which you might like to try. [EDITED]: If you set col_clip=True, characters that are clipped are partially rendered. This is an idea I've had at the back of my mind for a while. I think it looks better and should solve part of your problem. I have tested monochrome and color displays, with and without fast rendering, on various displays and also checked that the GUI is unaffected.

Please let me know the outcome of any tests you do.

I'm considering a way of fixing the wrap behaviour (without changing the UI). I'll code it up and assess the impact on code size and performance.

Re font_to_py I am still thinking about how best to support bitmapped font formats. I think it's a good idea, but it needs a little more thought.

peterhinch commented 3 years ago

I don't know if you're still onboard with this.

I've pushed an update on the col_clip branch which should fix the word wrap issue. It requires further testing but initial tests were successful. There are no API changes. At runtime it now takes account of any columns of blank pixels at the end of the last character before determining whether to wrap. In the process I found a couple of bugs - I just hope I haven't introduced any new ones.

With the proportional font I used for testing I had trouble locating a glyph that had a trailing blank column. This is probably why nobody had spotted this bug. With fixed pitch bitmapped fonts these will be commonplace.

I'd be grateful for results from any testing you might do.

enigmaniac commented 3 years ago

Great, will review this weekend!

On Fri, Jan 29, 2021, 14:07 Peter Hinch notifications@github.com wrote:

I don't know if you're still onboard with this.

I've pushed an update on the col_clip branch which should fix the word wrap issue. It requires further testing but initial tests were successful. There are no API changes. At runtime it now takes account of any columns of blank pixels at the end of the last character before determining whether to wrap. In the process I found a couple of bugs - I just hope I haven't introduced any new ones.

With the proportional font I used for testing I had trouble locating a glyph that had a trailing blank column. This is probably why nobody had spotted this bug. With fixed pitch bitmapped fonts these will be commonplace.

I'd be grateful for results from any testing you might do.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/peterhinch/micropython-font-to-py/pull/32#issuecomment-769794348, or unsubscribe https://github.com/notifications/unsubscribe-auth/AICX6NH4JTX7W3NMZ645OADS4KXIBANCNFSM4WQU7O2Q .

peterhinch commented 3 years ago

I have now pushed an update to master so, barring any bugs I think we can close this. Thank you for pointing out this issue.