Jump to content

small frame buffer


miogui

Recommended Posts

Hi,

I'm working on a project on Chibios/ugfx with a STM32F407 and an SSD1961.

I've got a background picture (BMP) for the entire screen, and I try to update a String on this.

for the moment, on update I do the following :

- gdispGImageDraw on the area where the String is supposed to be .

- gdispGDrawString with the new string.

But this is not perfect...

Is there a way to allocate a small buffer for the area that need to be redraw, write image and text, and then send it to the screen ?

thanks

Guillaume

Link to comment
Share on other sites

Hello miogui and welcome to the community!

The correct way to write a buffer to the display is by using gdispBlitArea() which takes a pointer to a buffer (see gdispBlitArea() API documentation).

The function is implemented so it always uses the fastest way possible. If your display supports hardware fills, it will use that. If your display driver support hardware streaming, it will use it. You can take a look at the implementation here.

I hope that helps.

~ Tectu

Link to comment
Share on other sites

Ugfx doesn't currently support drawing to an off-screen pixmap. There is a work around however that will enable this to be done.

ugfx supports multiple displays. It would be possible to create a board file for the framebuffer driver for the off-screen pixmap and treat it as if it was a secondary display. If you set the pixel format of that pixmap to match your real display you can then use gdispGBlitArea to transfer that to your real display.

Using the new dynamic driver registration api it would also be possible to do this at run - time. In fact it is our intention to eventually add an api for creating off-screen pixmap drawing using exactly this method. Unfortunately this is however lower on our agenda so if you go to this extent please contribute it back as it would be useful for the community.

Link to comment
Share on other sites

Hi inmarket & tectu

Thanks to your reply, I'm now able to write in the off-screen buffer and refresh the screen on this aera.

This help me to use a picture as background..

here is the diff I made to be able to do this

index 5c20d3d..f0c52c5
--- a/drivers/gdisp/SSD1963/gdisp_lld_SSD1963.c
+++ b/drivers/gdisp/SSD1963/gdisp_lld_SSD1963.c
@@ -13,6 +13,7 @@
#include "drivers/gdisp/SSD1963/gdisp_lld_config.h"
#include "src/gdisp/driver.h"

+
#define CALC_PERIOD(w,b,f,p) (p+b+w+f)
#define CALC_FPR(w,h,hb,hf,hp,vb,vf,vp,fps) ((fps * CALC_PERIOD(w,hb,hf,hp) * CALC_PERIOD(h,vb,vf,vp) * 1048576

@@ -46,7 +47,7 @@ typedef struct LCD_Parameters {
#define LCD_PANEL_TYPE_SERIAL_RGB_DUMMY_MODE (((1<<5) | (1<<6)) << 8) // Serial RGB+dummy
} LCD_Parameters;

-#include "board_SSD1963.h"
+#include "gdisp_lld_board_st_stm32f4_discovery.h"

/*===========================================================================*/
/* Driver local definitions. */
@@ -81,8 +82,8 @@ static inline void set_viewport(GDisplay* g) {
write_data16(g, g->p.x);
write_data16(g, g->p.x+g->p.cx-1);
write_index(g, SSD1963_SET_PAGE_ADDRESS);
- write_data16(g, g->p.y);
- write_data16(g, g->p.y+g->p.cy-1);
+ write_data16(g, g->frame_buffer*GDISP_SCREEN_HEIGHT+g->p.y);
+ write_data16(g, g->frame_buffer*GDISP_SCREEN_HEIGHT+g->p.y+g->p.cy-1);
write_index(g, SSD1963_WRITE_MEMORY_START);
break;
case GDISP_ROTATE_90:
@@ -142,6 +143,12 @@ static inline void set_backlight(GDisplay *g, uint8_t percent) {
write_data(g, 0x0F); // Brightness prescaler - active when Trans
}

+
+
+
+
+
+
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
@@ -230,6 +237,19 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
write_data(g, 0x00);
#endif

+ /*write scroll aera*/
+ write_index(g,SSD1963_SET_SCROLL_AREA); //set PWM for BackLight
+ write_data(g,( 0 >> 8) & 0xFF); //top
+ write_data(g,( 0 >> 0) & 0xFF);
+ write_data(g,( 272 >> 8) & 0xFF); //scroll
+ write_data(g,( 272 >> 0) & 0xFF);
+ write_data(g,( 0 >> 8) & 0xFF); //bottom
+ write_data(g,( 0 >> 0) & 0xFF);
+
+ write_index(g,SSD1963_SET_SCROLL_START);
+ write_data(g,( 0 >> 8) & 0xFF);
+ write_data(g,( 0 >> 0) & 0xFF);
+
/* Tear effect indicator ON. This is used to tell the host MCU when the driver is not refreshing the panel
write_reg(g, SSD1963_SET_TEAR_ON, 0x00);

@@ -239,6 +259,8 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
/* Turn on the back-light */
set_backlight(g, GDISP_INITIAL_BACKLIGHT);

+
+
// Finish Init
post_init_board(g);

@@ -246,6 +268,8 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
release_bus(g);

/* Initialise the GDISP structure */
+ g->frame_display = 0;
+ g->frame_buffer = 0;
g->g.Width = lcdp->width;
g->g.Height = lcdp->height;
g->g.Orientation = GDISP_ROTATE_0;
@@ -268,6 +292,8 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
}
#endif

+
+
#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL
LLDSPEC void gdisp_lld_control(GDisplay *g) {
switch(g->p.x) {
@@ -363,6 +389,23 @@ LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
g->g.Backlight = (unsigned)g->p.ptr;
return;

+ case GDISP_CONTROL_FRAME:
+ if (g->frame_display == (unsigned)g->p.ptr)
+ return;
+ else
+ {
+ acquire_bus(g);
+ write_index(g,SSD1963_SET_SCROLL_START);
+ write_data(g,( ((unsigned)g->p.ptr * GDISP_SCREEN_HEIGHT) >> 8) & 0xFF);
+ write_data(g,( ((unsigned)g->p.ptr * GDISP_SCREEN_HEIGHT) >> 0) & 0xFF);
+ release_bus(g);
+ g->frame_display = (unsigned)g->p.ptr;
+ }
+ return;
+
+ case GDISP_CONTROL_FRAME_BUFFER:
+ g->frame_buffer = (unsigned)g->p.ptr;
+ return;
//case GDISP_CONTROL_CONTRAST: - see DDS1963_SET_POST_PROC command - contrast, brightness, saturati
default:
return;

index 8d22135..42b4524
--- a/src/gdisp/driver.h
+++ b/src/gdisp/driver.h
@@ -237,6 +237,9 @@ struct GDisplay {
uint8_t Contrast;
} g;

+ uint8_t frame_display;
+ uint8_t frame_buffer;
+
void * priv; // A private area just for
void * board; // A private area just for

index f952e41..6321be0
--- a/src/gdisp/sys_defs.h
+++ b/src/gdisp/sys_defs.h
@@ -105,6 +105,8 @@ extern GDisplay *GDISP;
#define GDISP_CONTROL_ORIENTATION 1
#define GDISP_CONTROL_BACKLIGHT 2
#define GDISP_CONTROL_CONTRAST 3
+#define GDISP_CONTROL_FRAME 4
+#define GDISP_CONTROL_FRAME_BUFFER 5
#define GDISP_CONTROL_LLD 1000

/*===========================================================================*/
@@ -1067,6 +1069,12 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co
#define gdispGSetOrientation(g, newOrientation) gdispGControl((g), GDISP_CONTROL_ORIENTATION, (void
#define gdispSetOrientation(newOrientation) gdispGControl(GDISP, GDISP_CONTROL_ORIENTATION, (vo

+#define gdispGSetFrame(g, newFrame) gdispGControl((g), GDISP_CONTROL_FRAME, (void *)(unsigned)(
+#define gdispSetFrame(newFrame) gdispGControl(GDISP, GDISP_CONTROL_FRAME, (void *)(
+
+#define gdispGSetFrameBuffer(g, newFrame) gdispGControl((g), GDISP_CONTROL_FRAME_BUFFER, (voi
+#define gdispSetFrameBuffer(newFrame) gdispGControl(GDISP, GDISP_CONTROL_FRAME_BUFFER, (v
+
/**
* @brief Set the display backlight.
* @note Ignored if not supported by the display.

Link to comment
Share on other sites

and then,

If you just have to write the following:

  • g->frame_buffer (represent which frame Gdisp will write)

  • g->frame_display (represent which frame the LCD will display)

with:

  • gdispSetFrameBuffer(NB FRAME);
  • gdispSetFrame(NB FRAME);

for example :


// Set the Display to show the frame 0
gdispSetFrame(0);


// Write ON-SCREEN frame buffer
gdispSetFrameBuffer(0);
gdispClear(Black);
gdispFillArea(30, 30, 300, 150, Red);
gdispFillArea(50, 50, 200, 100, Blue);

// Write OFF-SCREEN frame buffer
gdispSetFrameBuffer(1);
gdispClear(Black);
gdispFillArea(80, 80, 150, 50, Yellow);
gdispFillArea(200, 130, 100, 100, Green);

// Switch the Display to show the OFF-SCREEN frame
gdispSetFrame(1);

Maybe this could be an entrie for your competition http://ugfx.org/competitions/competition-widget-drawing ??

Link to comment
Share on other sites

Thank-you for this excellent contribution.

I will integrate it (possibly with a few minor changes) into the master repository over the next week or so.

Unfortunately this doesn't meet the requirements for the competition which is really about the GWIN widget set. We would however be very interested in your ideas for a new competition. Please post your idea's in this thread http://ugfx.org/forum/viewtopic.php?f=25&t=123

Link to comment
Share on other sites

Thanks,

Pixmap look cool.. and look like what I want to achieve, but basicaly it don't use BMP picture ..

I've managed to duplicate the gdispimagecache,

This new function will help to cache the aera needed instead the whole part....

GDsipImageDraw will be able to display the cached area, and if the cache fails work exactly as now (access and decode the picture, and just display the aera needed by sx,sy,cx,cy)

thanks for the info

Link to comment
Share on other sites

Pixmap can be used to cache anything you want to draw on it including images (bmp, gif etc) or lines, text or anything else.

The pixmap image call is to enable you to save the pixmap to a file as a native format image and other such image related purposes. The gdispGetPixmapBits call is the one normally used to get the display surface when you are ready to transfer it to the real screen. Note that pixmaps are much more efficient and faster than even image caching.

Link to comment
Share on other sites

Ok,

I've just look the demo, but if the Pixmap do that , I will have a try ...

One point that could be ehanced is the GdsipImageDraw_BMP with a cx,cy, sx,sy lower than the img-Width and img-Height:

This routine read the entire priv->buf and "blit" only the area needed, I'm looking to speed up that we a small trick ..

Link to comment
Share on other sites

  • 2 months later...

Is it possible to do this partially? Given that I want to divide a 128*128 display into four pages, like so


+------------+
| | page 0 (128*32 pixels)
|____________|
| | page 1
|____________|
| | page 2
|____________|
| | page 3
|____________|

I want ugfx to only draw on one page at a time, and then send that page to the display. When that has been done, ugfx may draw on the next page. To get a full frame, all drawing operations have to be done four times.

I could try to implement this in the driver, but I think it would be quite hard to have the driver force ugfx into clipping all drawing operations to the current page. OTOH, the driver could do the clipping for pixel writes and reads, and apply a coordinate conversion when accessing its private buffer. That feels like reinventing a lot of wheels.

The pixmap method suggested here seems to use a pixmap that has a fixed coordinate system (starting at 0,0). Can I change that coordinate system instead and then blit the pixmap to my display? The pixmap would first occupy page 0, then page 1, and so on.

Link to comment
Share on other sites

Pixmaps are interesting in that they have a dual personality. On one hand they look like a full display (origin =0, 0). On the other hand they look like a native format image. A native format image can be blitted to a real display at any position using gdispGBlitArea.

I suspect therefore that this will do what you want. initialise it as 64x64 (1/4 the size of the real display). Draw to it as if it is a it is a 64x64 display and then blit it to each quarter of the real display as required.

Link to comment
Share on other sites

I don't think we are having the same mental model of this. There is still one important thing missing:

The pixmap has a fixed coordinate system. When I draw to a pixmap that represents page 0, that page has the origin (0,0) and represents the refion (0,0) (128,32). This is exactly what the page represents. The next page has for example the origin (0,32) as shown in the ascii drawing above. Therefore all drawing operations have to be clipped to the region (0,32) (128,64). This is outside of the pixmap's fixed coordinate system, and nothing will be drawn on the pixmap.

If I can shift all drawing operations by a fixed offset vector that would certainly help. If that is not possible I'd have to implement a driver that provides pixel write and read functions and a private page buffer that is drawn on. I'd still have to apply clipping either in the driver or (probably more efficient) by using gdispSetClip to set the current page region.

The steps to take for a full frame would be:

  • reset page buffer, set driver's internal offset vector to (0,0), set ugfx clipping to (0,0) (128,32)
  • draw all elements (with clipping applied)
  • blit page to the display (it will now display 1/4 of a full frame)
  • reset page buffer, set driver's internal offset to (0,32), set ugfx clipping to (0,32) (128,64)
  • draw all elements
  • blit page to the display (it will now display 1/2 of a full frame)
  • reset page buffer, set driver's internal offset to (0,64), set ugfx clipping to (0,64) (128,96)
  • draw all elements
  • blit page to the display (it will now display 3/4 of a full frame)
  • reset page buffer, set driver's internal offset to (0,96), set ugfx clipping to (0,96) (128,128)
  • draw all elements
  • blit page to the display (it will now display a full frame)

The clipping vectors above are specified as the corners of the active region, not as point and width/height.

Link to comment
Share on other sites

I've seen the suggestion here to use pixmaps instead of a framebuffer, so I tried to get paging by using a pixmap. I know those are different things, but I also hoped that I could get around writing a different driver.

Setting the clip on the real display is dangerous, because clipping should be free for the software to use in situations where drawing needs to be clipped form a graphical point of view, not because the hardware requires it. If some drawing routine uses clipping for its own purposes that would mess with my driver. I guess I'll just try to implement paging in the driver.

Link to comment
Share on other sites

I'm not understanding why paging is needed at all. Why not just draw the lot in one pass?

Adding paging of any sort is a big drain on available ram.

The only reasons I can think for paging is to smooth drawing artifacts in high motion situations -like video. In those situations I am not sure paging the way we have been talking about it is the right approach. For something like video the gdisp streaming api would be much more efficient.

Link to comment
Share on other sites

By the way, none of the gdisp api will alter the clipping region (except gdispSetClip). The only place that currently uses api level clipping in the current ugfx is the gwin api which uses it is clip drawing operations on a window to the window area.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...