Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting flickering with large front redraws #36

Closed
scottchiefbaker opened this issue Nov 27, 2023 · 6 comments
Closed

Getting flickering with large front redraws #36

scottchiefbaker opened this issue Nov 27, 2023 · 6 comments

Comments

@scottchiefbaker
Copy link
Contributor

I'm attempting to build a count-up timer with a large font (160px+). When I redraw the screen once per second I get a visible flicker when I draw over the previous value. I have tried using BgFillMethod::Block and drawing a large rectangle over the previous text, and both have the same visual flicker. Is this a limitation of using large fonts? Is it even possible to render a large font without flicker on an ESP32?

#include "NotoSans_Bold.h"
#include "OpenFontRender.h"

#define TTF_FONT NotoSans_Bold // The font is referenced with the array name
OpenFontRender ofr;

#include <SPI.h>
#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI();

void setup(void) {
    tft.begin();
    tft.setRotation(1);

    ofr.setDrawer(tft); // Link drawing object to tft instance (so font will be rendered on TFT)
    ofr.loadFont(TTF_FONT, sizeof(TTF_FONT));
}

void loop() {
    tft.fillScreen(TFT_BLACK);

    for (int i = 0; i <= 1000; i++) {
        ofr.setCursor(10, 10);
        ofr.setFontColor(TFT_WHITE, TFT_BLACK);
        ofr.setFontSize(200);

        //ofr.setBackgroundFillMethod(BgFillMethod::Block);
        tft.fillRect(10, 10, 120, 120, TFT_BLACK);

        ofr.printf("%02d", i);

        delay(1000);
    }
}
@takkaO
Copy link
Owner

takkaO commented Nov 29, 2023

Thank you for using 😊

Looking at the code you provided, the problem is probably tft.fillRect in the loop.
In your code, every time you update a character, you repaint it in black and then rewrite it. This is the cause of the flickering.

To fix it, try removing tft.fillRect and set setBackgroundFillMethod(BgFillMethod::Block.

void setup(void) {
    tft.begin();
    tft.setRotation(1);

    ofr.setDrawer(tft); // Link drawing object to tft instance (so font will be rendered on TFT)
    ofr.loadFont(TTF_FONT, sizeof(TTF_FONT));

    ofr.setBackgroundFillMethod(BgFillMethod::Block);    // Add
}

void loop() {
    tft.fillScreen(TFT_BLACK);

    for (int i = 0; i <= 1000; i++) {
        ofr.setCursor(10, 10);
        ofr.setFontColor(TFT_WHITE, TFT_BLACK);
        ofr.setFontSize(200);

        //ofr.setBackgroundFillMethod(BgFillMethod::Block);
        //tft.fillRect(10, 10, 120, 120, TFT_BLACK);    // Remove

        ofr.printf("%02d", i);

        delay(1000);
    }
}

If this does not stop the flickering, you may want to consider using the Sprite function.

@scottchiefbaker
Copy link
Contributor Author

Using BgFillMethod::Block doesn't seem to solve my flickering issue. I get ghosting from the previous character and I still see the flickering on each character change.

PXL_20231129_214434321.mp4

Am I missing something obvious?

@chconnor
Copy link

chconnor commented Dec 4, 2023

@scottchiefbaker - You're using TFT_eSPI? If you have enough memory, make a sprite and use that. So every time you want to change the numeral, you will:

  • clear the sprite with mysprite.fillSprite(TFT_BLACK);
  • draw the numeral to the sprite (using coordinate system of the sprite, i.e. 0,0 at top left)
  • push the sprite to the tft: mysprite.pushSprite(x,y); // where x,y is the top left of the position to draw the sprite

You'll need to make an appropriately-sized sprite, so you'll have to figure out the rectangular bounds of a character. So, something like:
static TFT_eSprite mysprite = TFT_eSprite(&tft);
...
in setup():
void *out = mysprite.createSprite(SPRITE_WIDTH, SPRITE_HEIGHT);
if (out == NULL) Serial.println("Could not allocate memory for sprite!");

I'm just a beginner, but this is my understanding of how to do this with minimal flicker. (Sprites don't draw instantly, but I don't think it gets any faster than that -- the limit will be the time it takes to push pixels to the screen.)

@scottchiefbaker
Copy link
Contributor Author

scottchiefbaker commented Dec 5, 2023

@chconnor using a sprite actually worked great! Now my updates are smooth.

I did run in to hiccup with setAlignment() which doesn't appear to work with a sprite? I want my text to align to the lower right of my sprite. Anytime I put ofr.setAlignment(Align::BottomRight) (or any alignment) no text renders at all. As soon as I comment the alignment out it works again.

Is there a better way to align text to the lower right?

@chconnor
Copy link

chconnor commented Dec 5, 2023

When you set Align::BottomRight, are you are adjusting the coordinates of where you are drawing? E.g. if you are doing setCursor(0,0) with Align::TopLeft, when you are doing Align::BottomRight you want to change that to setCursor(x,y) where x and y are the width and height of the sprite (i.e. the coordinates of the bottom right of the sprite). If you aren't doing that, it makes sense that nothing would appear, because the cursor being at 0,0 would make everything show up above and to the left of the bounds of the sprite.

(If you are using drawChar or drawString, analogous logic applies.)

That said, at least with the font that I use, I've had some minor alignment issues with TopRight and BottomRight (see my issue here) so you might be better off with TopLeft and BottomLeft if you can make those work, if you need consistent alignment. (Or it might just be me.)

@scottchiefbaker
Copy link
Contributor Author

@chconnor thank you for your help with the Sprite method. Once I learned that, I was able to get things work correctly. I'll submit a PR for a SmoothFont example here in a little bit. Just so that it's archived here is the sample code I wrote to output millis() on the screen.

#include "OpenFontRender.h"
OpenFontRender ofr;

#include "NotoSans_Bold.h"
#define TTF_FONT NotoSans_Bold

#include <SPI.h>
#include <TFT_eSPI.h>

TFT_eSPI tft       = TFT_eSPI();
TFT_eSprite sprite = TFT_eSprite(&tft);

void setup(void) {
  tft.begin();
  tft.setRotation(1);
  tft.fillScreen(TFT_BLACK);

  ofr.setDrawer(sprite);
  ofr.loadFont(TTF_FONT, sizeof(TTF_FONT));
}

void loop() { 
  void *out = sprite.createSprite(140, 50);
  if (out == NULL) Serial.println("Could not allocate memory for sprite!");
  sprite.fillSprite(TFT_RED);

  ofr.setFontSize(64);
  ofr.setCursor(sprite.width() / 2, sprite.height() / 2); // Middle of box
  ofr.setAlignment(Align::MiddleCenter);
  ofr.setFontColor(TFT_WHITE, TFT_BLACK);
  ofr.printf("%d", millis());

  int hcenter = (tft.width()  / 2) - (sprite.width()  / 2);
  int vcenter = (tft.height() / 2) - (sprite.height() / 2);

  sprite.pushSprite(hcenter,vcenter);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants