7
\$\begingroup\$

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 until Rect10 are objects.
  • Elements of the Rect array are {x0,y0,x1,y1}, where x0, 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 and DM_CHECKERBOARD_COLOR are two color values, #defineed
  • GUI_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;
    }
\$\endgroup\$

migrated from programmers.stackexchange.com Jun 28 '13 at 12:16

This question came from our site for professionals, academics, and students working within the systems development life cycle.

4
\$\begingroup\$

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.

    1. 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.

    2. 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?

\$\endgroup\$
  • \$\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 Jun 28 '13 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 Jun 28 '13 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\$ – GlenH7 Jun 28 '13 at 13:45
3
\$\begingroup\$

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.

\$\endgroup\$
  • \$\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 one drawRect(cellX,cellY,cellSize,cellSize,cellSize,cellColor); you suggested \$\endgroup\$ – WedaPashi Jun 28 '13 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 Jun 28 '13 at 11:31
3
\$\begingroup\$

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;
}
\$\endgroup\$
  • \$\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 Jul 11 '14 at 20:24
  • \$\begingroup\$ There's other ways to simplify it, for instance, checkerIndex = 1-checkerIndex; instead of that monstrosity. \$\endgroup\$ – Snowbody Jul 15 '14 at 5:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.