2024-02-18 09:41 AM - edited 2024-02-20 02:50 PM
I'm looking for right way to draw simple shapes (like lines, circles, rectangles, Bezier curves, etc - just simple editor like oldschool Windows Paint) and studying how to implement this correctly.
I'm believe I should use some kind of canvas (Container or something else?) to draw my geometric objects. Looking at samples and documentations, I have found two methods for adding graphical points to canvas.
1. getAnimationStorage()
void MainView::setupRings()
{
//draw a couple of random colorful rings in the pixel buffer
int red = (int)random(0, 1000);
int green = (int)random(0, 1000);
int blue = (int)random(0, 1000);
int rings = (int)random(0, 10) + 4;
uint8_t* pixelBuffer = (uint8_t*)HAL::getInstance()->getAnimationStorage();
uint32_t offset = 0;
for (int y = 0; y < size; y++)
{
for (int x = 0; x < size; x++, offset += 4)
{
int dx = x - size / 2;
int dy = y - size / 2;
float dist = sqrtf((float)dx * dx + dy * dy);
uint8_t alpha = 0;
if (dist < size / 2)
{
alpha = (uint8_t)(cosf(dist / (size / 2) * rings * 2 * 3.14159f + 3.14159f) * 127 + 128);
}
// Due to endianness, ARGB8888 bytes are written backwards
pixelBuffer[offset + 0] = (uint8_t)(sinf(dist * 2 * (rings + 10) / size + blue) * 127 + 128);
pixelBuffer[offset + 1] = (uint8_t)(sinf(dist * 2 * (rings + 10) / size + green) * 127 + 128);
pixelBuffer[offset + 2] = (uint8_t)(sinf(dist * 2 * (rings + 10) / size + red) * 127 + 128);
pixelBuffer[offset + 3] = alpha;
}
}
}
one could look to full source code by creating Pixel Data Example in TouchGFX examples generator
2. lockFrameBuffer()
void QRCodeWidget::draw(const touchgfx::Rect& invalidatedArea) const
{
if (!data)
{
return;
}
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::FRAME_BUFFER_WIDTH] =
data->at(x / scale, y / scale) ? 0x0000 : 0xffff;
}
}
touchgfx::HAL::getInstance()->unlockFrameBuffer();
}
this is part from Custom Widget documentation.
In 1st case just used `getAnimationStorage` without any kind of `unlock` as in 2nd one.
So what is difference between them and what you could recommend to use in simple Painter-like application where user could add, modify, delete sample graphical objects (I use cross-cursor as pointer, user could move it to set location or input length, height, width, etc of shape).
Thank you!
2024-02-18 11:32 AM - edited 2024-02-18 11:47 AM
Hello
Im not sure can I answer your question about those two methods, but I would also consider to use of Dynamic bitmaps.
Here is simple example how to use those:
And the documentation:
Especially if you want to use also other widgets same time in some part of the screen, show some modal selection popups etc it would be much easier since dynamic bitmap is one of the widgets.
Br JTP
2024-02-19 01:45 PM
Interesting proposal, I will try
Should I multiply width, height and working with buffer multiplying everything by 3 in case I have 24 bit display?
Thank you
2024-02-19 10:33 PM
Hello
Basically yes, in the dynamic bitmap example the color depth is 16bit so so there multiplier is two when calculating size in bytes.
Note also to use 8 bit pointer when manipulating data in the memory. Just write then 3 bytes (rgb) for each pixel. And when using 8 bit pointer, remember to multiply with 3 when converting x and y coordinates to address offset.
Br JTP
2024-02-20 01:49 AM
I have tried, but since these all a bit difficult to understand for me, result is not I expect.
Modified code for try 24 bit
void screenView::setupScreen()
{
screenViewBase::setupScreen();
imgCursor.setPosition(cursorX, cursorY, cursorL, cursorL);
bmpId = Bitmap::dynamicBitmapCreate(dynamicBitmapWidth, dynamicBitmapHeight, Bitmap::RGB888);
// some random color pattern
uint8_t *cache_ptr;
uint32_t i;
uint32_t color;
color=0; // I'd like to iterate from 0x000000 black to 0xFFFFFF white
cache_ptr=(uint8_t *)Bitmap::dynamicBitmapGetAddress(bmpId);
for(i=0;i<dynamicBitmapWidth*dynamicBitmapHeight;i++)
{
*cache_ptr=color;
cache_ptr++;
*cache_ptr=color;
cache_ptr++;
*cache_ptr=color;
cache_ptr++;
color++;
}
image1.setBitmap(Bitmap(bmpId));
image1.setXY(20, 20);
image1.setWidth(dynamicBitmapWidth);
image1.setHeight(dynamicBitmapHeight);
image1.invalidate();
}
2024-02-20 03:01 AM - edited 2024-02-20 03:02 AM
What the result looks like ? It should be complete mess since the color is overflowing all the time.
If you put something like
If((i%dynamicBitmapHeight)==0)color++;
Instead of increment color with every pixel, you should get colored lines.(start from black, shades of grey).
Br JTP
2024-02-20 05:16 AM
As I mentioned in comment, I'd like to obtain some thing like RGB-color picker, from 0x000000 to 0xFFFFFF HTML colors.
so I need to increment by 1 for get next RGB color for each following pixel.
Is I'm right understand that for each position when increment pointer by 1 and 2, I should write same color as without increment? or for each increment I should set different part of color: R, G for increment 1 and B for increment 2?
Is this way is correct (same color for each increment for single pixel at display)? or I should split color to R, G, B for each pixel?
for(i=0;i<dynamicBitmapWidth*dynamicBitmapHeight;i++)
{
*cache_ptr=color;
cache_ptr++;
*cache_ptr=color;
cache_ptr++;
*cache_ptr=color;
cache_ptr++;
color++;
}
2024-02-20 09:16 AM
Hello
To get this kind of color scale, it needs much more complicated code. This is the way how color components should vary.
This is probably the slowest and dummiest impementation of this color slide, but as an example:
void Screen1View::setupScreen()
{
uint8_t widthPer6,colStep,phase=0;
uint8_t *cache_ptr;
uint32_t i;
int16_t red=0,green=0,blue=0;
Screen1ViewBase::setupScreen();
// REMOVE const from dynamicBitmapWidth at view.hpp !!
// color slide works best when 256/(width/6) is integer, so use 96,192,384 as dynamicBitmapWidth !!
// make dynamicBitmapWidth divisible with 6
dynamicBitmapWidth-=(dynamicBitmapWidth%6);
// to avoid div-by-zero later
if(dynamicBitmapWidth>=6)
{
bmpId = Bitmap::dynamicBitmapCreate(dynamicBitmapWidth, dynamicBitmapHeight, Bitmap::RGB888);
// divide line to 6 areas
widthPer6=(dynamicBitmapWidth/6);
// calculate color step
colStep=(256/widthPer6);
// init pointer to bitmap
cache_ptr=(uint8_t *)Bitmap::dynamicBitmapGetAddress(bmpId);
for(i=0;i<(dynamicBitmapWidth*dynamicBitmapHeight);i++)
{
// check color stepping boundaries when not in the first pixel
if(i>0)
{
if((i%dynamicBitmapWidth)==0)phase=0;
else if((i%widthPer6)==0)phase++;
}
// make increments/decrements
switch(phase)
{
case 0:
red=0xff;
blue=0;
green+=colStep;
if(green>0xff)green=0xff;
break;
case 1:
red-=colStep;
if(red<0)red=0;
blue=0;
green=0xff;
break;
case 2:
red=0;
blue+=colStep;
if(blue>0xff)blue=0xff;
green=0xff;
break;
case 3:
red=0;
blue=0xff;
green-=colStep;
if(green<0)green=0;
break;
case 4:
red+=colStep;
if(red>0xff)red=0xff;
blue=0xff;
green=0;
break;
case 5:
red+=0xff;
blue-=colStep;
if(blue<0)blue=0;
green=0;
break;
}
// finally draw the pixel
*cache_ptr=blue;
cache_ptr++;
*cache_ptr=green;
cache_ptr++;
*cache_ptr=red;
cache_ptr++;
}
//Make Image widget show the dynamic bitmap
image1.setBitmap(Bitmap(bmpId));
//Position image and add to View
image1.setXY(20, 20);
}
}
This is how it looks then in simulator (width is 384 pixel)
Maybe you can develop yourself the vertical slide
Br JTP
2024-02-20 02:49 PM
Wow! Thank you very much!
And I understand right by looking to your code, it is need to split RGB to 3 parts for each 3 positions , by setting blue at 1st, green at 2nd and finally red ad last position?
// finally draw the pixel
*cache_ptr=blue;
cache_ptr++;
*cache_ptr=green;
cache_ptr++;
*cache_ptr=red;
cache_ptr++;
2024-05-16 07:19 AM
This conversation have continued on this thread : https://community.st.com/t5/stm32-mcus-touchgfx-and-gui/how-to-drawing-geometric-shapes-on-dynamic-bitmap-suggestions/m-p/646940#M36543