Draw Surface Widget Documentation

Last modified by Microchip on 2024/12/12 12:14

Introduction

The Draw Surface Widget document covers:

  1. Creating and customizing Draw Surface widgets using Microchip Graphics Suite (MGS) Harmony Composer.
  2. Draw Surface widget properties.
  3. Handling Draw Surface widget properties and events through application code.
  4. Draw Surface widget example project.
  5. Application Programming Interfaces (APIs) specific to Draw Surface widgets.

Before following this training, ensure that you are familiar with the process of designing with MGS Harmony Composer, generating code with MPLAB® Code Configurator (MCC) and debugging with MPLAB X IDE. To learn more about this, refer to the "Getting Started with Microchip Graphics Suite (MGS) Harmony" page.

Designing Draw Surface Widgets Using MGS Harmony Composer

Follow these steps to add a Draw Surface widget to your design:

In the Graphic Composer, locate the DrawSurfaceWidget within the Toolbox on the left panel. Drag and position this widget to your preferred location on the screen.

Drag "DrawSurface" widget from Tool Box to your screen

Figure 1: Select Draw Surface Widget

Back to Top


Enable the Draw Notification event:

Enable Draw Notification

Figure 2: Enable Draw Notification

 

Back to Top

Managing Draw Surface Widget Scheme

Schemes control the look and feel of a widget.

Note: To learn more about Schemes, like how to add a new scheme and apply it to the widget, refer to the "About the Schemes and Scheme Editor" page.

To learn how to set the scheme for Draw Surface widget, click on the ? (question mark) button next to the Scheme Property editor of the Object Editor.

Scheme Editor help button

Figure 3: Scheme help button from Object Editor.

This will launch the Draw Surface Scheme Helper window containing tips on how different sections of the scheme settings manipulate various elements of the Image Sequence widget.

Scheme Helper

Figure 4: Draw Surface Scheme Helper.

By default, the border is set to none, making only the Base scheme color visible.

Figure 5 shows an example color scheme and the resulting Draw Surface appearance when border is set to line. In this case, the Shadow Dark color will represent the line color.

Draw Surface scheme example

Figure 5: Draw Surface line border scheme example

Figure 6 shows an example color scheme and the resulting Draw Surface appearance when the border is set to bevel.

Scheme Helper bevel border example

Figure 6: Draw Surface bevel border scheme example

The information from the Draw Surface Scheme helper and the example color scheme is summarized in Figure 7.

Scheme Helper example colors

Figure 7: Description of Scheme Helper.

Note: Understanding how the various segments of the color table influence the appearance of the image assists you in selecting the appropriate colors.

Back to Top

Draw Surface Properties

Name

This will be used to reference the Draw Surface widget by the application. Example - Screen0_DrawSurfaceWidget_0.
Draw Notification

Enabling this will generate an event when the Draw Surface needs to paint. 

Table 1: Draw Surface Properties

Note: Widget properties specific to Draw Surface widget were discussed. For the properties common to all widgets, refer to the "Base Widget Documentation" page.

Back to Top

Managing Draw Surface Widget Through Programming

Once the graphical design is completed using MGS Composer, MCC generates the required code for all the widgets based on the properties set in the Object Editor. To learn more about the process flow between designing a User Interface (UI) and developing application code, refer to the "Designing an Application with Microchip Graphics Suite (MGS)" page.

Let us suppose that a Draw Surface widget is created with the following properties in MGS Composer:

Draw Surface example

Figure 8: Draw Surface Widget Object Editor

For the Draw Surface widget with the properties shown in Figure 8, MCC will automatically generate the following lines of code in src\confg\default\gfx\legato\generated\screen\le_gen_screen_Screen0.c:

  • A new Draw Surface widget is created by the variable name Screen0_DrawSurfaceWidget_0:

 Screen0_DrawSurfaceWidget_0 = leDrawSurfaceWidget_New();

  • Its position is set to pixel location 20x40:

 Screen0_DrawSurfaceWidget_0->fn->setPosition(Screen0_DrawSurfaceWidget_0, 20, 40);

  • The drawing surface size is set to have a width of 760 pixels and a height of 400 pixels:

Screen0_DrawSurfaceWidget_0->fn->setSize(Screen0_DrawSurfaceWidget_0, 760, 400);

  • Its scheme is set to WhiteScheme:

Screen0_DrawSurfaceWidget_0->fn->setScheme(Screen0_DrawSurfaceWidget_0, &WhiteScheme);

  • The border type is set to line:

Screen0_DrawSurfaceWidget_0->fn->setBorderType(Screen0_DrawSurfaceWidget_0, LE_WIDGET_BORDER_LINE);

  • The draw notification event is set to event_Screen0_DrawSurfaceWidget_0_OnDraw:

Screen0_DrawSurfaceWidget_0->fn->setDrawCallback(Screen0_DrawSurfaceWidget_0, event_Screen0_DrawSurfaceWidget_0_OnDraw);

  • Finally, the Draw Surface Widget is added to the screen:

Screen0_DrawSurfaceWidget_0->fn->addChild(root0, (leWidget*)Screen0_DrawSurfaceWidget_0);

Back to Top

Application Code

The default code generated by MCC sets the initial state of the widget. The property or behavior of the widgets can be changed by using the APIs discussed above, in the application code. Additional application code information related to the Draw Surface widget is presented below.

Here is an example to draw a rectangle or a previously filled array when the draw notification event is triggered.

/* When Draw Surface area is marked to be updated by invalidating the widget or a specific area from the widget,
OnDraw event is called and using native APIs drawings can be done */


leBool event_Screen0_DrawSurfaceWidget_0_OnDraw(leDrawSurfaceWidget* sfc, leRect* bounds)
{
    leBool retval = LE_TRUE;
   switch(drawCmd)
    {
       case SCRN_DRAW_RECT:
        {
            leRenderer_FillArea(damagedRect.x,
                                damagedRect.y,
                                pointSize, pointSize,
                                clr,
                                255);
            break;
        }
       case SCRN_DRAW_RAND:
        {
           /*WARNING - modifying pixel by pixel all the widget area may produce screen tearing on hardware devices
             *        - issue is not seen on simulator due to drawing speed
             *        - this method was only used here for demo purpose on how to modify each pixel
             */

            for (int i = 0; i < DRAWSURFACE_ARRAY_X_SIZE; i++)
                for (int j = 0; j < DRAWSURFACE_ARRAY_Y_SIZE; j++)
                     leRenderer_PutPixel_Safe(i + DRAWSURFACE_X_OFFSET,
                                              j + DRAWSURFACE_Y_OFFSET,
                                              (leColor)drawsurface[i][j]);
            break;
        }
       case SCRN_DRAW_ERASE:
        {
            break;
        }
       default:
        {
            break;
        }
    }
   return retval;
}

 

Back to Top

Draw Surface Widget Example Project

Refer to the Draw Surface widget example project in GitHub:

In this example, we show:

  1. How to configure Draw Surface widget.
  2. How to capture touch input using filter event.
  3. How to draw back the touched pixels on the Draw Surface widget.
  4. How to draw a random generated image on the Draw Surface widget.
  5. How to clear the Draw Surface area.

Back to Top

MGS Simulator Output

Draw Surface Example

Callback functions for handling the screen events are defined in the app.c file.

  • When the screen is shown, an event filter is installed to capture the touch coordinates. The same event filter is removed when the screen is changed.
void Screen0_OnShow(void)
{
    Screen0_DrawSurfaceWidget_0->fn->installEventFilter(Screen0_DrawSurfaceWidget_0, DrawSurface_eventFilter);
}

void Screen0_OnHide(void)
{
    Screen0_DrawSurfaceWidget_0->fn->removeEventFilter(Screen0_DrawSurfaceWidget_0, DrawSurface_eventFilter);
}
  • The event filter will help to capture the touch. In this example, we only capture the touch down and move event. When a touch point is detected, it will be checked if it's in the Draw Surface area and a rectangle that will mark the damaged area is created.
Information

Note: For more details about widget event filters used for custom input handling, refer to "Custom Input Event Handling" page.

  • Using the _damageArea() API will optimize the drawing process by only invalidating the area that needs to be updated. While using the widget, the invalidate API will redraw the complete widget (the previously drawn points will also be lost in this situation). 

  • The color buttons change color based on the release event, and the slider will change the line width based on the value change event. The color buttons and slider are shown in Figure 9, and the button for violet color and slider value change events are presented below.
Draw Surface example color buttons and point size slider

Figure 9: Draw Surface example, color buttons, line width slider

void event_Screen0_ButtonWidget_Violet_OnReleased(leButtonWidget* btn)
{
    clr = COLOR_VIOLET;
}

void event_Screen0_SliderWidget_0_OnValueChanged(leSliderWidget* scr)
{
    pointSize = scr->fn->getValue(scr) * DEFAULT_POINT_SIZE;
}
  • The rectangle that is created when a touch point is detected will mark the damaged area, and can also be used by the leRenderer_FillArea() API to draw back the expected area. 
case SCRN_DRAW_RECT:
    {
        leRenderer_FillArea(damagedRect.x,
                            damagedRect.y,
                            pointSize, pointSize,
                            clr,
                           255);
       break;
    }
  • Pressing the random button will create a 2D array of size 760x400 with a random color for each pixel, and will draw it on the Draw Surface widget using the leRenderer_PutPixel_Safe() API. The complete widget is invalidated in this situation, as all its surface needs to be updated.
void event_Screen0_ButtonWidget_Random_OnReleased(leButtonWidget* btn)
{
   int i,j;
   // Create a matrix with random colors in it
   for (i = 0; i < DRAWSURFACE_ARRAY_X_SIZE; i++)
       for (j = 0; j < DRAWSURFACE_ARRAY_Y_SIZE; j++)
            drawsurface[i][j] = (rand() * rand()) | 0x000000FF;
    drawCmd = SCRN_DRAW_RAND;
    Screen0_DrawSurfaceWidget_0->fn->invalidate(Screen0_DrawSurfaceWidget_0);
}

case SCRN_DRAW_RAND:
    {
       /*WARNING - modifying pixel by pixel all the widget area may produce screen tearing on hardware devices
        *        - issue is not seen on simulator due to drawing speed
        *        - this method was only used here for demo purpose on how to modify each pixel
        */

       for (int i = 0; i < DRAWSURFACE_ARRAY_X_SIZE; i++)
           for (int j = 0; j < DRAWSURFACE_ARRAY_Y_SIZE; j++)
                leRenderer_PutPixel_Safe(i + DRAWSURFACE_X_OFFSET,
                                         j + DRAWSURFACE_Y_OFFSET,
                                         (leColor)drawsurface[i][j]);
       break;
        }
  • In order to clear the Draw Surface area, it's enough to just invalidate the widget. This way the library will clear its area and nothing will be drawn again on it, as no specific case for this situation was created. One can also redraw the complete widget with a specific background if clear may require another background color.
void event_Screen0_ButtonWidget_Erase_OnReleased(leButtonWidget* btn)
{
    drawCmd = SCRN_DRAW_ERASE;
   //To show a clean drawing surface it's enough to invalidate the widget.
   Screen0_DrawSurfaceWidget_0->fn->invalidate(Screen0_DrawSurfaceWidget_0);
}

Back to Top

Draw Surface Widget APIs Description

The Draw Surface widget does not have a specific API. For a description of APIs common to all widgets, refer to the "Base Widget Documentation" page.

Back to Top