I am using uGui to display fonts on the screen. But the final result is not as satisfactory as it should be. The hardware is quite limited: color of 8 bpp (256 colors in total), with 3 bits for red, 3 bits for green, and 2 bits for blue (3: 3: 2).
The original code to obtain the color of a point is:
color = (((fc & 0xFF) * b + (bc & 0xFF) * (256 - b)) >> 8) & 0xFF |//Blue component
(((fc & 0xFF00) * b + (bc & 0xFF00) * (256 - b)) >> 8) & 0xFF00|//Green component
(((fc & 0xFF0000) * b + (bc & 0xFF0000) * (256 - b)) >> 8) & 0xFF0000; //Red component
Being:
-
fc
:uint32_t
, the color of the ink for that character. -
bc
:uint32_t
, the color of the background for that character. -
b
:uint8_t
, percentage of ink / background mix for a specific point of the character.
That last data is taken from the source file, previously rendered (using an external utility) and converted to array[]
of unsigned char
, in which each element represents the ink / background ratio of the point.
The results of the original code are quite satisfactory:
But several artifacts of color are appreciated (especially in A
). If we do zoom ...
In an attempt on my part to limit or eliminate this, and taking into account that only certain colors are used for the text / background, it occurred to me to artificially limit the number of possible colors to apply for the smoothing : for example, if the ink is black, use only the gray color for points that are not ink or background.
The code stayed like this:
// Calculamos el color para el antialiasing.
UG_COLOR acolor; // unsigned char
switch( fc ) {
case 0x92: // GRAY
acolor = 0xDB;
break;
case 0x1C: // GREEN
acolor = 0x5E;
break;
case 0xF0: // ORANGE
acolor = 0x90;
break;
case 0xDB: // SEMI_WHITE
acolor = 0xFF;
break;
default: // BLACK
acolor = 0xB6; // GRAY;
break;
}
color = (((fc & 0xFF) * b + (bc & 0xFF) * (256 - b)) >> 8) & 0xFF |//Blue component
(((fc & 0xFF00) * b + (bc & 0xFF00) * (256 - b)) >> 8) & 0xFF00|//Green component
(((fc & 0xFF0000) * b + (bc & 0xFF0000) * (256 - b)) >> 8) & 0xFF0000; //Red component
// Si no vamos a pintar ni con la tinta ni con el fondo, lo hacemos
// con el color para el antialiasing.
if( ( color != fc ) && ( color != bc ) ) color = acolor;
The result was ... different :
It is appreciated that the points of inappropriate have almost disappeared, but the general quality ... leaves a lot to be desired.
What is the correct way to perform this type of smoothing? What bit combination / operation should I perform?
Note: C or C ++, bit operations are the same in both.
EDITO
In response to @abufalia.
To paint a pixel on the screen, a unsigned char
is used:
*((unsigned char *)(posicion-en-memoria)) = color;
With color
being a UG_COLOR, which in turn is a uint32_t
.
In all paint text operations, the value of fc
and bc
is limited to 8 bits:
const UG_COLOR BLACK = 0X00;
const UG_COLOR GRAY = 0x92;
const UG_COLOR SEMI_WHITE = 0xDB;
const UG_COLOR WHITE = 0XFF;
const UG_COLOR GREEN = 0X1C;
const UG_COLOR ORANGE = 0XF0;
To get the percentage of the mix, use:
b = font->p[index++];
being:
typedef struct {
unsigned char* p;
FONT_TYPE font_type;
UG_S16 char_width;
UG_S16 char_height;
UG_U16 start_char;
UG_U16 end_char;
UG_U8 *widths;
} UG_FONT;