adafruit / Adafruit-GFX-Library

Adafruit GFX graphics core Arduino library, this is the 'core' class that all our other graphics libraries derive from
https://learn.adafruit.com/adafruit-gfx-graphics-library
Other
2.39k stars 1.54k forks source link

Background color for custom fonts #223

Open AntonioFromBrazil opened 5 years ago

AntonioFromBrazil commented 5 years ago

I really know the difficult involved to implement background color for custom fonts since they do not have an uniform size. Maybe one intermediate solution would be implemented only for monospaced custom fonts. It would be usefull when a better characters resolution is required. The user would create monospaced fonts bigger then the classic 5x7 built-in font. If the xAdvance has the same value for every characters it means that font is a monospaced one and this value is the width of the rect to be filled with bg color. For height of the area to be filled with bg color would be the maximum value find in the font definition table (height)... Does it make sense ?

makermelissa commented 5 years ago

Hi, would it work for you to draw a filled rectangle behind the character before writing the character or does it already have an unchangeable background on the character that would overwrite that?

AntonioFromBrazil commented 5 years ago

Thanks for your comment makermelissa. The problem is that if you draw a filled rectangle the screen will flicking but if the rectangle is done at GFX library it does not occurs. This can be proved writing at same x,y many different values with different bg colors using the classic 5x7 built-in font...

makermelissa commented 5 years ago

Do you want to try making the change and submitting a PR?

pljakobs commented 5 years ago

the font rendering code doesn't know about monospace or not, nor does the font format we use. Flickering can be fixed by drawing to a canvas first and then drawing the full canvas to screen. Really, that's the best possible way.

AntonioFromBrazil commented 5 years ago

Thanks pljakobs... Do you know some where to find examples of using canvas as you suggest ? I am not succeed in my tryies ! Thanks a lot

KurtE commented 5 years ago

For what it is worth, recently a few of us added GFX font support back into some of our more optimized display libraries for the Teensy 3.x and 4.0 boards. Currently we have it in my ILI9341_t3n library (https://github.com/KurtE/ILI9341_t3n) as well s a ST7735/ST7789 and ILI9488 library.

Secondary note: All three of these libraries support the ability to set a backing frame buffer (canvas) for those Teensy boards who have enough memory... You can tun on frame buffer, and do a several draws and then say tft.updateScreen(); and it does logically a drawRect of the whole buffer to the screen. Also have Asynchronous update function that uses DMA...

With this GFX font support, we added in some speedups of transparent text output. In particular it does quick checks to see if can output 8, 4, 3, 2 and finally 1 horizontal logical pixel using one logical FilllRect, which helps reduce the number of outputs...

But in addition to this, we added Opaque text output, which appears to be working pretty well. To do this, required the code to do some special stuff including: a) When you set one of the fonts as the current font, it scans through the font, finding the maximum extent up that any character extends, as to figure out a logical area to blank per character.

b) When I output a character, I remember the farthest X extent of that character, such that when the next character is output, if a pixel is within that extent (either because the previous character output beyond it's increment ((xOffset+width) > xAdvance) Or because this character has a negative xOffset, then the output code, needs to be careful if it is going to output a text background pixel. That is if the font bit is NOT set, we check to see if the previous characters font bit was set, if so we use it, else we set to text background color? Which one? of our x < xCursor we use previous characters background color else we use current characters background color (most the time the same...).

How b) is done depends on using frame buffer or not using frame buffer. If using frame buffer, I just look back at the logical pixel and see what color is there and stuff the memory out.

If not Frame buffer, I setup to do one logical drawRect, where I output all of the pixels for that character in one shot... So opaque output is a LOT faster than transparent. And I don't have a backing canvas to use, to see previous characters output, so I have code that can logically run through the font processing and ask if logical bit x, y is supposed to be a FG pixel or not...

c) complications of using transparent text. To make this properly work, the code will fill with the text background color, the full y extend (yAdvance), which can often extend farther down than you may want. In our libraries, we can set up a logical clipping rectangle which for cases like this, where maybe you with to display text within a button, you can setup your clipping such that the blanking does not extend down farther than you want.

I guess the question might be, does it make sense to try to integrate any of this into the GFX library?

If anyone wishes to look at the code or try it out (on Teensy) , the sources for the ILI9341 display are in the above mentioned link, the ST7735/89 is also in my github in a different not released branch (main copy of this branch is up in @mjs513 github project branch(ST7735_T4_rewrite) and ILI9488_t3 project (master branch) again @mjs513 has the master fork...

Sorry if I am off topic here.

akardas16 commented 4 years ago

hı, I am here because of same problem.. when I don't use any custom fonts, tft.print works nice and also background color for text works. but if I use any custom font, text overwriting screen at same position.(with other previous values!) I found that this is because of not working background color for custom fonts and this prevents previous value to hide on screen. I would to use custom fonts `// tft.setCursor(90,186); renderCoordinates(90,186,millis()/1000); tft.setTextColor(ORANGE,BLACK); // tft.setTextSize(1); // tft.setFont(&DSEG7_Classic_Mini_Bold_35); // tft.fillRect(90,140,60,60,BLACK); // tft.print(millis()/1000); tft.setFont();

  }

void renderCoordinates(int textXPosition, int textYPosition, int heading){ Serial.println(heading); tft.setFont(&DSEG7_Classic_Mini_Bold_35); tft.setCursor(textXPosition, textYPosition); tft.setTextColor(ORANGE, BLACK); tft.setTextSize(1); tft.print(heading); }`

PaintYourDragon commented 4 years ago

The lack of background color in custom fonts is on purpose and by design, this is covered in the documentation, about halfway down the page here: https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts

The issue is that custom fonts may have an indeterminate number of glyphs overlapping the same region, and the glyph-drawing function just isn’t set up to render that way (it would be prohibitive in both memory and speed on AVRs, which are still supported by the library).

For a flicker-free effect, consider making a GFXcanvas1 object covering the maximum text area (RAM permitting), clearing and drawing your text in there, then copying to screen using drawBitmap() with both foreground and background colors specified.

akardas16 commented 4 years ago

"The issue is that custom fonts may have an indeterminate number of glyphs overlapping the same region, and the glyph-drawing function just isn’t set up to render that way (it would be prohibitive in both memory and speed on AVRs, which are still supported by the library)." yes that's good point. but I dont know how to make GFXcanvas object. I use fillRect functon but now values flicker

PaintYourDragon commented 4 years ago

GFXcanvas1 canvas(width, height);

Then draw into 'canvas' same way you would the screen (all the same graphics primitives work - fillScreen(), print(), etc.). Colors are 0 for “off” pixels, 1 for “on.”

Then copy to the display with: tft.drawBitmap(x, y, canvas, width, height, foreground, background);

KurtE commented 4 years ago

Note: with our display libraries that we added support for drawing the GFX characters, I added support for output of these fonts in Opaque mode. We have something similar to canvas but simply call it a frame buffer...

This is for Teensy 3.x and T4.x boards

Yes your font format adds some complexity by allowing the characters to overlap, some, so the code does the best it can to handle this. This includes issues like it remembers the last character that was output as well as it's fg/bg color It then checks to see which portions of the character rectangles overlap and then has to some simple rules on what color to use. If the font character I am outputting is going to set that pixel it does. If it is not and going to BG color AND it overlaps with previous character and it is in the overlap And that pixel was used by that character it leaves it uses that color, else it sees it wants to draw it in BG color. If the position is in the previous characters rectangle it is uses that characters BG color else it uses the new characters BG color...

The bright side of all of this, is it is faster. That is since I know I am going to draw a full rectangle, I setup like the drawRect and simply output the appropriate bytes... And no flicker...

Note: there are additional interesting issues as well, which is for example if you draw 2 lines of text with Opaque mode, it needs to write the area beneath the character to the start of the next Row in the background color. Sometimes you don't want this large of area updated... So we also have the ability to set clip rectangle to bound this.

If your interested this code is contained in a few of our libraries, like my ILI9341_t3n, ST7735_t3/ST7789 as well, RA8875, ILI9488_t3, HX9357... Example code for this is up at: https://github.com/KurtE/ILI9341_t3n/blob/master/src/ILI9341_t3n.cpp#L3834

Note: It may not be 100% totally correct, but you always have the fallback to not use it and do like mentioned above. :D

orhanyor commented 4 years ago

GFXcanvas1 canvas(width, height);

Then draw into 'canvas' same way you would the screen (all the same graphics primitives work - fillScreen(), print(), etc.). Colors are 0 for “off” pixels, 1 for “on.”

Then copy to the display with: tft.drawBitmap(x, y, canvas, width, height, foreground, background);

im trying to test this feature with feather m4 and st7789 and getting this error.

AppData\Local\Temp\arduino_modified_sketch_559965\Display_TFT_SPI_.ino: In function 'void loop()':

Display_TFT_SPI_:181:68: error: no matching function for call to 'Adafruit_ST7789::drawBitmap(int, int, GFXcanvas1&, int, int, int, int)'

   tft.drawBitmap(10, 10, canvas, 128, 32, ST77XX_RED , ST77XX_BLACK);

                                                                    ^

In file included from Documents\Arduino\libraries\Adafruit_ST7735_and_ST7789_Library/Adafruit_ST77xx.h:30:0,

                 from Documents\Arduino\libraries\Adafruit_ST7735_and_ST7789_Library/Adafruit_ST7735.h:4,

                 from AppData\Local\Temp\arduino_modified_sketch_559965\Display_TFT_SPI_.ino:1:

Documents\Arduino\libraries\Adafruit_GFX_Library/Adafruit_GFX.h:81:8: note: candidate: void Adafruit_GFX::drawBitmap(int16_t, int16_t, const uint8_t*, int16_t, int16_t, uint16_t)

   void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w,

        ^~~~~~~~~~

Documents\Arduino\libraries\Adafruit_GFX_Library/Adafruit_GFX.h:81:8: note:   candidate expects 6 arguments, 7 provided

Documents\Arduino\libraries\Adafruit_GFX_Library/Adafruit_GFX.h:83:8: note: candidate: void Adafruit_GFX::drawBitmap(int16_t, int16_t, const uint8_t*, int16_t, int16_t, uint16_t, uint16_t)

   void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w,

        ^~~~~~~~~~

Documents\Arduino\libraries\Adafruit_GFX_Library/Adafruit_GFX.h:83:8: note:   no known conversion for argument 3 from 'GFXcanvas1' to 'const uint8_t* {aka const unsigned char*}'

Documents\Arduino\libraries\Adafruit_GFX_Library/Adafruit_GFX.h:85:8: note: candidate: void Adafruit_GFX::drawBitmap(int16_t, int16_t, uint8_t*, int16_t, int16_t, uint16_t)

   void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h,

        ^~~~~~~~~~

Documents\Arduino\libraries\Adafruit_GFX_Library/Adafruit_GFX.h:85:8: note:   candidate expects 6 arguments, 7 provided

Documents\Arduino\libraries\Adafruit_GFX_Library/Adafruit_GFX.h:87:8: note: candidate: void Adafruit_GFX::drawBitmap(int16_t, int16_t, uint8_t*, int16_t, int16_t, uint16_t, uint16_t)

   void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h,

        ^~~~~~~~~~

Documents\Arduino\libraries\Adafruit_GFX_Library/Adafruit_GFX.h:87:8: note:   no known conversion for argument 3 from 'GFXcanvas1' to 'uint8_t* {aka unsigned char*}'

exit status 1
no matching function for call to 'Adafruit_ST7789::drawBitmap(int, int, GFXcanvas1&, int, int, int, int)' 

when i remove the following line from the code it compiles and uploads normally

tft.drawBitmap(10, 10, canvas, 128, 32, ST77XX_RED , ST77XX_BLACK);

am i doing some very obvious mistake? :)