# CGraphCtx Class

`CGraphCtx` is a graphic control for pictures, text and graphics. You can use both GDI and GDI+ to draw graphics and text and to load and manipulate images. Optionally, you can add support for OpenGL by passing "OPENGL" in the caption.

This control features persistence and uses a virtual buffer (set initially equal to the size of the control when it is created) to allow to display images bigger than the size of the control. Scrollbars are automatically added when the size of the virtual buffer is bigger than the size of the control and removed when unneeded. It also features keyboard navigation and sends command messages to the parent window when the return or Escape keys are pressed, and notification messages for mouse clicks.

To use the control, include the CGraphCtx.inc file in your program and use the namespace Afx.

Include file: CGraphCtx.inc

### Constructor

Registers the class name and creates an instance of the control.

    CONSTRUCTOR CGraphCtx (BYVAL pWindow AS CWindow PTR, BYVAL cID AS LONG_PTR, _
       BYREF wszTitle AS WSTRING = "", BYVAL x AS LONG = 0, BYVAL y AS LONG = 0, _
       BYVAL nWidth AS LONG = 0, BYVAL nHeight AS LONG = 0, BYVAL dwStyle AS DWORD = 0, _
       BYVAL dwExStyle AS DWORD = 0, BYVAL lpParam AS LONG_PTR = 0)

Parameter | Description
--- | ---
pWindow | Pointer to the CWindow class of the parent window.
cID | Control identifier.
wszTitle | The window caption. If "OPENGL" is used as the caption, support for OpenGL is added to the control.
x | The x-coordinate of the upper-left corner of the window relative to the upper-left corner of the parent window's client area.
y | The initial y-coordinate of the upper-left corner of the window relative to the upper-left corner of the parent window's client area.
nWidth | The width of the control.
nHeight | The height of the control.
dwStyle | The style of the window being created. Pass -1 to use the default styles. Default styles: WS_VISIBLE OR WS_CHILD OR WS_TABSTOP.
dwExStyle | The extended window style of the control being created. Pass -1 to use the default styles.
lpParam | Pointer to custom data.

Name | Description
--- | ---
Example 1 | CWindow Graphic Control Skeleton.
Example 2 | CWindow OpenGL Graphic Control Skeleton.

### Helper Procedure: AfxCGraphPtr

Returns a pointer to the CGraphCtx class given the handle of its associated window.

    FUNCTION AfxCGraphPtr (BYVAL hwnd AS HWND) AS CGraphCtx PTR

Parameter | Description
--- | ---
hwnd | Handle of the window associated with the graphic control. Call the hWindow method of the CGraphCtx class to retrieve it.

### Methods and Properties

Name | Description
--- | ---
Clear | Clears the graphic control with the specified RGB color.
CreateBitmapFromFile | Loads and displays the specified image in the Graphic Control. It also allows to convert the image to gray scale and/or dim the image.
DrawBitmap | Draws a bitmap in the Graphic Control.
GetBits | Returns the location of the DIB bit values.
GethBmp | Returns the handle of the compatible bitmap.
GethRC | If OpenGL is enabled, it returns the handle of the rendering context of the control.
GetMemDC | Handle to the memory device context of the control.
GetVirtualBufferHeight | Returns the height of the virtual buffer.
GetVirtualBufferWidth | Returns the width of the virtual buffer.
hWindow | Returns the handle of the control.
LoadImageFromFile | Loads and displays the specified image in the Graphic Control.
LoadImageFromRes | Loads the specified image from a resource file in the Graphic Control. It also allows to convert the image to gray scale and/or dim the image.
MakeCurrent | Redirects OpenGL calls are directed to the correct rendering context.
PrintImage | Prints the image in the default printer.
Resizable | Gets/sets the value of the Resizable property.
SaveImage | Saves the image to a file.
SetVirtualBufferSize | Sets the size of the virtual buffer.
Stretchable | Gets/sets the value of the Stretchable property.
StretchMode | Gets/sets the value of the StretchMode property.

### Notification Messages

Name | Description
--- | ---
NM_CLICK | Sent by the control when the user clicks it with the left mouse button.
NM_DBLCLK | Sent by the control when the user double clicks it with the left mouse button.
NM_KILLFOCUS | Notifies a control's parent window that the control has lost the input focus.
NM_RCLICK | Sent by the control when the user clicks it with the right mouse button.
NM_RDBLCLK | Sent by the control when the user double clicks it with the right mouse button.
NM_SETFOCUS | Notifies a control's parent window that the control has received the input focus.

### NM_CLICK Notification Message

Sent by the control when the user clicks it with the left mouse button. This notification code is sent in the form of a WM_NOTIFY message.

    CASE WM_NOTIFY
       DIM phdr AS NMHDR PTR = CAST(NMHDR PTR, lParam)
       IF wParam = IDC_GRCTX THEN
          SELECT CASE phdr->code
             CASE NM_CLICK
                ' Left button clicked
          END SELECT
       END IF
    END IF

#### Remarks

IDC_GRCTX is the constant value used as identifier of the control. Change it if needed.

### NM_DBLCLK Notification Message

Sent by the control when the user double clicks it with the left mouse button. This notification code is sent in the form of a WM_NOTIFY message.

    CASE WM_NOTIFY
       DIM phdr AS NMHDR PTR = CAST(NMHDR PTR, lParam)
       IF wParam = IDC_GRCTX THEN
          SELECT CASE phdr->code
             CASE NM_DBLCLK
                ' Left button double clicked
          END SELECT
       END IF
    END IF

#### Remarks

IDC_GRCTX is the constant value used as identifier of the control. Change it if needed.

### NM_KILLFOCUS Notification Message

Notifies a control's parent window that the control has lost the input focus. This notification code is sent in the form of a WM_NOTIFY message.

    CASE WM_NOTIFY
       DIM phdr AS NMHDR PTR = CAST(NMHDR PTR, lParam)
       IF wParam = IDC_GRCTX THEN
          SELECT CASE phdr->code
             CASE NM_KILLFOCUS
                ' The control has lost focus
          END SELECT
       END IF
    END IF

#### Remarks

IDC_GRCTX is the constant value used as identifier of the control. Change it if needed.

### NM_RCLICK Notification Message

Notifies a control's parent window that the control has lost the input focus. This notification code is sent in the form of a WM_NOTIFY message.

    CASE WM_NOTIFY
       DIM phdr AS NMHDR PTR = CAST(NMHDR PTR, lParam)
       IF wParam = IDC_GRCTX THEN
          SELECT CASE phdr->code
             CASE NM_RCLICK
                ' Right button clicked
          END SELECT
       END IF
    END IF

#### Remarks

IDC_GRCTX is the constant value used as identifier of the control. Change it if needed.

### NM_RDBLCLK Notification Message

Sent by the control when the user double clicks it with the right mouse button. This notification code is sent in the form of a WM_NOTIFY message.

    CASE WM_NOTIFY
       DIM phdr AS NMHDR PTR = CAST(NMHDR PTR, lParam)
       IF wParam = IDC_GRCTX THEN
          SELECT CASE phdr->code
             CASE NM_RDBLCLK
                ' Right button double clicked
          END SELECT
       END IF
    END IF

#### Remarks

IDC_GRCTX is the constant value used as identifier of the control. Change it if needed.

### NM_SETFOCUS Notification Message

Notifies a control's parent window that the control has received the input focus. This notification code is sent in the form of a WM_NOTIFY message.

    CASE WM_NOTIFY
       DIM phdr AS NMHDR PTR = CAST(NMHDR PTR, lParam)
       IF wParam = IDC_GRCTX THEN
          SELECT CASE phdr->code
             CASE NM_SETFOCUS
                ' The control has gained focus
          END SELECT
       END IF
    END IF

#### Remarks

IDC_GRCTX is the constant value used as identifier of the control. Change it if needed.

### Clear

Clears the graphic control with the specified RGB color.

    FUNCTION Clear (BYVAL RGBColor AS COLORREF) AS BOOLEAN

Parameter | Description
--- | ---
RGBColor | RGB color used to fill the control.

#### Return value

TRUE or FALSE.

### CreateBitmapFromFile

Loads and displays the specified image in the Graphic Control. It also allows to convert the image to gray scale and/or dim the image.

    SUB CreateBitmapFromFile (BYREF wszFileName AS WSTRING, _
       BYVAL dimPercent AS LONG = 0, BYVAL bGrayScale AS LONG = FALSE)

Parameter | Description
--- | ---
wszFileName | Absolute path to the file.
dimPercent | Optional. Percent of dimming (1-99).
bGrayScale | TRUE or FALSE. Convert to gray scale.

#### Return value

TRUE or FALSE.

### DrawBitmap

Draws a bitmap in the Graphic Control.

    SUB DrawBitmap (BYVAL hbmp AS HBITMAP, BYVAL x AS SINGLE = 0, BYVAL y AS SINGLE = 0, _
       BYVAL nRight AS SINGLE = 0, BYVAL nBottom AS SINGLE = 0) AS GpStatus

    SUB DrawBitmap (BYVAL pBitmap AS GpBitmap PTR, BYVAL x AS SINGLE = 0, BYVAL y AS SINGLE = 0, _
       BYVAL nRight AS SINGLE = 0, BYVAL nBottom AS SINGLE = 0) AS GpStatus

    SUB DrawBitmap (BYREF pMemBmp AS CMemBmp, BYVAL x AS SINGLE = 0, BYVAL y AS SINGLE = 0, _
       BYVAL nRight AS SINGLE = 0, BYVAL nBottom AS SINGLE = 0) AS GpStatus

Parameter | Description
--- | ---
hbmp | A handle to the bitmap.
pBitmap | Pointer to a GDI+ bitmap.
pMemBmp | Reference to a memory bitmap (CMemBmp class).
x | Optional. Left position.
y | Optional. Top position.
nRight | Optional. Right position..
nBottom | Optional. Bottom position..

#### Return value

Returns OK (0) on success or a GdiPlus status code on failure.

#### Remarks

If both x and y are ommited, the image is draw starting at position 0, 0.

If nRight and nBottom are specified, the image is draw stretched in the bounding rectangle formed by x, y, nRight and nBottom.

#### Sample code

    DIM pMemBmp AS CMemBmp = CMemBmp($"C:\Users\Pepe\Pictures\Cole_Kyla_01.jpg")
    Rectangle pMemBmp.GetMemDC, 10, 10, 150, 150
    LineTo pMemBmp.GetMemDC, 30, 180
    pGraphCtx.DrawBitmap pMemBmp

### GetBits

Returns the location of the DIB bit values.

    FUNCTION GetBits () AS ANY PTR

#### Return value

Pointer to the location of the DIB bit values.

### GethBmp

Returns the handle of the compatible bitmap.

    FUNCTION GethBmp () AS HBITMAP

### GethRC

If OpenGL is enabled, it returns the handle of the rendering context of the control.

    FUNCTION GethRC () AS HGLRC

### GetMemDC

Returns the handle of the memory device context of the control.

    FUNCTION GetMemDC () AS HDC

### GetVirtualBufferHeight

Returns the height of the virtual buffer.

    FUNCTION GetVirtualBufferHeight () AS LONG

### GetVirtualBufferWidth

Returns the width of the virtual buffer.

    FUNCTION GetVirtualBufferWidth () AS LONG

### hWindow

Returns the handle of the control.

    FUNCTION hWindow () AS HWND

### LoadImageFromFile

Loads and displays the specified image in the Graphic Control.

    SUB LoadImageFromFile (BYREF wszFileName AS WSTRING)

Parameter | Description
--- | ---
wszFileName | Absolute path to the file.

#### Remarks

A quirk in the GDI+ GdipCreateBitmapFromFile function causes that black and white images are loaded with increased contrast. Therefore, it's better to use the CreateBitmapFromFile for black and white images.

### LoadImageFromRes

Loads the specified image from a resource file in the Graphic Control. It also allows to convert the image to gray scale and/or dim the image.

    SUB LoadImageFromRes (BYVAL hInst AS HINSTANCE, BYREF wszImageName AS WSTRING, _
       BYVAL dimPercent AS LONG = 0, BYVAL bGrayScale AS LONG = FALSE, _
       BYVAL clrBackground AS ARGB = 0)

Parameter | Description
--- | ---
hInst | A handle to the module whose portable executable file or an accompanying MUI file contains the resource. If this parameter is NULL, the function searches the module used to create the current process.
wszImageName | Name of the image in the resource file (.RES). If the image resource uses an integral identifier, wszImage should begin with a number symbol (#) followed by the identifier in an ASCII format, e.g., "#998". Otherwise, use the text identifier name for the image. Only images embedded as raw data (type RCDATA) are valid. These must be icons in format .png, .jpg, .gif, .tiff.
dimPercent | Percent of dimming (1-99).
bGrayScale | TRUE or FALSE. Convert to gray scale.
clrBackground | The background color. This parameter is ignored if the bitmap is totally opaque.

### MakeCurrent

As more than one instance of this control can be used on a form, we need to make sure that OpenGL calls are directed to the correct rendering context. This is achieved by calling the MakeCurrent method.

    FUNCTION MakeCurrent () AS BOOLEAN

#### Return value

TRUE or FALSE.

### PrintImage

Prints the image in the default printer.

    FUNCTION PrintImage (BYVAL bStretch AS BOOLEAN = FALSE, _
       BYVAL nStretchMode AS LONG = InterpolationModeHighQualityBicubic) AS BOOLEAN

Parameter | Description
--- | ---
bStretch | Optional. Stretch the image.
nStretchMode | Optional. Stretching mode. Default value = InterpolationModeHighQualityBicubic.

### InterpolationMode Enumeration

Constant | Description
--- | ---
InterpolationModeDefault | Specifies the default interpolation mode.
InterpolationModeLowQuality | Specifies a low-quality mode.
InterpolationModeHighQuality | Specifies a high-quality mode.
InterpolationModeBilinear | Specifies bilinear interpolation. No prefiltering is done. This mode is not suitable for shrinking an image below 50 percent of its original size.
InterpolationModeBicubic | Specifies bicubic interpolation. No prefiltering is done. This mode is not suitable for shrinking an image below 25 percent of its original size.
InterpolationModeNearestNeighbor | Specifies nearest-neighbor interpolation.
InterpolationModeHighQualityBilinear | Specifies high-quality, bilinear interpolation. Prefiltering is performed to ensure high-quality shrinking.
InterpolationModeHighQualityBicubic | Specifies high-quality, bicubic interpolation. Prefiltering is performed to ensure high-quality shrinking. This mode produces the highest quality transformed images.

#### Return value

Returns TRUE if the bitmap has been printed successfully, or FALSE otherwise.

### Resizable

Gets/sets the value of the Resizable property.

    PROPERTY Resizable () AS BOOLEAN
    PROPERTY Resizable (BYVAL bResizable AS BOOLEAN)

#### Return value

TRUE or FALSE.

##### Remarks

If resizable, the virtual buffer is set to the size of the control. If the control is made smaller and then bigger, part of the contents are lost. Therefore, the caller must redraw it. Resizable and stretchable are mutually exclusive.

### SaveImage

Saves the image to a file.

    FUNCTION SaveImage (BYREF wszFileName AS WSTRING, BYREF wszMimeType AS WSTRING) AS LONG

    FUNCTION SaveImageAsBmp (BYREF wszFileName AS WSTRING) AS LONG
    FUNCTION SaveImageAsJpeg (BYREF wszFileName AS WSTRING) AS LONG
    FUNCTION SaveImageAsPng (BYREF wszFileName AS WSTRING) AS LONG
    FUNCTION SaveImageAsGif (BYREF wszFileName AS WSTRING) AS LONG
    FUNCTION SaveImageAsTiff (BYREF wszFileName AS WSTRING) AS LONG

Parameter | Description
--- | ---
wszFileName | Absolute path name of the file.
wszMimeType | The mime type. "image/bmp" = Bitmap (.bmp) "image/gif" = GIF (.gif) "image/jpeg" = JPEG (.jpg) "image/png" = PNG (.png) "image/tiff" = TIFF (.tiff)

#### Return value

If the method succeeds, it returns Ok, which is an element of the GDI+ Status enumeration.

If the method fails, it returns one of the other elements of the GDI+ Status enumeration.

### SetVirtualBufferSize

Sets the size of the virtual buffer.

    FUNCTION SetVirtualBufferSize (BYVAL nWidth AS LONG, BYVAL nHeight AS LONG) AS LONG

Parameter | Description
--- | ---
nWidth | Width, in pixels, of the virtual buffer.
nHeight | Height, in pixels, of the virtual buffer.

#### Return value

If the function succeeds, the return value is S_OK (0).

### Stretchable

Gets/sets the value of the Stretchable property.

    PROPERTY Stretchable () AS BOOLEAN
    PROPERTY Stretchable (BYVAL bStretchable AS BOOLEAN)

Parameter | Description
--- | ---
bStretchable | TRUE or FALSE.

#### Return value

TRUE or FALSE.

### StretchMode

Gets/sets the value of the StretchMode property.

    PROPERTY StretchMode () AS LONG
    PROPERTY StretchMode (BYVAL nStretchMode AS LONG)

Parameter | Description
--- | ---
nStretchMode | The stretching mode.

Mode | Description
--- | ---
BLACKONWHITE | Performs a Boolean AND operation using the color values for the eliminated and existing pixels. If the bitmap is a monochrome bitmap, this mode preserves black pixels at the expense of white pixels.
COLORONCOLOR | Deletes the pixels. This mode deletes all eliminated lines of pixels without trying to preserve their information.
HALFTONE | Maps pixels from the source rectangle into blocks of pixels in the destination rectangle. The average color over the destination block of pixels approximates the color of the source pixels. After setting the HALFTONE stretching mode, an application must call the SetBrushOrgEx function to set the brush origin. If it fails to do so, brush misalignment occurs.
STRETCH_ANDSCANS | Same as BLACKONWHITE.
STRETCH_DELETESCANS | Same as COLORONCOLOR.
STRETCH_HALFTONE | Same as HALFTONE.
STRETCH_ORSCANS | Same as WHITEONBLACK.
WHITEONBLACK | Performs a Boolean OR operation using the color values for the eliminated and existing pixels. If the bitmap is a monochrome bitmap, this mode preserves white pixels at the expense of black pixels.

#### Return value

The previous value of the property.

### CWindow Graphic Control Skeleton

    ' ########################################################################################
    ' Microsoft Windows
    ' Contents: CWindow Graphic Control Skeleton
    ' Compiler: FreeBasic 32 & 64 bit
    ' Copyright (c) 2016 José Roca. Freeware. Use at your own risk.
    ' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
    ' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
    ' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
    ' ########################################################################################

    #INCLUDE ONCE "windows.bi"
    #INCLUDE ONCE "Afx/CWindow.inc"
    #INCLUDE ONCE "Afx/CGraphCtx.inc"
    USING Afx

    CONST IDC_GRCTX = 1001

    DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
                              BYVAL hPrevInstance AS HINSTANCE, _
                              BYVAL szCmdLine AS ZSTRING PTR, _
                              BYVAL nCmdShow AS LONG) AS LONG

       END WinMain(GetModuleHandleW(NULL), NULL, COMMAND(), SW_NORMAL)

    ' ========================================================================================
    ' Window procedure
    ' ========================================================================================
    FUNCTION WndProc (BYVAL hWnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

       SELECT CASE uMsg

          CASE WM_CREATE
             EXIT FUNCTION

          CASE WM_COMMAND
             SELECT CASE LOWORD(wParam)
                CASE IDCANCEL
                   ' // If ESC key pressed, close the application sending an WM_CLOSE message
                   IF HIWORD(wParam) = BN_CLICKED THEN
                      SendMessageW hwnd, WM_CLOSE, 0, 0
                      EXIT FUNCTION
                   END IF
             END SELECT

          CASE WM_SIZE
             ' // If the window isn't minimized, resize it
             IF wParam <> SIZE_MINIMIZED THEN
                DIM pWindow AS CWindow PTR = CAST(CWindow PTR, GetWindowLongPtrW(hwnd, 0))
                pWindow->MoveWindow GetDlgItem(hwnd, IDC_GRCTX), 0, 0, pWindow->ClientWidth, pWindow->ClientHeight, CTRUE
             END IF

            CASE WM_DESTROY
             PostQuitMessage(0)
             EXIT FUNCTION

       END SELECT

       FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)

    END FUNCTION
    ' ========================================================================================

    ' ========================================================================================
    ' Main
    ' ========================================================================================
    FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
                      BYVAL hPrevInstance AS HINSTANCE, _
                      BYVAL szCmdLine AS ZSTRING PTR, _
                      BYVAL nCmdShow AS LONG) AS LONG

       ' // Set process DPI aware
       AfxSetProcessDPIAware

       DIM pWindow AS CWindow
       pWindow.Create(NULL, "CWindow Graphic Control Skeleton", @WndProc)
       pWindow.SetClientSize(600, 350)
       pWindow.Center

       ' // Add a graphic control
       DIM pGraphCtx AS CGraphCtx = CGraphCtx(@pWindow, IDC_GRCTX, "", 0, 0, pWindow.ClientWidth, pWindow.ClientHeight)
       pGraphCtx.Clear BGR(255, 255, 255)

       ' // Capture the desktop window and display it in the control
       DIM hBitmap AS HBITMAP = AfxCaptureDisplay
       pGraphCtx.SetVirtualBufferSize(AfxGetBitmapWidth(hBitmap), AfxGetBitmapHeight(hBitmap))
       AfxDrawBitmap(pGraphCtx.GetMemDC, 0, 0, hBitmap)
       DeleteObject hBitmap

       ' // Process Windows events
       FUNCTION = pWindow.DoEvents(nCmdShow)

    END FUNCTION
    ' ========================================================================================

### CWindow OpenGL Graphic Control Skeleton

The following example demonstrates the use of OpenGL and GDI+ together.

    ' ########################################################################################
    ' Microsoft Windows
    ' Contents: CWindow OpenGL Graphic Control Skeleton
    ' Compiler: FreeBasic 32 & 64 bit
    ' Copyright (c) 2017 José Roca. Freeware. Use at your own risk.
    ' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
    ' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
    ' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
    ' ########################################################################################

    #define UNICODE
    #INCLUDE ONCE "Afx/CWindow.inc"
    #INCLUDE ONCE "Afx/AfxGdiPlus.inc"
    #INCLUDE ONCE "Afx/CGraphCtx.inc"
    USING Afx

    CONST IDC_GRCTX = 1001

    DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
                              BYVAL hPrevInstance AS HINSTANCE, _
                              BYVAL szCmdLine AS ZSTRING PTR, _
                              BYVAL nCmdShow AS LONG) AS LONG

       END WinMain(GetModuleHandleW(NULL), NULL, COMMAND(), SW_NORMAL)

    ' // Forward declaration
    DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

    ' ========================================================================================
    ' The following sample code draws a line.
    ' Change it with your own code.
    ' ========================================================================================
    SUB GDIP_Render (BYVAL hDC AS HDC)

       DIM hStatus AS GpStatus
       DIM pGraphics AS GpGraphics PTR
       DIM pPen AS GpPen PTR

       ' // Create a graphics object
       hStatus = GdipCreateFromHDC(hDC, @pGraphics)
       ' // Create a Pen
       hStatus = GdipCreatePen1(GDIP_ARGB(255, 255, 0, 0), AfxScaleX(1), UnitPixel, @pPen)
       ' // Draw the line
       GdipDrawLineI pGraphics, pPen, 0, 0, AfxScaleX(200), AfxScaleY(100)

       ' // Cleanup
       IF pPen THEN GdipDeletePen(pPen)
       IF pGraphics THEN GdipDeleteGraphics(pGraphics)

    END SUB
    ' ========================================================================================

    ' =======================================================================================
    ' All the setup goes here
    ' =======================================================================================
    SUB RenderScene (BYVAL nWidth AS LONG, BYVAL nHeight AS LONG)

       ' // Specify clear values for the color buffers
       glClearColor 0.0, 0.0, 0.0, 0.0
       ' // Specify the clear value for the depth buffer
       glClearDepth 1.0
       ' // Specify the value used for depth-buffer comparisons
       glDepthFunc GL_LESS
       ' // Enable depth comparisons and update the depth buffer
       glEnable GL_DEPTH_TEST
       ' // Select smooth shading
       glShadeModel GL_SMOOTH

       ' // Prevent divide by zero making height equal one
       IF nHeight = 0 THEN nHeight = 1
       ' // Reset the current viewport
       glViewport 0, 0, nWidth, nHeight
       ' // Select the projection matrix
       glMatrixMode GL_PROJECTION
       ' // Reset the projection matrix
       glLoadIdentity
       ' // Calculate the aspect ratio of the window
       gluPerspective 45.0!, nWidth / nHeight, 0.1!, 100.0!
       ' // Select the model view matrix
       glMatrixMode GL_MODELVIEW
       ' // Reset the model view matrix
       glLoadIdentity

       ' // Clear the screen buffer
       glClear GL_COLOR_BUFFER_BIT OR GL_DEPTH_BUFFER_BIT
       ' // Reset the view
       glLoadIdentity

       glTranslatef -1.5!, 0.0!, -6.0!       ' Move left 1.5 units and into the screen 6.0

       glBegin GL_TRIANGLES                 ' Drawing using triangles
          glColor3f   1.0!, 0.0!, 0.0!       ' Set the color to red
          glVertex3f  0.0!, 1.0!, 0.0!       ' Top
          glColor3f   0.0!, 1.0!, 0.0!       ' Set the color to green
          glVertex3f  1.0!,-1.0!, 0.0!       ' Bottom right
          glColor3f   0.0!, 0.0!, 1.0!       ' Set the color to blue
          glVertex3f -1.0!,-1.0!, 0.0!       ' Bottom left
       glEnd                                 ' Finished drawing the triangle

       glTranslatef 3.0!,0.0!,0.0!           ' Move right 3 units

       glColor3f 0.5!, 0.5!, 1.0!            ' Set the color to blue one time only
       glBegin GL_QUADS                     ' Draw a quad
          glVertex3f -1.0!, 1.0!, 0.0!       ' Top left
          glVertex3f  1.0!, 1.0!, 0.0!       ' Top right
          glVertex3f  1.0!,-1.0!, 0.0!       ' Bottom right
          glVertex3f -1.0!,-1.0!, 0.0!       ' Bottom left
       glEnd                                 ' Done drawing the quad

       ' // Required: force execution of GL commands in finite time
       glFlush

    END SUB
    ' =======================================================================================

    ' ========================================================================================
    ' Main
    ' ========================================================================================
    FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
                      BYVAL hPrevInstance AS HINSTANCE, _
                      BYVAL szCmdLine AS ZSTRING PTR, _
                      BYVAL nCmdShow AS LONG) AS LONG

       ' // Set process DPI aware
       AfxSetProcessDPIAware

       ' // Create the main window
       DIM pWindow AS CWindow
       pWindow.Create(NULL, "CWindow OpenGL Graphic Control Skeleton", @WndProc)
       pWindow.Brush = CAST(HBRUSH, COLOR_WINDOW + 1)
       pWindow.SetClientSize(400, 250)
       pWindow.Center

       ' // Initialize GDI+
       DIM token AS UINT_PTR = AfxGdipInit

       ' // Add a graphic control with OPENGL enabled
       DIM pGraphCtx AS CGraphCtx = CGraphCtx(@pWindow, IDC_GRCTX, "OPENGL", 0, 0, pWindow.ClientWidth, pWindow.ClientHeight)
       ' // Make the control stretchable
       pGraphCtx.Stretchable = TRUE
       ' // Make current the rendering context
       pGraphCtx.MakeCurrent
       ' // Render the OpenGL scene
       RenderScene pGraphCtx.GetVirtualBufferWidth, pGraphCtx.GetVirtualBufferHeight
       ' // Draw the GDI+ graphics
       GDIP_Render pGraphCtx.GetMemDC

       ' // Dispatch Windows events
       FUNCTION = pWindow.DoEvents(nCmdShow)

       ' // Shutdown GDI+
       IF token THEN GdiPlusShutdown token

    END FUNCTION
    ' ========================================================================================

    ' ========================================================================================
    ' Main window callback procedure
    ' ========================================================================================
    FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

       SELECT CASE uMsg

          CASE WM_COMMAND
             SELECT CASE GET_WM_COMMAND_ID(wParam, lParam)
                CASE IDCANCEL
                   ' // If ESC key pressed, close the application by sending an WM_CLOSE message
                   IF GET_WM_COMMAND_CMD(wParam, lParam) = BN_CLICKED THEN
                      SendMessageW hwnd, WM_CLOSE, 0, 0
                      EXIT FUNCTION
                   END IF
             END SELECT

          CASE WM_SIZE
             ' // If the window isn't minimized, resize it
             IF wParam <> SIZE_MINIMIZED THEN
                DIM pWindow AS CWindow PTR = AfxCWindowPtr(hwnd)
                IF pWindow THEN pWindow->MoveWindow GetDlgItem(hwnd, IDC_GRCTX), 0, 0, pWindow->ClientWidth, pWindow->ClientHeight, CTRUE
             END IF

            CASE WM_DESTROY
             ' // End the application
             PostQuitMessage(0)
             EXIT FUNCTION

       END SELECT

       ' // Default processing of Windows messages
       FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)

    END FUNCTION
    ' ========================================================================================