arduino / ArduinoCore-mbed

330 stars 195 forks source link

Proper way to use Arduino_H7_Video with ArduinoGraphics.h on Giga Display Shield #936

Closed KurtE closed 4 weeks ago

KurtE commented 1 month ago

Sorry, I am trying to understand, how the code integration of these two classes is supposed to work.

Background, I am playing with an Image viewer sketch (BMP, PNG, JPEG), that I use as part of testing of different display libraries I work with... Including for the fun of it on the GIGA Display Shield. I was first using the Adafruit GFX which worked, but wondered about lower overhead version, so thought I would try this approach. A version of it is up at: https://github.com/KurtE/Arduino_GIGA-stuff/tree/main/sketches/tft_picture_view_sd_giga_shield_24Bit

I am mainly trying to understand the expectations of calls to these two methods:

void Arduino_H7_Video::beginDraw() {
  ArduinoGraphics::beginDraw();

  dsi_lcdClear(0); 
}

void Arduino_H7_Video::endDraw() {
  ArduinoGraphics::endDraw();

  dsi_drawCurrentFrameBuffer();
}

I understand: If I call beginDraw() it sets the entire screen to black. And if I call endDraw() - it sends the current frame buffer to display.

Now suppose at startup of the sketch I may want to write three lines of text on the screen as the sketch checks to see if an SDCard or USB Drive are ready... This can take over several seconds, so how do I incrementally write the text to the screen?

Simpler sketch to demonstrate:

#include <elapsedMillis.h>

#include "Arduino_H7_Video.h"
#include "ArduinoGraphics.h"

REDIRECT_STDOUT_TO(Serial)

Arduino_H7_Video Display(800, 480, GigaDisplayShield);

void fillScreen(uint8_t r, uint8_t g, uint8_t b, bool begin_end = true) {

    if (begin_end) Display.beginDraw();
    Display.background(r, g, b);
    Display.clear();
    if (begin_end) Display.endDraw();
}

//****************************************************************************
// Setup
//****************************************************************************
void setup(void) {
    Serial.begin(115200);
    while (!Serial && millis() < 3000)
        Serial.println("*** start up display ***");
    Display.begin();

    //tft.setRotation(1);

    fillScreen(0xff, 0, 0, true);  // experiment going direct
    //tft.fillScreen(RED);
    delay(500);
    fillScreen(0, 0xff, 0, true);
    //tft.fillScreen(GREEN);
    delay(500);
    fillScreen(0, 0, 0xff, true);
    //tft.fillScreen(BLUE);
    delay(500);

    Display.beginDraw();
    fillScreen(0, 0, 0xff, false);
    Display.textFont(Font_5x7);
    //Display.textSize(5, 5);
    Display.setTextSize(5);
    Display.stroke(0xff, 0, 0);
    Display.background(0, 0, 0xff);
    Display.text("Waiting for SD or USB", 0, 0);
    Display.endDraw();

    delay(2000);
    //Display.beginDraw();
    Display.text("SD Started", 100, 100);
    Display.endDraw();

    delay(2000);
   // Display.beginDraw();
    Display.text("USB Started", 100, 200);
    Display.endDraw();
    delay(2000);

    fillScreen(0xff, 0, 0, true);
    Display.beginText(100, 300);
    Display.print("Here is some Text");
    Display.textScrollSpeed(100);
    Display.endText(SCROLL_LEFT);
}

void loop() {
}

The first line comes out fine: Waiting for SD or USB

Now if it finds the SD and I display: SD Started If first I call beginDraw Only the new text is displayed and rest of the screen is black. If I don't call beginDraw, the new text is the only text on screen, and the background is red.

Now if I then try to output the third line: USB Started Again if I call beginDraw - it is the only thing on screen with background black. If I don't call beginDraw: both the first line (Waiting...) and this line are displayed with Red bacground, but not 2nd line.

Pretty sure: double buffering (two buffers that alternate).

The last part of this test sketch tried the beginText, write, endText to see if an update to an enahancement to the ArduinoGraphics library to allow the font to be scaled works.
https://github.com/arduino-libraries/ArduinoGraphics/pull/45

It appears to, however the code in endText for scrolling like the left that I tried:

  if (scrollDirection == SCROLL_LEFT) {
    int scrollLength = _textBuffer.length() * textFontWidth() + _textX;

    for (int i = 0; i < scrollLength; i++) {
      beginDraw();
      int const text_x = _textX - i;
      text(_textBuffer, text_x, _textY);
      scaledBitmap(_font->data[0x20], text_x - 1, _textY, 1, _font->height, _textsize_x, _textsize_y);
      endDraw();

      delay(_textScrollSpeed);
    }

Appears like it maybe relies on screen being erased? Otherwise when the code draws it one pixel farther to the left, it would leave a smear with the pixels that were on the right hand edge... Sort of like this library assumes only one thing on it at a time. So at a minimum I would think that if beginDraw is going to erase the screen it should do so to the currently selected background color.

Sorry I hope this all makes sense.

KurtE commented 1 month ago

Note: in my own sketch mentioned above, I have locally updated it to have a helper function which outputs the display the current startup status:

void show_updated_startup_status() {
    Display.beginDraw();
    fillScreen(0, 0, 0xff, false);
    Display.textFont(Font_5x7);
    //Display.textSize(5, 5);
    Display.setTextSize(5);
    Display.stroke(0xff, 0, 0);
    Display.background(0, 0, 0xff);
    Display.text("Waiting for SD or USB", 0, 0);

    if (g_devices_started & SD_DRIVE) {
        Display.text("SD Started", 100, 100);
    }

    if (g_devices_started & USB_DRIVE) {
        Display.text("USB Started", 100, 200);
    }

    Display.endDraw();

}
leonardocavagnis commented 1 month ago

Dear @KurtE, The screens built with the ArduinoGraphics library are not designed to be "incrementally" modified. Each screen is independent of the previous one and must be enclosed by the following primitives:

  Display.beginDraw();
  // draw something
  Display.endDraw();

Note: The beginDraw function clears the image buffer and sets the background to black (dsi_lcdClear(0), where 0 corresponds to black).

In your example, if you want to update the screen incrementally, you need to redraw the entire previous screen each time, adding the new elements to it:

  Display.beginDraw();
  fillScreen(0, 0, 0xff, false);
  Display.textFont(Font_5x7);
  Display.setTextSize(5);
  Display.stroke(0xff, 0, 0);
  Display.background(0, 0, 0xff);
  Display.text("Waiting for SD or USB", 0, 0);
  Display.endDraw();

  delay(2000);

  Display.beginDraw();
  fillScreen(0, 0, 0xff, false);
  Display.textFont(Font_5x7);
  Display.setTextSize(5);
  Display.stroke(0xff, 0, 0);
  Display.background(0, 0, 0xff);
  Display.text("Waiting for SD or USB", 0, 0);
  Display.text("SD Started", 100, 100); // new element
  Display.endDraw();

  delay(2000);

  Display.beginDraw();
  fillScreen(0, 0, 0xff, false);
  Display.textFont(Font_5x7);
  Display.setTextSize(5);
  Display.stroke(0xff, 0, 0);
  Display.background(0, 0, 0xff);
  Display.text("Waiting for SD or USB", 0, 0);
  Display.text("SD Started", 100, 100);
  Display.text("USB Started", 100, 200); // new element
  Display.endDraw();

I understand this isn't the most efficient approach, but this library is designed for drawing simple and static screens. If you need to create more complex screens, I recommend using one of the third-party libraries compatible with the Arduino H7 Video library (LVGL or emWin).

I tested the scrolled text example (with the appropriate primitives) and I didn’t encounter the issue you mentioned:

    Display.beginDraw();
    Display.beginText(100, 300);
    Display.print("Here is some Text");
    Display.textScrollSpeed(100);
    Display.endText(SCROLL_LEFT);
    Display.endDraw();

Could you please provide more details and attach some pictures?

KurtE commented 1 month ago

I tested the scrolled text example (with the appropriate primitives) and I didn’t encounter the issue you mentioned:

Display.beginDraw();
Display.beginText(100, 300);
Display.print("Here is some Text");
Display.textScrollSpeed(100);
Display.endText(SCROLL_LEFT);
Display.endDraw();

Could you please provide more details and attach some pictures?

Suppose I want the text to scroll on a blue background? With Red text? I believe the sketch mentioned had higher up in it

Note: I have a related Pull request/Issue over on the ArduinoGraphics github library. https://github.com/arduino-libraries/ArduinoGraphics/pull/45 Which added support to scale the text up to some size you can actually see on the display...

    Display.beginDraw();
    Display.stroke(0xff, 0, 0);
    Display.background(0, 0, 0xff);
    Display.beginText(100, 300);
    Display.print("Here is some Text");
    Display.textScrollSpeed(100);
    Display.endText(SCROLL_LEFT);
    Display.endDraw();

Note: the calls here to beginDraw/endDraw are sort of overdone by the scroll text code...

Understand that this library is really basic... Just thought some of it should be documented, like the dual buffers.

Also wondered about, if the beginDraw() should clear the buffer to black? Or should potentially respect the background color setting?

Anyway I am just playing. Was curious about how well the display would look if I sent it images with 24 bit colors... Most of the GFX like libraries are mostly setup around 16 bit color (RGB565), so I went down to this level. Mostly I am playing with other displays (ILI948x, RA8876, NT35510, ...) on Teensy 4.x (including some custom variants).

And thought it might be good to document the things I ran into.

leonardocavagnis commented 4 weeks ago

Note: I have a related Pull request/Issue over on the ArduinoGraphics github library. arduino-libraries/ArduinoGraphics#45

Yes, I reviewed it. Thanks

Note: the calls here to beginDraw/endDraw are sort of overdone by the scroll text code...

Sure, scrolled text already uses beginDraw()/endDraw() inside the endText() function. It should be redesigned to be compliant with the H7 Video library. Feel free to propose your fix :)

Also wondered about, if the beginDraw() should clear the buffer to black? Or should potentially respect the background color setting?

beginDraw restores the buffer to a "clean state". We defined the "clean state" as a black background.

KurtE commented 4 weeks ago

Feel free to close...

beginDraw restores the buffer to a "clean state". We defined the "clean state" as a black background. The document for the library: https://reference.arduino.cc/reference/en/libraries/arduinographics/begindraw/ Just says: Begins a drawing operation. The endDraw has a bit more description: Ends a drawing operation, any drawing operations after beginDraw() is called will be displayed to the screen.

Neither describe, what happens to other stuff that might have been on the screen before...

As for what is a "clean state", your description works, alternative might be to clear the screen to the currently defined background color... As this would keep users from having the overhead of clearing the whole screen buffer twice. But probably not a high priority thing. There are many other things with the Arduino and MBED and GIGA that I wish would be improved. LIke, with the program I am playing with, why is reading an image off of an SDCard several times faster than reading it off of a USB Host memory stick? Buffered output on Serial ports, ...

Thanks again Kurt

leonardocavagnis commented 4 weeks ago

Feel free to open as many PRs you want to improve mbed and Giga :)