Skip to content

Text Render (the LPGO algorithm)

max mc costa edited this page Oct 20, 2015 · 5 revisions

Fast Text Rendering it's available from 0.70b11!!!
The RA8875 comes with internal font, rom font support and it's pretty fast but I decided to include external font support as well, commonly named 'render font'.
Many LCD/TFT libraries have this ability, most use the 'write pixel' method that consist in decode the char glyph in bits and write sequential stream of pixels (1:pixel on, 0:pixel off) but RA8875 its not the right beast to do that since it's drawPixel (even accellerated) it's slow and screen it's too big.
The goal its:

  • Fast rendering (of course)
  • Can use indipendent w and h scaling (any size)
  • Can use background transparency
  • Can use all the features of internal fonts (almost all)
  • Use the same commands of internal text (setCursor,setTextColor,etc)

So I have to find alternatives...

Here's the methods I have tested so far


Single Line streaming

This method it's simple, I read an entire line of the glyph by storing in a micro buffer, then I send the entire line by using direct pixel writing (or pixel stream) that it's faster than singlePixel draw since it doesn't need address every pixel.
This methods has the main drawback that cannot use font scaling, font background transparency it's impossible and.. it was surprisely slow!

Area stream

In this methods you define a delimited area that correspond to the w/h of the single glyph then send all the glyph bits sequentially.
On paper this should work but for some reason I cannot get properly working, also I had difficulties to include font scaling and it's almost impossible font background transparency

Geometry Compressed Font

This can be the best method, maybe not for small font size but for big ones can be killing!
The major drawback it's compress a font by divide the glyph in small sizes of geometric primitives (that RA8875 render fast by using his hardware accelleration). To be honest I finally get a decent alghorithm to do that (after weeks of developing, nobody does something similar in the past) but actually I decided to keep private.
The main advantage it's the amazing quality with font scaling and the fast rendering for big sizes, with this method the font became most similar to a vector font than a bitmap, but drawback it's on small sizes where the advantages vanish in slow render.
Maybe in a future...

chunks of bytes optimization [abandoned]

The first rule to get speed is write as less single pixels it's possible and since RA8875 it's extremely fast to write geometrics like filled rects, my first optimizing trick it's write only pixels are ON.
The second trick it's an algoritm that scans glyph data for concurrent pixels (in blocks of 8,16,24,32,40,48,etc. depends of the glyph width) and write an hardware accellerated line if pixels are ON, move the cursor if are OFF and write the remain pixels with singlePixel draw.
If the text has background, before all I calculate text area and fill with rectangle.
With this method I can deal easily with font background transparency and font scaling by using (for font scaling) filled rect instead line and forget completely of the 'black' pixels.
Here's the first results in speed:

optimized mode (8x8 scaled, SUMOTOY text,fluide_caps,CENTER(x) Text):

  • background transparent: 22240ms
  • filled: 23752ms

optimized mode (no scale, SUMOTOY text,fluide_caps,CENTER(x) Text):

  • background transparent: 15099ms
  • filled: 15199ms

old mode (8x8 scaled SUMOTOY text,fluide_caps,CENTER(x) Text):

  • background transparent: 68623ms
  • filled: 71090ms

old mode (no scale, SUMOTOY text,fluide_caps,CENTER(x) Text):

  • background transparent: 31854ms
  • filled: 32269ms

This results from 8,10 pixel wide fonts but can be better for big fonts where the optimizer alghorithm has more chances to write lines/rect instead of pixels.
Another thing to notice it's the minimal gap between scaled and not scaled of the optimized method!
The major drawback it's the complexity of the code, everithing has a cost unfortunatly, but I hope to get better optimization in the future.
Do not forget that I will support RLE compressed fonts soon and this will halve the RAM/FLASH needs.

The solution, Line Pixel Grouping Optimization (LPGO)

The last approach was not completely bad but has 2 big issues:
a) using chunks of 8,16,.. pixels was a good idea but has really works only when font w it's divisible by chunk size, otherwise they go offsetted and the optimization it's not so effective.
b) dealing with chunks results a messy code.
The new approach save the entire line in a buffer and I use a checksum to identify on the fly blank or filled lines, the rest it's analyzed to see in there's chunks of concurrent pixels.
This method result in less messy and faster code, I've called this Line Pixel Grouping Optimization (LPGO)
Here's a picture that explain how LPGO work:

lofr You notice that very few calls are needed to write the entire char since rects are hardware accellerated in RA8875 and I'm actually draw only ON pixels, the blank ones are used just to increment x,y cursor!
In this way char is transparent but what happen if I need a solid background? Easy, the library knows exact the w,h of the char and draws a solid rect before that it's blazing fast since it's a single hardware accellerated command!
I'm using exclusively fillRect here but my library falls automatically to drawLine if the hight of the rect is 1 (and even this command it's hardware accellerated).

Here's in comparison an old school pix drawing, many libraries use this tecnique, it write every pixel (no matter if they use lines or pix stream) so even blank pix are painted. oldlofr

Speed Test

Screen Size: 800x480
Font: akashi_36
Color: WHITE (background transparent)
Font Scale: 3
Text: SUMOTOY


Arduino UNO
Old Method:2520744 -> New Method:135068


Teensy 3.1
Old Method:612941 -> New Method:26005