cancel
Showing results for 
Search instead for 
Did you mean: 

Display corruption issue when direct drawing to framebuffer in custom widget

Michael K
Senior III

I'm following this guide to creating a custom widget. Instead of any qr code logic, all I'm starting with is just drawing a solid color to the invalidated widget.

void MyWidget::draw(const touchgfx::Rect& invalidatedArea) const {
	touchgfx::Rect absolute = getAbsoluteRect();
 
	uint16_t* framebuffer = touchgfx::HAL::getInstance()->lockFrameBuffer();
	for (int y = invalidatedArea.y; y < invalidatedArea.bottom(); y++)
	{
		for (int x = invalidatedArea.x; x < invalidatedArea.right(); x++)
		{
			framebuffer[absolute.x + x + (absolute.y + y) * touchgfx::HAL::DISPLAY_WIDTH] = 0xFFFF;
		}
	}
 
 
	touchgfx::HAL::getInstance()->unlockFrameBuffer();
}

When I download and run the program, the area in which the widget is supposed to reside does not update when I enter the screen, instead it holds the image of the last screen. I also see three white boxes of interleaving white in a different part of the screen altogether.

As an experiment, I attempted to draw a white 800px line at the top of the screen, and discovered the white line only went 2/3 of the way across. I reason this is because the framebuffer is a 16 bit pointer, and my pixel format is 24 bits.

  1. If this is the case, in my opinion this should be clearly indicated in the linked touchgfx documentation page.
  2. Is there a pixel-format independent way to draw pixels to the framebuffer? As it stands I would have to work out the byte offsets for every x/y pixel, and write 24 bits of data into two adjacent 16 bit cells
  3. Does the STM32F769i support 16 bit pixel format?

Thanks.

4 REPLIES 4
Michael K
Senior III

If anyone stumbles on this in the future, here is a possible solution that worked for me. Reinterpret_cast the framebuffer pointer to an 8-bit pointer, find the base address by multiplying the offset by 3 (8 bits x 3 = 24 bits), and write to it and the two adjacent cells.

uint8_t* framebuffer = reinterpret_cast<uint8_t*>(touchgfx::HAL::getInstance()->lockFrameBuffer());
	for (int y = invalidatedArea.y; y < invalidatedArea.bottom(); y++)
	{
		for (int x = invalidatedArea.x; x < invalidatedArea.right(); x++)
		{
			framebuffer[(absolute.x + x + (absolute.y + y) * touchgfx::HAL::DISPLAY_WIDTH) * 3] = 0xFF;
			framebuffer[(absolute.x + x + (absolute.y + y) * touchgfx::HAL::DISPLAY_WIDTH) * 3 + 1] = 0xFF;
			framebuffer[(absolute.x + x + (absolute.y + y) * touchgfx::HAL::DISPLAY_WIDTH) * 3 + 2] = 0xFF;
		}
	}

Martin KJELDSEN
Chief III

If you're moving to a new screen and you're seeing some old elements, it's typically because you don't have a background that covers the entire framebuffer. You're peeking at the left-over state of the framebuffer that hasn't been overwritten by your new screen.

uint8_t* RESTRICT buf = reinterpret_cast<uint8_t*>(HAL::getInstance()->lockFrameBuffer());

  1. Yes, the framebuffer pointer is 16-bit and you need to interpret it however you need to compared to the depth of your framebuffer. Taking a lock on the framebuffer is advanced usage - The API clearly states that it returns a 16-bit pointer.
  2. No, but you worked it out because you're an advanced user / the right target group! Once you get the pointer, you're on your own.
  3. Yes it does. Are you having issues?

/Martin

Michael K
Senior III

For what it's worth, although my solution above worked, I didn't end up drawing to the framebuffer directly as my implementation was slow and didn't suit my personal application; instead I used fillrect for most of my drawing.

Martin KJELDSEN
Chief III

Usually, you'll only draw directly to the framebuffer if there's something that TouchGFX cannot handle for you. In some cases, it'll even help you to optimize by using the ChromART chip. For instance, if you don't want to pay the price in memory for widgets, or if you need to do some calculations around a certain pixel, etc.

/Martin