News:

Let's find out together what makes a PIC Tick!

Main Menu

Smooth scaling up small pixel fonts

Started by trastikata, Jul 08, 2024, 03:45 PM

Previous topic - Next topic

trastikata

Hi,

Usually when we scale a font in a MCU at run-time every pixel from the original font is printed n-times in each direction. Because the original font did not have much pixels to begin with, the scaled-up font looks like every pixel has been magnified like in the old consoles...

This is a simple method to smooth the pixelization of small fonts being scaled up at run-time. Basically we look through the original font's pixels for a pattern where two diagonal pixels are set and when scaling them up, we add some pixels at the sharp rectangle edges in the opposite direction. This would mean that when they are multiplied, the empty space between the squares will be magnified too and we have to fill it with some pixels.

Originally I found the method described here: http://www.technoblogy.com/show?3AJ7 but the code there works only for doubling the size. And I need it to be universal with different font multipliers - even and odd numbers.

So I made an Excel sheet simulation and looked for the pattern, which became immediately apparent once everything has been drawn 2-s are the additional pixels:

SM1.jpg

The additional pixels can be add by two loops which are as it seems related to the scaling factor - thus as you move from the edge up to Scale-1 pixels print Scale-1-current_pixel#.

Also to remove a glitch from the original code where the code is overwriting some additional pixels, we first have to print the jugged scaled character and then loop through the original pixels starting from the second line looking for the diagonal pattern and add the pixels with this "move from the edge up to Scale-1 pixels print Scale-1-current_pixel#" algorithm.

The font looks different, a bit bolder but it is smoother and in my opinion looks better than the big pixels in the original variant.

For best appearance, however you have to use a separate font table with the appropriate font size which takes extra space, which nowadays is not a problem most of the time, but this was a good mental exercise to fill some free time...  :)

An example of scaled up 6x8 font with and without the smoothing.

SM6.JPG SM5.JPG

SM2.JPG SM3.JPG

top204

#1
I look forward to seeing the source codes, and trying it out. :-)

It takes me back to the mid 1980s, when I invented a method to make standard dot-matrix printers 'Near Letter Quality' (does anyone still remember the expensive NLQ dot matrix printers?), by moving the head a fraction of a dot and re-printing, and the edges of each character were calculated so the printer moved a tiny bit for certain re-print sections of the characters then a bit more for the rest of the re-prints, so it smoothed them out as well.

I know it sounds like something from the stone age, but at the time it was a one-off invention, that made cheap printers look excellent. However, unfortunately I also got ripped off then by a Fulham UK company I created it for, and took things on trust and not written so I never got a penny for it, even back then. I never learned from that experience, so... "More Fool I".

TimB


"It takes me back to the mid 1980s, when I invented a method to make standard dot-matrix printers 'Near Letter Quality"

I think Les I used your software. I was trying to print a manual for a product I was selling of a Mouse and a drawing package for the Enterprise Computer. Not every manual just a master I could get photo copied.

One thing I remember though it was V slow.

top204

#3
Good memory Tim. It was slower than standard printing because it had to go over a line a few times, moving the paper very fractionally and calculating each character in the line before moving the head across.

I created the original code in 6502 assembler for the Atari 8-bit machines and the Commodore 64 machine and the BBC B, and a couple in Z80 assembler for a few other machines so it silently went between the O/S and the end user program and acted as a print buffer when the machine supported that type of mechanism. I had to borrow friends different computers for the coding on them, then give them back after a few days, and they had the trust in me to do that. :-) I was going to develop it further, but then got ripped off, so he probably went on to sell it to others because I, stupidly, gave him the code and the methods because of false promises.

The enterprise machine was well out of my price tag back then, but I truly wish I had one now. I had to get the machines that I could afford, and mostly bought second hand, repair it, learn it, then sell it for a step up to another computer. Good times... Poor times, financially, but good times because what you never had you could not miss and I always loved learning. :-)



trastikata

Quote from: top204 on Jul 09, 2024, 01:11 PMI look forward to seeing the source codes, and trying it out. :-)
...

Hello Les,

It still amazes me how much gets forgotten and reinvented again at some point ... I am fully convinced that the people back then were much smarter than the average user nowadays. I remember how in the past electronics came with a manual and a repair schematic, cars had repair tools for not if but when it breaks on the road to nowhere ;D, now it's all buy new gadget and get road assistance when needed. 

On the main topic, here's the code I wrote. It lacks good commenting but should be more or less understandable. In my code screen origin is bottom -left so everything is printed and counted lef-to-right and bottom-up.

I did not have much time to test it and I think it will bug if the font size is 1 but this easy to fix. (Un)fortunately the company I work for got a new big project, so I am back to work, which would mean hobby projects (TFT libraries) will get less time in the next few weeks, but it is what it is.

    Dim OffsetX As Word
    Dim OffsetY As Word
    Dim RelativeX As Byte
    Dim RelativeY As Byte
    Dim yCounter As Byte
    Dim xCounter As Byte
    Dim iCounter As Byte
    Dim jCounter As Byte
    Dim pBit1 As Bit
    Dim pBit2 As Bit
    Dim wTemp1 As Word
    Dim wTemp2 As Word
   
    If bFontSize = 0 Then bFontSize = 1
    TftLookUpFont(bChar)

    OffsetX = wX_Pos * bFontSpace * bFontSize
    OffsetY = wY_Pos * bFontSpace * bFontSize
   
    'Scale character
    For yCounter = 0 To bFontPixels - 1                                         'Loop vertically
        wTemp1 = waTftPrintArray[yCounter]                                      'Get Row from array
        For xCounter = 0 To bFontPixels - 1                                     'loop horizontaly
            pBit1 = GetBit wTemp1, xCounter                                     'Get current bit  
            For jCounter = 0 To bFontSize - 1                                   'print scale rows
                For iCounter = 0 To bFontSize - 1                               'print scale col
                    RelativeX = xCounter * bFontSize
                    RelativeY = yCounter * bFontSize
                    If pBit1 = 1 Then
                        TftPixel(OffsetX + RelativeX + iCounter, OffsetY + RelativeY + jCounter, wForeColor)
                    Else
                        If pTransparent = 0 Then 'Transparent print bit not set
                            TftPixel(OffsetX + RelativeX + iCounter, OffsetY + RelativeY + jCounter, wBackColor)
                        EndIf
                    EndIf
                Next
            Next
        Next
    Next       

    'Fill corners
    For yCounter = 0 To bFontPixels - 1                                         'Loop vertically
        wTemp1 = waTftPrintArray[yCounter]                                      'Get Row from array
        For xCounter = 0 To bFontPixels - 1                                     'loop horizontaly
            pBit1 = GetBit wTemp1, xCounter                                     'Get current bit
            If yCounter > 1 Then
                If xCounter = 0 Then                                            'Leftmost                       
                    wTemp2 = waTftPrintArray[yCounter - 1]
                    pBit2 = GetBit wTemp2, xCounter + 1
                    If pBit1 = 1 And pBit2 = 1 Then
                        For iCounter = 0 To bFontSize - 2                                           'horizontal
                            For jCounter = 0 To bFontSize - 2 - iCounter                            'vertical
                                RelativeX = bFontSize + iCounter
                                RelativeY = yCounter * bFontSize + jCounter
                                TftPixel(OffsetX + RelativeX, OffsetY + RelativeY , wForeColor)     '+x +y
                                RelativeX = bFontSize - 1 - iCounter
                                RelativeY = yCounter * bFontSize - 1 - jCounter
                                TftPixel(OffsetX + RelativeX, OffsetY + RelativeY , wForeColor)     '-x -y
                            Next
                        Next
                    EndIf
                ElseIf xCounter = bFontPixels - 1  Then                         'Rightmost
                    wTemp2 = waTftPrintArray[yCounter - 1]
                    pBit2 = GetBit wTemp2, xCounter - 1
                    If pBit1 = 1 And pBit2 = 1 Then
                        For iCounter = 0 To bFontSize - 2                                           'horizontal
                            For jCounter = 0 To bFontSize - 2 - iCounter                            'vertical
                                RelativeX = xCounter * bFontSize - 1 - iCounter
                                RelativeY = yCounter * bFontSize + jCounter
                                TftPixel(OffsetX + RelativeX, OffsetY + RelativeY , wForeColor)     '-x +y
                                RelativeX = xCounter * bFontSize + iCounter
                                RelativeY = yCounter * bFontSize - 1 - jCounter
                                TftPixel(OffsetX + RelativeX, OffsetY + RelativeY , wForeColor)     '+x -y
                            Next
                        Next
                    EndIf
                Else                                                            'Middle
                    wTemp2 = waTftPrintArray[yCounter - 1]
                    pBit2 = GetBit wTemp2, xCounter - 1
                    If pBit1 = 1 And pBit2 = 1 Then
                        For iCounter = 0 To bFontSize - 2                                           'horizontal
                            For jCounter = 0 To bFontSize - 2 - iCounter                            'vertical
                                RelativeX = xCounter * bFontSize - 1 - iCounter
                                RelativeY = yCounter * bFontSize + jCounter
                                TftPixel(OffsetX + RelativeX, OffsetY + RelativeY , wForeColor)     '-x +y
                                RelativeX = xCounter * bFontSize + iCounter
                                RelativeY = yCounter * bFontSize - 1 - jCounter
                                TftPixel(OffsetX + RelativeX, OffsetY + RelativeY , wForeColor)     '+x -y
                            Next
                        Next
                    EndIf
                    pBit2 = GetBit wTemp2, xCounter + 1
                    If pBit1 = 1 And pBit2 = 1 Then
                        For iCounter = 0 To bFontSize - 2                                           'horizontal
                            For jCounter = 0 To bFontSize - 2 - iCounter                            'vertical
                                RelativeX = xCounter * bFontSize + bFontSize + iCounter
                                RelativeY = yCounter * bFontSize + jCounter
                                TftPixel(OffsetX + RelativeX, OffsetY + RelativeY , wForeColor)     '+x +y
                                RelativeX = xCounter * bFontSize + bFontSize - 1 - iCounter
                                RelativeY = yCounter * bFontSize - 1 - jCounter
                                TftPixel(OffsetX + RelativeX, OffsetY + RelativeY , wForeColor)     '-x -y
                            Next
                        Next
                    EndIf
                EndIf
            EndIf
        Next
    Next