I am trying to draw a Checkerboard pattern on a LCD using a GUI library called emWin. I have actually managed to draw it using the following code. But having this many loops in the program body for a single task, that too in the internal flash of the microcontroller is not a good idea.
For those who have not worked with emWin, I will try and explain a few things before we go for the actual logic.
GUI_RECT
is a structure which id define source files of emWin and I am blind to it.Rect
,Rect2
,Rect3
.. and so on untilRect10
are objects.- Elements of the
Rect
array are{x0,y0,x1,y1}
, wherex0
,y0
are starting locations of the rectangle in the X-Y plane and x1, y1 are end locations of the Rectangle in the X-Y plane.
Rect={0,0,79,79}
is a rectangle that starts at the top left of the LCD and is up to (79,79), so it's basically a square.
- The function
GUI_setBkColor(int color);
sets the color of the background. - The function
GUI_setColor(int color);
sets the color of the foreground. GUI_WHITE
andDM_CHECKERBOARD_COLOR
are two color values,#define
edGUI_FillRectEx(&Rect);
will draw the rectangle.
The code below works fine but I want to make it smarter.
GUI_RECT Rect = {0, 0, 79, 79};
GUI_RECT Rect2 = {80, 0, 159, 79};
GUI_RECT Rect3 = {160, 0, 239, 79};
GUI_RECT Rect4 = {240, 0, 319, 79};
GUI_RECT Rect5 = {320, 0, 399, 79};
GUI_RECT Rect6 = {400, 0, 479, 79};
GUI_RECT Rect7 = {480, 0, 559, 79};
GUI_RECT Rect8 = {560, 0, 639, 79};
GUI_RECT Rect9 = {640, 0, 719, 79};
GUI_RECT Rect10 = {720, 0, 799, 79};
WM_SelectWindow(Win_DM_Main);
GUI_SetBkColor(GUI_BLACK);
GUI_Clear();
for(i = 0; i < 6; i++)
{
if(i%2 == 0)
GUI_SetColor(GUI_WHITE);
else
GUI_SetColor(DM_CHECKERBOARD_COLOR);
GUI_FillRectEx(&Rect);
Rect.y0 += 80;
Rect.y1 += 80;
}
for(i = 0; i < 6; i++)
{
if(i%2 == 0)
GUI_SetColor(DM_CHECKERBOARD_COLOR);
else
GUI_SetColor(GUI_WHITE);
GUI_FillRectEx(&Rect2);
Rect2.y0 += 80;
Rect2.y1 += 80;
}
for(i = 0; i < 6; i++)
{
if(i%2 == 0)
GUI_SetColor(GUI_WHITE);
else
GUI_SetColor(DM_CHECKERBOARD_COLOR);
GUI_FillRectEx(&Rect3);
Rect3.y0 += 80;
Rect3.y1 += 80;
}
for(i = 0; i < 6; i++)
{
if(i%2 == 0)
GUI_SetColor(DM_CHECKERBOARD_COLOR);
else
GUI_SetColor(GUI_WHITE);
GUI_FillRectEx(&Rect4);
Rect4.y0 += 80;
Rect4.y1 += 80;
}
for(i = 0; i < 6; i++)
{
if(i%2 == 0)
GUI_SetColor(GUI_WHITE);
else
GUI_SetColor(DM_CHECKERBOARD_COLOR);
GUI_FillRectEx(&Rect5);
Rect5.y0 += 80;
Rect5.y1 += 80;
}
for(i = 0; i < 6; i++)
{
if(i%2 == 0)
GUI_SetColor(DM_CHECKERBOARD_COLOR);
else
GUI_SetColor(GUI_WHITE);
GUI_FillRectEx(&Rect6);
Rect6.y0 += 80;
Rect6.y1 += 80;
}
for(i = 0; i < 6; i++)
{
if(i%2 == 0)
GUI_SetColor(GUI_WHITE);
else
GUI_SetColor(DM_CHECKERBOARD_COLOR);
GUI_FillRectEx(&Rect7);
Rect7.y0 += 80;
Rect7.y1 += 80;
}
for(i = 0; i < 6; i++)
{
if(i%2 == 0)
GUI_SetColor(DM_CHECKERBOARD_COLOR);
else
GUI_SetColor(GUI_WHITE);
GUI_FillRectEx(&Rect8);
Rect8.y0 += 80;
Rect8.y1 += 80;
}
for(i = 0; i < 6; i++)
{
if(i%2 == 0)
GUI_SetColor(GUI_WHITE);
else
GUI_SetColor(DM_CHECKERBOARD_COLOR);
GUI_FillRectEx(&Rect9);
Rect9.y0 += 80;
Rect9.y1 += 80;
}
for(i = 0; i < 6; i++)
{
if(i%2 == 0)
GUI_SetColor(DM_CHECKERBOARD_COLOR);
else
GUI_SetColor(GUI_WHITE);
GUI_FillRectEx(&Rect10);
Rect10.y0 += 80;
Rect10.y1 += 80;
}
3 Answers 3
It's unlikely that your app's performance concerns are due to the excessive looping. They are all small loops and should rip through pretty quickly, even on an embedded controller. I suspect your expensive call is the GUI_FillRectEx() calls.
That said, there are techniques to clean up your code and make it more maintainable.
- Look for the duplicated logic.
Notice how you have the same loop structure + code for each of those blocks?
for(i = 0; i < 6; i++)
{
if(i%2 == 0)
GUI_SetColor(DM_CHECKERBOARD_COLOR);
else
GUI_SetColor(GUI_WHITE);
While the cost isn't necessarily great in this case, you are repeating it for each of your rectangles.
Look for the actions that don't need to occur.
In this case, do you really need to paint both the white and black squares? Could you "cheat" by using a black canvas and only paint the white squares? That would cut your FillRectEx() calls in half.
Do you really need to declare 10 Rect's and increment coordinates on each loop? How about declaring a fixed array ahead of time and pre-populate / hard code all the coordinates?
-
\$\begingroup\$ Sir, totally agree with you. The same thing occurred to me as well of drawing alternate white squares on a black background, but the requirements are like, I should have a background color other than the two colors used to draw the checkerboard. \$\endgroup\$WedaPashi– WedaPashi2013年06月28日 11:28:18 +00:00Commented Jun 28, 2013 at 11:28
-
\$\begingroup\$ @ GlenH7: I will try and spend some time coding to see if I can manage that with two Rects, each for single color. \$\endgroup\$WedaPashi– WedaPashi2013年06月28日 11:29:33 +00:00Commented Jun 28, 2013 at 11:29
-
1\$\begingroup\$ @wedapashi having two rectangles will simplify the code but will require more calculations in order to complete the task. You need to decide on which path you want to follow. If execution time is critical, minimize calculations. \$\endgroup\$user26710– user267102013年06月28日 13:45:00 +00:00Commented Jun 28, 2013 at 13:45
C-style pseudo-code:
char cellSize = 50;
int cellX,cellY;
Color cellColor;
for ( char row = 0 ; row < 8 ; row++ )
{
for ( char col = 0 ; col < 8 ; col++ )
{
cellX = col*cellSize
cellY = row*cellSize;
cellColor = (row+col)%2 ? COLOR_WHITE : COLOR_BLACK;
drawRect(cellX,cellY,cellSize,cellSize,cellColor);
}
}
I believe the algorithm of drawing a checkerboard is that trivial. Draw cell by cell moving from left to right and from bottom to top and choose color basing on whether the sum of row
and col
is even or odd.
-
\$\begingroup\$ Totally agree with with what you said regarding drawing the checkerboard, but here this function
GUI_FillRectEx
is a library function call, and that I can not change, and I have no other alternative like writing my own function to draw like the onedrawRect(cellX,cellY,cellSize,cellSize,cellSize,cellColor);
you suggested \$\endgroup\$WedaPashi– WedaPashi2013年06月28日 11:24:23 +00:00Commented Jun 28, 2013 at 11:24 -
2\$\begingroup\$
I have no other alternative like writing my own function to draw like the one
In fact it is a very good practice. You should split your code in functions doing exactly one thing. So extracting the functionality of drawing a rectangle from the function drawing the checker-board is what you should do. \$\endgroup\$Kolyunya– Kolyunya2013年06月28日 11:31:52 +00:00Commented Jun 28, 2013 at 11:31
The basic concept behind this pseudo-code is to place the 10 separate loops into one single loop and implement the functionality through array indices.
GUI_RECT Rect = new int[10][4];
int checkerIndex = 0;
for (int groi = 0; groi < 10; groi++)
{
Rect[groi] = {groi*80, 0, (groi+1)*80 - 1, 79};
}
WM_SelectWindow(Win_DM_Main);
GUI_SetBkColor(GUI_BLACK);
GUI_Clear();
for(int groi = 0; groi < 10; groi++)
{
for(i = 0; i < 6; i++)
{
if(i%2 == checkerIndex)
GUI_SetColor(GUI_WHITE);
else
GUI_SetColor(DM_CHECKERBOARD_COLOR);
GUI_FillRectEx(&Rect[groi]);
Rect[groi].y0 += 80;
Rect[groi].y1 += 80;
}
checkerIndex = 1 - checkerIndex;
}
-
\$\begingroup\$ @Jamal sorry, you're right. The basic concept behind this pseudo-code was to place the 10 separate loops into one single loop and implement the functionality through array indices. However I applied a fix to it otherwise the checkerboard pattern would not show. \$\endgroup\$András Hummer– András Hummer2014年07月11日 20:24:37 +00:00Commented Jul 11, 2014 at 20:24
-
\$\begingroup\$ There's other ways to simplify it, for instance,
checkerIndex = 1-checkerIndex;
instead of that monstrosity. \$\endgroup\$Snowbody– Snowbody2014年07月15日 05:38:39 +00:00Commented Jul 15, 2014 at 5:38