Thursday 23 February 2017

Vector graphics in MFC/Visual C++

Although MFC, and Visual C++ are old technologies, yet they have supported vector graphics for long. Do not be too excited though, you won't be able to load up your SVG file in an MFC application just yet, and for that you will still require a non Microsoft library. But as the topic suggests, we will be able to load vector graphics.

In Windows vector graphics is natively supported by meta files (or enhanced meta files - EMF). These files can store both a raster image, and a vector image. Like any other vector image file format, an EMF file stores image in the form of commands which are to be issued to a graphics device. Thus, the file is capable of storing vector graphics. More about these files can be found at blogs.msdn.microsoft.com.

With the advent of high resolution displays, and DPI scaling, it becomes even more significant than before that we use vector graphics in our applications. In this post we will go through the process of creating a vector image in Inkcape, and then displaying it in an MFC application (same process can be used for any other win32 app created in Visual C++ without MFC).

Creating EMF Vector graphics in Inkscape


Let's create some clouds in Inkscape, we will call it clouds.svg. Note that it is important to save the image first in SVG so that later on we are able to make modifications to it. EMF file may also permit modifications, but I think SVG would be better supported by Inkscape.

Clouds.svg
Now from Inkscape, save the image as an EMF file (say clouds.emf).

Save as EMF

Displaying vector EMF file in an MFC application


Create a simple MFC dialog based application.








In Dialog's OK button's click handler, write the following code.




void CmetaFileDlg::OnBnClickedOk()
{
    // TODO: Add your control notification handler code here
    CClientDC client(this);

    HENHMETAFILE hEmf = GetEnhMetaFile(L"clouds.emf");
    
    if(hEmf==NULL)
    {
        TRACE("Error getting enhanced meta file\n");
    }
    else
    {
    TRACE("Success - enhanced metafile");
    CRect rc;
    GetClientRect(rc);
    PlayEnhMetaFile(client,hEmf,&rc);
    }

    //CDialogEx::OnOK(); //preventing Dialog from closing
}

Now compile and run the application. On pressing "OK" button, the application will show our vector image.

MFC application rendering a vector image

Since our image is a vector image, we can vary the third parameter passed to PlayEnhMetaFile() to change the size of displayed image, and we will see no pixellation/blurring. I even modified the code slightly to make the clouds appear on my 4K monitor (yes I have one!). Here is how it looked.

Vector image on 4K display

I was hoping that the background of the clouds would be transparent, as it was when we drew earlier, but it was black. SetBkMode(TRANSPARENT) also didn't help.

Here is the modified code.


void CmetaFileDlg::OnBnClickedOk()
{
    // TODO: Add your control notification handler code here
    CClientDC client(NULL);//draw on desktop

    HENHMETAFILE hEmf = GetEnhMetaFile(L"clouds.emf");
    
    if(hEmf==NULL)
    {
        TRACE("Error getting enhanced meta file\n");
    }
    else
    {
    TRACE("Success - enhanced metafile");
    CRect rc;
    
    rc.top=rc.left=0;
    rc.right=3840; rc.bottom=2160; //4K monitor

    PlayEnhMetaFile(client,hEmf,&rc);
    }

    //CDialogEx::OnOK(); //preventing Dialog from closing
}

References -

  • Creating meta files programmatically - http://www.functionx.com/visualc/gdi/cmetafile.htm
  • EMF File Overview - https://blogs.msdn.microsoft.com/openspecification/2011/06/28/emf-file-overview/
  • Opening an Enhanced Metafile and Displaying Its Contents - https://msdn.microsoft.com/en-us/library/windows/desktop/dd162750(v=vs.85).aspx