2025-04-17

Back to LCD

My graphics library has undergone plenty of improvements for the e-paper work, but now I am back on LCDs. These have extra challenges, because they have colours and shades.

The main reason is new environmental sensor boards with LCD.

This means I need to anti-alias text, digits, lines, circles, etc. Previously I had pre-rendered 16 level fonts and icons. But I have moved away from pre-rendered bit map fonts in my latest e-paper code already.

I have gone through several approaches to this. The fact my fonts are vector based previously allowed me to just draw lines and circles on the e-paper. That is fine when over plotting is no problem, i.e. white on white is white.

But if you want to anti-alias you have to over sample, e.g. a 4x4 matrix to then know how many pixels are set (0-16) and use that to control alpha blending of existing pixel and new pixel (or background pixel and foreground pixel).

My first approach for fonts was to make code to allow me to tell if a point x,y is plotted or not - so instead of actively plotting dots (circles) and multiple lines that over lap other lines and dots, I have to do the maths to work out "is this point within a dot" and "is this point on a line". The dot is easy - simple Pythagoras. But the lines was more work - I can work out distance to a point is within stroke width using squares. Working out end of line is harder, so ended up working out the point of perpendicular intersection so I could range check end points and then check distance using squares.

It worked, and allowed me to over sample 4x4 and work out grey levels.

A better way

The problem with the above is the 16 times processing of points. So I came up with a new plan - scan lines and run lengths.

The new logic scans the character top to bottom to determine the left/right for each line or dot that is in the character at that scan line. This is actually quite easy for a dot as I can use a look up to get the left/right for any point on a circle and scale. The line is a rectangle, and the fact it is only ever horizontal, vertical, or a 45 degree line means I don't even need square roots to work out the points, and simply work out the left/right for the specified Y scan. It is simple to merge overlapping runs on a scan line and the max runs is low as we know how the font works (up to 6 ranges).

This means only 4 times the processing of non anti-aliasing, as opposed to 16 times. The runs can then be worked out 4 scan lines at a time and plotting a sequence of the same grey level. The same logic works for anti-aliased or not, just not 4 times over sampling on the Y axis when not anti-aliasing.

7 segment digits

The 7 segment digits are not simple lines and dots, they are a complex path (original in SVG), but it is easy to render and run length encode the scan lines for a single large 7 segment digit (well, 9 segments with colon and dot). I can then scale which lines I extract from this large image to make the same run length logic as I do for the vector fonts and use the same run length plotting code.

Lines and circles

Another primitive is lines and circles, and whilst arbitrary angle lines do need one fast integer square root to get the corners, converting to run length on scan lines is, again, simple, and can use the same run length plotting code.

The same is true for a circle, using the same circle lookup table (with interpolation for a larger circle than the table).

Even faster

All of the above still comes down to pixels, but there is a simple important step I have now added to that. I made the pixel plot primitive have a horizontal run length parameter. This allows a number of checks and memory access and so on to be worked out once, and then a simple loop to apply a run length of pixels plotted the same. When plotting 100% alpha it is extra quick as no checking of existing pixel and alpha blending. It can be a simple loop incrementing memory address. Having all of the primitives for fonts, 7 seg, lines, circles, and so on actually convert to run length makes it easy to use such a primitive.

QR!

P.S. Yes, everyone knows I am a stickler for accurate valid QR codes, but I have in fact gone as bit mad with an option here - the targets are 100% correct but the pixels are circles. This is because of the way QR codes are decoded hitting the pixel centres (having found the targets to get accurate alignment), so this should always work whilst having a subtly different aesthetic. I think it is better than some of the other QR code abuse, but it is an option on my EPD code.

1 comment:

  1. Perhaps you could use your existing rendering algorithm from the e-paper implementation that sounds like it rasterises the vector font into a bitmap. If you do this in an off-screen buffer you can then transfer it to the screen with a Bit-BLIT algorithm: use the offscreen buffer as a mask for alpha and again in the colour channels.
    The implementation you have come up with does sound similar to Bit-BLIT but it might be worth abstracting and formalising it as it's a useful algorithm to have around for getting arbitrarily shaped things from rectangular buffers onto a screen with existing content.
    During screen refresh you have the option of re-rendering everything or re-compositing from the off-screen buffers, depending on your CPU/RAM tradeoffs.

    -- andyjpb

    ReplyDelete

Comments are moderated purely to filter out obvious spam, but it means they may not show immediately.

Faikin Remote

We have done a lot of small PCB designs over the years, but by far the most popular is my Faikin board. Reports are that even Daikin have re...