Paint on Chalk Board App

Contains additional notes on extending the functionality of this application.

To Draw a Shape using Lines

Use the draw_line function from PaintApp.cpp:

// Parameters:
// Device context
// current x, y position (could be mouse coordinates)
// where the line should end at coordinates a, b
// color of the line
void draw_line(HDC DC, int x, int y, int a, int b, COLORREF color)
{
    // creates a logical pen that has the specified style, width (thickness) and color.
    // https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-createpen
    HPEN pen = CreatePen(PS_SOLID, 3, color);

    // selects object into device context
    // https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-selectobject
    SelectObject(DC, pen);

    // updates current position to specified point
    // https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-movetoex
    MoveToEx(DC, x, y, NULL);

    // draws line from current position up to but not including specified point
    // https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-lineto
    LineTo(DC, a, b);

    // deletes a logical pen to free system resources associated with object
    // https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-deleteobject
    DeleteObject(pen);

}

For example to draw a Star that shows up on the window, call the following several draw_line functions within OnPaint after creating a compatible device context.

Shows an image of a star shaped with yellow lines

It is possible to insert these draw_line calls after this line in the PaintApp.cpp code or after this line as well.

draw_line(DC, 325, 600, 375, 450, yellowline);
draw_line(DC, 375, 450, 425, 600, yellowline);
draw_line(DC, 425, 600, 300, 500, yellowline);
draw_line(DC, 325, 600, 445, 500, yellowline);
draw_line(DC, 300, 500, 445, 500, yellowline);

This will create yellow lines of thickness set to 3 pixels, see CreatePen. If a varied length is preferred, create a width parameter for draw_line to pass in the desired width.

For the color of the lines, initialize a COLORREF variable within OnPaint e.g. COLORREF yellowline = RGB( 244, 244, 66); //yellow.

x, y, a and b are coordinates on the window.

Have a Shape Move change the Cursor Coordinates

Have global variables int dx = 0, dy = 0; initialized and have place them within OnMM, a function called in WndProc whenever it receives a WM_MOUSEMOVE message like so HANDLE_MSG(hwnd, WM_MOUSEMOVE, OnMM);.

void OnMM(HWND hwnd, int x, int y, UINT keyFlags)
{
    // records current cursor position
    dx = x;
    dy = y;
    .
    .
    .
}

To Create A Rectangle

Place the four draw_line calls in a function for organization:

// draws a quadrilateral
void draw_rectangle(HDC DC, int x, int y, int w, int h, COLORREF linecolor, int lineWidth)
{

        draw_line(DC, x, y, w, y, magentaline, lineWidth);
        draw_line(DC, w, y, w, h, magentaline, lineWidth);  
        draw_line(DC, w, h, x, h, magentaline, lineWidth);
        draw_line(DC, x, h, x, y, magentaline, lineWidth);

}

Make a call to draw_rectangle in OnPaint: draw_rectangle(DC, dx, dy, dx+50, dy+50, magentaline, 5);

This call will create a quadrilateral made up of 4 magenta colored lines of thickness 5 pixels, that has equal height and width i.e. 300 from the cursor.

COLORREF magentaline = RGB( 255, 0, 255 ); //magenta

a shapes that moves with the cursor demo

This call instead will vary the height and width of the quadrilateral with respect to the cursor position, where a corner of the quadrilateral will be fixed.

draw_rectangle(DC,50, 50, dx, dy, magentaline, 5);

a shapes that changes in witdh and height with the cursor demo

To Create a Target Mark or Crosshair ➕

These draw_line calls creates a cross (a target mark) of 10 pixels for height and width:

draw_line(DC, dx - 10, dy, dx + 10, dy, 219, 139, 19, 5);
draw_line(DC, dx, dy - 10, dx, dy + 10, 219, 139, 19, 5);

a cross that moves with the cursor demo

Add the following lines to OnTimer in PaintApp.cpp.

void OnTimer(HWND hwnd, UINT id)
{
    static int count = 0;
    count++;

  // showing and hiding (i.e. blinking) every 20 milliseconds is too fast
    if (count >= 10) // called 2 times a second  // 20 miliseconds * 10  = 200 milisec == 2 secs
    {
        count = 0;

        // to switch between showing the crosshair && blinking the star
        if (showcross == 1 || star_blink == 1 ) {
            showcross = 0;
            showstar = 0;
       }
        else
        {
            showcross = 1;
            showstar = 1;
        }
    }

    // redraws the background every 20 miliseconds
    redr_win_full(hwnd, FALSE);
}

showcross and showstar are global variables (i.e. outside of any function) initialized as such, int showcross = 1, showstar=1; These variables can also be Booleans.

Simply wrap around an if statement where drawing the star and crosshair is called within OnPaint:

void OnPaint(HWND hwnd)
{
    .
    .
    .
    if (showstar == 1)
            {
                draw_line(DC, 325, 600, 375, 450, 244, 244, 66, 8);
                draw_line(DC, 375, 450, 425, 600, 244, 244, 66, 8);
                draw_line(DC, 425, 600, 300, 500, 244, 244, 66, 8);
                draw_line(DC, 325, 600, 445, 500, 244, 244, 66, 8);
                draw_line(DC, 300, 500, 445, 500, 244, 244, 66, 8);
    }

    if (showcross == 1)
        {
        draw_line(DC, dx - 10, dy, dx + 10, dy, 219, 139, 19, 5);
        draw_line(DC, dx, dy - 10, dx, dy + 10, 219, 139, 19, 5);
    }
    .
    .
    .
}

This logic indicates that only draw the star and crosshair when showstar and showcross are set to 1. These variables are switching between 1 and 0 very 2 seconds within OnTimer.

shows a star and a crosshair blinking every 2 seconds

Render Text on the Window

The following snippet is called within OnPaint that will render (display the string) a text at coordinates (10, 10).

/*
 * Prints the coordinates of the Cross
 */
    // int to string
    crossPos = "x = " + to_string(dx) + " y= " + to_string(dy);
    // put string in buffer char array
    strcpy(buffer, crossPos.c_str());
    // Output text
    TextOut(DC, 10, 10, buffer, strlen(buffer));

Since crossPos changes with respect to cursor coordinates, it is a global variable i.e. string crossPos = "";. If the display string does not need to change, it is possible to have it within OnPaint like so, crossPos = "hello!";

shows cursor coordinates as text in the window

Reference:
Win32 Text Output Example