Quantcast
Channel: mfc – codexpert blog
Viewing all 41 articles
Browse latest View live

Using Direct2D for Rendering WIC Bitmap Sources

$
0
0

Once having a WIC (Windows Imaging Component) source, we can render it by using GDI, GDI+ or Direct2D. There are many examples over the internet including MSDN Library that describes how to perform this.
This article describes how to use WIC Wrapper Library, which is designed for using WIC without worrying about directly deal with COM interfaces. The sample code as well as attached demo project uses MFC, but WIC Wrapper Library can be used in ATL or raw-Win32 projects, as well.

Let’s see the code, step by step!

  1. Add WIC and Direc2D factoy members to the aplication class.
    // DemoImageViewer.h
    // ...
    #include "WicWrapperLibrary.h"
    // ...
    class CDemoImageViewerApp : public CWinAppEx
    {
    private:
        std::shared_ptr<ns::wic::ImagingFactory> m_pWICImagingFactory;
        std::shared_ptr<ns::d2d1::Factory>       m_pDirect2DFactory;
    
    public:
        std::shared_ptr<ns::wic::ImagingFactory> GetWICImagingFactory() {return m_pWICImagingFactory;}
        std::shared_ptr<ns::d2d1::Factory> GetDirect2DFactory() {return m_pDirect2DFactory;}
    //...
    };
  2. Create WIC and Direc2D factory instances after initializing OLE libraries.
    BOOL CDemoImageViewerApp::InitInstance()
    {
        // ...
        // Initialize OLE libraries
        if (!AfxOleInit())
        {
            AfxMessageBox(IDP_OLE_INIT_FAILED);
            return FALSE;
        }
    
        try
        {
            // create WIC Imaging Factory instance
            m_pWICImagingFactory = std::make_shared<ns::wic::ImagingFactory>();
            m_pWICImagingFactory->CreateInstance();
    
            // Create Direct2D Factory instance
            m_pDirect2DFactory = std::make_shared<ns::d2d1::Factory>();
            m_pDirect2DFactory->CreateInstance();
        }
        catch(CException* e)
        {
            e->ReportError();
            e->Delete();
            return FALSE;
        }
        // ...
    }
  3. Release factories befor calling AfxOleTerm.
    int CDemoImageViewerApp::ExitInstance()
    {
        // release WIC Imaging and Direct2D Factory instances
        m_pWICImagingFactory->ReleaseInstance();
        m_pDirect2DFactory->ReleaseInstance();
    
        AfxOleTerm(FALSE);
    
        return CWinAppEx::ExitInstance();
    }

     
  4. Add WIC bitmap source member to document class then override CDocument::OnOpenDocument and CDocument::DeleteContents. Also add an implementation function that loads the WIC bitmap source from a file.
    // DemoImageViewerDoc.h
    // ...
    class CDemoImageViewerDoc : public CDocument
    {
        // Attributes
    private:
        std::shared_ptr<ns::wic::FormatConverter> m_pWICBitmapSource;
    
        // Operations
    public:
        std::shared_ptr<ns::wic::FormatConverter> GetWICBitmapSource() {return m_pWICBitmapSource;}
    
        // Overrides
    public:
        virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
        virtual void DeleteContents();
    
        // Implementation
    private:
        BOOL _LoadBitmapSourceFromFile(LPCTSTR pszFileName);
    // ...
    };

    // DemoImageViewerDoc.cpp
    // ...
    BOOL CDemoImageViewerDoc::OnOpenDocument(LPCTSTR lpszPathName)
    {
        if (!CDocument::OnOpenDocument(lpszPathName))
            return FALSE;
    
        return _LoadBitmapSourceFromFile(lpszPathName);
    }
    
    void CDemoImageViewerDoc::DeleteContents()
    {
        m_pWICBitmapSource.reset();
    
        CDocument::DeleteContents();
    }
    
    BOOL CDemoImageViewerDoc::_LoadBitmapSourceFromFile(LPCTSTR pszFileName)
    {
        BOOL bRet = FALSE;
        try
        {
            // get WIC Imaging Factory
            CDemoImageViewerApp* pApp = static_cast<CDemoImageViewerApp*>(AfxGetApp());
            std::shared_ptr pWICImagingFactory 
                = pApp->GetWICImagingFactory();
    
            // create BitmapDecoder from a file
            const std::shared_ptr<ns::wic::BitmapDecoder> pBitmapDecoder = 
                pWICImagingFactory->CreateDecoderFromFilename(CT2W(pszFileName));
    
            // get first page
            const std::shared_ptrr<ns::wic::BitmapFrameDecoder> pBitmapFrameDecoder 
                = pBitmapDecoder->GetFrame(0);
    
            // get and initialize a format converter  
            const std::shared_ptr<ns::wic::FormatConverter> pFormatConverter 
                = pWICImagingFactory->CreateFormatConverter();
            pFormatConverter->Initialize(pBitmapFrameDecoder);
    
            m_pWICBitmapSource = pFormatConverter;
            bRet = TRUE; // success
        }
        catch(CAtlException& e)
        {
            CString strErrMsg;
            strErrMsg.Format(_T("Open bitmap source failed\nError code: 0x%08X"), e.m_hr);
            AfxMessageBox(strErrMsg, MB_ICONERROR);
            DeleteContents();
        }
        return bRet;
    }
    // ...

    Now we have a WIC bitmap source in the document class that will be used in the view class to create a Direct2D bitmap and render it in the view’s window.
  5. Add Direct2D render target and bitmap to the view class
    // DemoImageViewerView.h
    // ...
    class CDemoImageViewerView : public CScrollView
    {
    // Attributes
    private:
        std::shared_ptr<ns::d2d1::HwndRenderTarget> m_pD2DRenderTarget;
        std::shared_ptr<ns::d2d1::Bitmap> m_pD2DBitmap;
    
    // Implementation
    private:
        bool _CreateDirect2DRenderTarget(); 
        void _Resize2DRenderTarget(int cx, int cy);
        bool _CreateDirect2DBitmapFromWICBitmap();
        void _RenderD2DBitmap(CDC* pDC);
    //...
    };
  6. Create a Direct2D render target for the view window.
    bool CDemoImageViewerView::_CreateDirect2DRenderTarget()
    {
        bool bRet = false;
        try
        {
            // reset the window render target
            m_pD2DRenderTarget.reset();
    
            // get Direct2D factory
            CDemoImageViewerApp* pApp = static_cast<CDemoImageViewerApp*>(AfxGetApp());
            std::shared_ptr<ns::d2d1::Factory> pDirect2DFactory = pApp->GetDirect2DFactory();
    
            // create a render target for this window
            // intial size is zero; will be resized in WM_SIZE message handler
            m_pD2DRenderTarget = pDirect2DFactory->CreateHwndRenderTarget(m_hWnd, 0, 0);
    
            bRet = true; // success
        }
        catch(CAtlException& e)
        {
            CString strError; // show what's going wrong
            strError.Format(_T("Create render target failed.\nError code: 0x%08X"), e.m_hr);
            AfxMessageBox(strError);
        }
        return bRet;
    }

    A good place to call _CreateDirect2DRenderTarget is in the WM_CREATE message handler.
  7. Resize the Direct2D render target each time is necessary.
    void CDemoImageViewerView::_Resize2DRenderTarget(int cx, int cy)
    {
        if(nullptr != m_pD2DRenderTarget)
        {
            try
            {
                ns::d2d1::SIZE_U sizeTarget = {cx, cy};
                m_pD2DRenderTarget->Resize(sizeTarget);
            }
            catch(CAtlException& e)
            {
                HRESULT hr = e.m_hr; // just catch it
            }
        }
    }

    If the render target must fit the view window, then the place to call _Resize2DRenderTarget is the WM_SIZE mesage handler.
  8. Create the Direct2D bitmap from WIC bitmap.
    bool CDemoImageViewerView::_CreateDirect2DBitmapFromWICBitmap()
    {
        bool bRet = false;
        try
        {
            // reset the Direct2D bitmap
            m_pD2DBitmap.reset();
    
            // get WIC bitmap source from document class
            CDemoImageViewerDoc* pDoc = GetDocument();
            std::shared_ptr pWICBitmapSource = pDoc->GetWICBitmapSource();
    
            if(nullptr != pWICBitmapSource)
            {
                // get Direct2D factory
                CDemoImageViewerApp* pApp = static_cast<CDemoImageViewerApp*>(AfxGetApp());
                std::shared_ptr<ns::d2d1::Factory> pDirect2DFactory = pApp->GetDirect2DFactory();
    
                // create Direct2D bitmap from WIC bitmap 
                std::shared_ptr<ns::wic::FormatConverter> pWICBitmapSource = pDoc->GetWICBitmapSource();
                m_pD2DBitmap = m_pD2DRenderTarget->CreateBitmapFromWicBitmap(pWICBitmapSource);
    
                bRet = true; // success
            }
        }
        catch(CAtlException& e)
        {
            CString strError; // show what's going wrong
            strError.Format(_T("Create Direct2D bitmap from WIC bitmap failed.")
                _T("\nError code: 0x%08X"), e.m_hr);
            AfxMessageBox(strError);
        }
        return bRet;
    }

    In our case, the best place to call _CreateDirect2DBitmapFromWICBitmap is the overridden CView::OnUpdate function.
  9. Finally, render the bitmap.
    void CDemoImageViewerView::_RenderD2DBitmap(CDC* pDC)
    {
        try
        {
            m_pD2DRenderTarget->BeginDraw();
    
            // clear target background using white color
            ns::d2d1::COLOR_F color = {1.f, 1.f, 1.f, 1.f}; // r, g, b, a
            m_pD2DRenderTarget->Clear(color);
    
            if(nullptr != m_pD2DBitmap)
            {
                ns::d2d1::SIZE_F sizeBitmap = m_pD2DBitmap->GetSize();
    
                CPoint point = this->GetScrollPosition();
                m_pD2DRenderTarget->SetTranslationTransform((float)-point.x, (float)-point.y);
                ns::d2d1::RECT_F rcTarget = {0, 0, sizeBitmap.width, sizeBitmap.height};
                m_pD2DRenderTarget->DrawBitmap(rcTarget, m_pD2DBitmap);
            }
    
            m_pD2DRenderTarget->EndDraw();
        }
        catch(CAtlException& e)
        {
            CRect rcClip;
            pDC->GetClipBox(rcClip);
            pDC->FillSolidRect(rcClip, RGB(255, 255, 255));
            CString strError; // display what's going wrong
            strError.Format(_T("Drawing bitmap failed. Error code: 0x%08X"), e.m_hr);
            pDC->TextOut(10, 10, strError);
        }
    }

    Of course, in a class derived from CView, the place for calling _RenderD2DBitmap is the overridden CView::OnDraw virtual function.
  10. Remains just few little things to ajust: handle WM_ERASEBKGND and override CScrollView::OnScrollBy. Anyway, you can find all the implementation details in the attached demo application.

Demo application

The demo application is a basic image file viewer that uses WIC Wrapper Library.
Download: WIC Wrapper Library v2_0 and Image Viewer Demo Project.zip (426) (full WIC Wrapper Library source code is included).

Demo Image Viewer

Demo Image Viewer

Requirements

Resources, related articles, examples and projects


Tree-View Control – TVS_EX_RICHTOOLTIP style

$
0
0

Windows Vista introduced a buch of extended styles for tree-view control (SysTreeView32). Among them, there is TVS_EX_RICHTOOLTIP style which is described in MSDN documentation as follows: “Allow rich tooltips in the tree view (custom drawn with icon and text)”. This article shows a little bit more about how to set and which is the effect of using this extended style.

How to set and remove TVS_EX_RICHTOOLTIP style

Like the other tree-view extended styles, TVS_EX_RICHTOOLTIP can be set or removed as follows:

Sending TVM_SETEXTENDEDSTYLE message

DWORD dwExMask = TVS_EX_RICHTOOLTIP;
    // if m_bRichTooltip is true TVS_EX_RICHTOOLTIP is set
    // otherwise it is removed
    DWORD dwExStyles = m_bRichTooltip ? TVS_EX_RICHTOOLTIP : 0;
    
    HWND hWndTreeCtrl = ::GetDlgItem(hWndDlg, IDC_TREE_DEMO);
    ::SendMessage(hWndTreeCtrl, TVM_SETEXTENDEDSTYLE, (WPARAM)dwExMask, (LPARAM)dwExStyles);

Using TreeView_SetExtendedStyle macro

UINT uExMask = TVS_EX_RICHTOOLTIP;
    // if m_bRichTooltip is true TVS_EX_RICHTOOLTIP is set
    // otherwise it is removed
    DWORD dwExStyles = m_bRichTooltip ? TVS_EX_RICHTOOLTIP : 0;

    HWND hWndTreeCtrl = ::GetDlgItem(hWndDlg, IDC_TREE_DEMO);
    TreeView_SetExtendedStyle(hWndTreeCtrl, dwExStyles, uExMask);

Calling CTreeCtrl::SetExtendedStyle (MFC)

void CWhateverDialog::_SetOrRemoveRichTooltipStyle()
{
    UpdateData();

    DWORD dwExMask = TVS_EX_RICHTOOLTIP;
    // if m_bRichTooltip is true TVS_EX_RICHTOOLTIP is set
    // otherwise it is removed
    DWORD dwExStyles = m_bRichTooltip ? TVS_EX_RICHTOOLTIP : 0;

    m_treeCtrl.SetExtendedStyle(dwExMask, dwExStyles);
}

Further, let’s use the above code and see what’s happen.

The effect of TVS_EX_RICHTOOLTIP style

By default, if the mouse pointer hovers a partially visible tree item, a tootip containing the item’s text is shown.

Tree item tooltip - TVS_EX_RICHTOOLTIP not set

Tree item tooltip – TVS_EX_RICHTOOLTIP not set

If the TVS_EX_RICHTOOLTIP style is set, then the tooltip displays the item’s icon in addition to the item’s text.

Tree item tooltip - TVS_EX_RICHTOOLTIP set

Tree item tooltip – TVS_EX_RICHTOOLTIP set

Note that TVS_EX_RICHTOOLTIP has no effect if TVS_INFOTIP style is set. In this case, the tooltip shows the text provided in TVN_GETINFOTIP notification handler and no icon. So far, that’s all I found about it. If somebody can reveal more, please do not hesitate to leave a reply!

Downloads

Demo project (Visual Studio 2013) – TVS_EX_RICHTOOLTIP_Demo.zip (207)

Resources

MFC Support for Direct2D – Part 1

$
0
0

A few time ago, I begun writing a series of wrapper classes to make easier the using of Direct2D interfaces. Meanwhile, I discovered that MFC Library, beginning with Visual Studio 2010 SP1, offers an excelent built-in support. That’s pretty cool!
So, let’s use Direct2D (D2D) MFC classes for making a simple image viewer.

Image viewer using Direct2D MFC classes

  1. In the view’s class definition, add data members for render target and Direct2D bitmap.
    #pragma once
    #include <memory>
    
    class CDemoView : public CScrollView
    {
        CHwndRenderTarget m_renderTarget;
        std::shared_ptr<CD2DBitmap> m_spBitmap;
        // ...
    };
  2. Create the render target in the view’s WM_CREATE message handler.
    int CDemoView::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        if (CScrollView::OnCreate(lpCreateStruct) == -1)
            return -1;
    
        HRESULT hr = m_renderTarget.Create(m_hWnd);
        return SUCCEEDED(hr) ? 0 : -1;    
    }
  3. In the view’s WM_SIZE message handler resize the render target.
    void CDemoView::OnSize(UINT nType, int cx, int cy)
    {
        CScrollView::OnSize(nType, cx, cy);
    
        if(m_renderTarget.IsValid())
        {
            m_renderTarget.Resize(CD2DSizeU(cx, cy));
        }
    }
  4. Override CView::OnUpdate and load the bitmap from the current document’s file.
    void CDemoView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
    {
        CDemoDoc* pDoc = GetDocument();
        ASSERT_VALID(pDoc);
    
        if(nullptr != m_spBitmap)
        {
            m_spBitmap->Destroy();
        }
        CSize sizeImage(100, 100);
    
        const CString& strFile = pDoc->GetPathName();
        if(! strFile.IsEmpty())
        {
            m_spBitmap = std::make_shared<CD2DBitmap>(&m_renderTarget, strFile);
            HRESULT hr = m_spBitmap->Create(&m_renderTarget);
            if(m_spBitmap->IsValid())
            {
                CD2DSizeF size = m_spBitmap->GetSize();
                sizeImage.SetSize(static_cast<int>(size.width), static_cast<int>(size.height));
            }
        }
       SetScrollSizes(MM_TEXT, sizeImage);
       ScrollToPosition(CPoint(0, 0));
    }
  5. Prevent painintg in WM_ERASEBKGND message handler.
    BOOL CDemoView::OnEraseBkgnd(CDC* pDC)
    {
        if(m_renderTarget.IsValid() && m_spBitmap->IsValid())
        {
            return TRUE; // all drawing is made in WM_PAINT handler
        }
        else
        {
            return CScrollView::OnEraseBkgnd(pDC);
        }
    }
  6. Finally, override CView::OnDraw and draw the bitmap.
    void CDemoView::OnDraw(CDC* pDC)
    {
    	if(m_renderTarget.IsValid())
        {
            // initiate drawing on render target
            m_renderTarget.BeginDraw();
            // clear background using white color
            D2D1_COLOR_F color = {1.f, 1.f, 1.f, 1.f}; // r, g, b, a
            m_renderTarget.Clear(color);
            if((nullptr != m_spBitmap) && m_spBitmap->IsValid())
            {
                // apply translation transform according to view's scroll position
                CPoint point = GetScrollPosition();
                D2D1_MATRIX_3X2_F matrix = D2D1::Matrix3x2F::Translation((float)-point.x, (float)-point.y);
                m_renderTarget.SetTransform(matrix);
                // draw the bitmap
                CD2DSizeF size = m_spBitmap->GetSize();
                m_renderTarget.DrawBitmap(m_spBitmap.get(), CD2DRectF(0, 0, size.width, size.height));
            }
            // ends drawing operations 
            HRESULT hr = m_renderTarget.EndDraw();
            // if the render target has been lost, then recreate it
            if(D2DERR_RECREATE_TARGET == hr)
            {
                m_renderTarget.ReCreate(m_hWnd);
            }
        }
    }

Notes

  • CD2DBitmap::Create uses WIC (Windows Imaging Component) interfaces, so beside common image formats like BMP, JPEG, GIF, PNG, and TIFF, can deal also with any other image format for which a WIC-compliant decoder is installed in system.
  • Next article will show an even easier mode of using Direct2D MFC support.

Demo project

Download: Image Viewer - Using Direct2D MFC Classes.zip (246)

Using Direct2D MFC Classes - Demo Project

Using Direct2D MFC Classes – Demo Project

Resources and related articles

MFC Support for Direct2D – Part 2

$
0
0

A previous article shows how to use D2D MFC classes for easily make a simple image viewer. As said there, it can be made even easier.

Enabling D2D support in MFC

  1. In the WM_CREATE message handler call CWnd::EnableD2DSupport.
    int CDemoView::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        if (CScrollView::OnCreate(lpCreateStruct) == -1)
            return -1;
    
            EnableD2DSupport(); // Enable D2D support for this window:
    
        return 0;
    }
  2. In the overridden CView::OnUpdate load the bitmap from the current document’s file.
    void CDemoView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
    {
        CDemoDoc* pDoc = GetDocument();
        ASSERT_VALID(pDoc);
    
        CSize sizeImage(100, 100);
        delete m_pBitmap;
        m_pBitmap = NULL;
    
        const CString& strFile = pDoc->GetPathName();
        if(! strFile.IsEmpty())
        {
            CHwndRenderTarget* pRenderTarget = this->GetRenderTarget();
            ASSERT_VALID(pRenderTarget);
    
            m_pBitmap = new CD2DBitmap(pRenderTarget, strFile);
            HRESULT hr = m_pBitmap->Create(pRenderTarget);
            if(m_pBitmap->IsValid())
            {
                CD2DSizeF size = m_pBitmap->GetSize();
                sizeImage.SetSize(static_cast<int>(size.width), static_cast<int>(size.height));
            }
        }
       SetScrollSizes(MM_TEXT, sizeImage);
       ScrollToPosition(CPoint(0, 0));
    }
  3. Map AFX_WM_DRAW2D registered message which is sent by the framework if D2D support is enabled. Perform all Direct2D painting in the AFX_WM_DRAW2D message handler.
    LRESULT CDemoView::OnDrawUsingDirect2D(WPARAM wParam, LPARAM lParam)
    {
        CHwndRenderTarget* pRenderTarget = (CHwndRenderTarget*)lParam;
        ASSERT_VALID(pRenderTarget);
    
        if(pRenderTarget->IsValid())
        {
            D2D1_COLOR_F color = {1.f, 1.f, 1.f, 1.f}; // r, g, b, a
            pRenderTarget->Clear(color);
            if((nullptr != m_pBitmap) && m_pBitmap->IsValid())
            {
                // apply translation transform according to view's scroll position
                CPoint point = GetScrollPosition();
                D2D1_MATRIX_3X2_F matrix = D2D1::Matrix3x2F::Translation((float)-point.x, (float)-point.y);
                pRenderTarget->SetTransform(matrix);
                // draw the bitmap
                CD2DSizeF size = m_pBitmap->GetSize();
                pRenderTarget->DrawBitmap(m_pBitmap, CD2DRectF(0, 0, size.width, size.height));
            }
        }
        return TRUE;
    }

That’s all. No need to create a render target, resize it, recreating it when nesessary, calling BeginDraw, EndDraw, etc. All is done in the MFC framework if D2D support is enabled for a window.

Demo project

Download: Image VIewer - Enabling D2D Support for MFC.zip (302)

Resources and related articles

Codexpert – 2014 Articles Summary

$
0
0

Microsoft Libraries

C++ Language

Windows Tips

See also

Using Lambdas in MFC Applications – Part 1: Sorting Arrays

$
0
0

Beginning with Visual Studio 2010 which supports lambda expressions introduced by C++11 standard, you can handily sort an MFC array like in the following example:

Sorting CStringArray by using a lambda expression

// Example #1
    // ...
    CStringArray arr;
    arr.Add(_T("John"));
    arr.Add(_T("Zorro"));
    arr.Add(_T("Sandy"));
    arr.Add(_T("Jimmy"));
    // ...

    bool bAscending = true;
    // ...

    std::sort(
        arr.GetData(),                                           // position of first element
        arr.GetData() + arr.GetSize(),                           // position one past the final element 
        [bAscending]                                             // lambda introducer
        (const CString& left, const CString& right)              // lambda formal parameters list
        { return bAscending ? (left < right) : (left > right); } // lambda body
        );
    // ...

Of course, you can write similar code for other types of MFC arrays like CArray, CUIntArray, and so on.
But also you can easily write a kind of “generic lambda” in order to sort any type of MFC arrays.

Using decltype to sort any type of MFC array

// Example #2
    // ...
    std::sort(
        arr.GetData(), 
        arr.GetData() + arr.GetSize(),
        [bAscending]
        (decltype(*arr.GetData()) left, decltype(*arr.GetData()) right)
        { return bAscending ? (left < right) : (left > right); }
        );
    // ...

That’s pretty cool… However would be nice to be possible to get rid of “wordy” constructions like “decltype(*arr.GetData())” in the lambda’s formal parameters list. Good news! There is a proposal for next C++ standards: using auto type-specifier in order to make generic lambda expressions (which accept any type of arguments). And that is already supported in Visual Studio 2015.

Using generic (polymorphic) lambda expressions

// Example #3
    std::sort(
        arr.GetData(),
        arr.GetData() + arr.GetSize(),
        [bAscending]                                         
        (auto left, auto right)                                
        { return bAscending ? (left < right) : (left > right); }
        );

Notes

  • Some people may claim that using MFC collection classes is obsolete and must use STL containers instead. That’s an old subject of arguing but it’s not in scope of this short article.
    It simply presents how to sort MFC arrays by using lambda expressions.

References and related articles

Using Lambdas in MFC Applications – Part 2: Replacing Callback Functions

$
0
0

According to C++11 Standard, stateless lambdas, i.e. having an empty lambda introducer or capture no variables, are implicitly convertible to function pointers. Visual C++ in Visual Studio 2012 and newer, supports this feature. Moreover, in Visual C++ stateless lambdas are convertible to function pointers with arbitrary calling conventions. This is great if have to deal with Win32 API functions, most of them using __stdcall calling convention.
Let’s begin with two simple examples of enumerating top-level windows, one by using a callback function and the other by using a lambda expression.

Enumerate windows using “classic” callback functions

Usually, for that purpose we declare and implement as callback, a static member function having the signature required by EnumWindows in its first argument.

class CDemoDlg : public CDialog
{
    // ...

    static BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam);    
};

BOOL CALLBACK CDemoDlg::EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
    CDemoDlg* pDlg = reinterpret_cast<CDemoDlg*>(lParam);
    // do something, e.g. add the found window handle to a list
    return TRUE; // continue the enumeration
}

void CDemoDlg::OnButtonEnumWindows()
{
    ::EnumWindows(&CDemoDlg::EnumWindowsProc, reinterpret_cast<LPARAM>(this));
}

Notes

  • CALLBACK is defined in Windows SDK as __stdcall.

Enumerate windows using lambda expressions

The same result can be achieved by using a lambda expression. This is somehow simpler because does not require to declare and define a static member function.

void CDemoDlg::OnButtonEnumWindows()
{
    // in the first parameter of EnumWindows, pass a lambda expression
    ::EnumWindows([](HWND hWnd, LPARAM lParam) -> BOOL
    {
        CDemoDlg* pDlg = reinterpret_cast<CDemoDlg*>(lParam);
        // do something, e.g. push the found window handle in a list.
        return TRUE; // continue the enumeration
    }, 
    reinterpret_cast<LPARAM>(this)); // second parameter of EnumWindows
}

Notes

  • as stated in the introduction, there is not necessary to explicitly convert the lambda expression to the function pointer required by EnumWindowsProc (WNDENUMPROC);
  • as stated in the introduction, there is not possible to capture neither local variables, nor this pointer; luckily, like in many other WinAPI callbacks, we can pass the necessary stuff in a parameter of type LPARAM or LPVOID.

Using nested lambda expressions

Let’s say we want to enumerate the top-level windows in a worker thread. We can write something like this:

void CDemoDlg::OnButtonEnumWindows()
{
    AfxBeginThread([](LPVOID lpParam) -> UINT
    {
        ::EnumWindows([](HWND hWnd, LPARAM lParam) -> BOOL
        {
            CDemoDlg* pDlg = reinterpret_cast<CDemoDlg*>(lParam);
            // do something, e.g. push the found window handle in a list
            return TRUE; // continue the enumeration
        }, 
            reinterpret_cast<LPARAM>(lpParam)); // second parameter of EnumWindows
        return 0;
    },
        reinterpret_cast<LPVOID>(this));  // second parameter of AfxBeginThread
}

We have passed lambdas both for callback parameter of AfxBeginThread and EnumWindows. EnumWindows is called in the first lambda body and takes a lambda as well.
This compiles with no problem in x64 builds, but may give a conversion error for the inner lambda in Win32 ones. That’s a little glitch which AFAIK has been fixed in Visual Studio 2015.
However, to be sure it compiles also in Visual Studio 2012/2013 – Win32 builds, we can simply cast explicitly the inner lambda.

void CDemoDlg::OnButtonEnumWindows()
{
    AfxBeginThread([](LPVOID lpParam) -> UINT
    {
        ::EnumWindows(static_cast<WNDENUMPROC>([](HWND hWnd, LPARAM lParam) -> BOOL
        {
            CDemoDlg* pDlg = reinterpret_cast<CDemoDlg*>(lParam);
            // do something, e.g. push the found window handle in a list
            return TRUE; // continue the enumeration
        }), 
            reinterpret_cast<LPARAM>(lpParam)); // second parameter of EnumWindows
        return 0;
    },
        reinterpret_cast<LPVOID>(this));  // second parameter of AfxBeginThread
}

Notes

  • x64 builds has no __stdcall calling convention; __stdcall keyword is still accepted but simply ignored by the compiler.
  • of course, we can use lambda expressions in the same way also in ATL applications or C++ programs that use plain WinAPI.

Resources and related articles

Using Lambdas in MFC Applications – Part 3: Dealing with C++ REST SDK

$
0
0

C++ REST SDK (also known as Casablanca) offers support for HTTP client/server, JSON, URI, asynchronous streams, WebSockets client, oAuth and PPL Tasks.
We can get C++ REST SDK from CodePlex  or use the one shipped with Visual Studio 2013.
This article gives suggestions of how to set up a Visual C++ project (particularly one that uses MFC) in order to be able to deal with Casablanca library. It also shows some simple sample code and a demo MFC project.

Set up the project for using C++ REST SDK

First, must find the location where C++ REST SDK is installed. With Visual Studio 2013 installed, I found it in the following path:
C:\Program Files (x86)\Microsoft SDKs\Cpp REST SDK for Visual Studio 2013\SDK\ which is similar to $(ExtensionSDKDirectoryRoot)\Cpp REST SDK for Visual Studio 2013\SDK.
Next, create a new MFC project or open an existing one, right click on it’s name, choose “Properties” from the context menu, then do the following:

  1. Choose All configurations in the Configuration combo box.
  2. Go to Configuration Properties / C/C++ / General and add $(ExtensionSDKDirectoryRoot)\Cpp REST SDK for Visual Studio 2013\SDK\include to Additional Include Directories list.
  3. Go to Configuration Properties / C/C++ / Command Line and add -Zm200 in Additional Options field; this is necessary to avoid C3859 compiler error when include Casablanca headers in stdafx.h.
  4. Go to Configuration Properties / Linker / General and add $(ExtensionSDKDirectoryRoot)\Cpp REST SDK for Visual Studio 2013\SDK\lib\$(PlatformTarget)\$(Configuration) to Additional Library Directories list.
  5. Go to Configuration Properties / Linker / Input and add casablanca120.lib to Additional Dependencies.
  6. Also in Configuration Properties / Linker / Input add casablanca120.dll to Delay Loaded DLLs list; this is necessary to avoid false memory leaks reported by the MFC framework;
  7. Go to Configuration Properties / Build Events / Post-Build Event and add the following command to Command Line field
     copy /Y “$(ExtensionSDKDirectoryRoot)\Cpp REST SDK for Visual Studio 2013\SDK\bin\$(PlatformTarget)\$(Configuration)\casablanca120.dll” “$(OutDir)”; this copies the necessary Casablanca DLL from its install folder in the output directory of our project.
  8. Close the project property pages dialog.
  9. In stdafx.h include all necesary Casablanca headers, e.g. http_client.h, json.h, pplxtasks.h and so on.

Now, we are ready to go on and use C++ REST SDK stuff in our own MFC application.

Notes

  • Remember that other versions of Visual Studio may install C++ REST SDK in other location!
  • May be other methods to set up the project paths, as for example by using Property Manager and/or edit .props files; I just presented one presumed simpler.
  • Ones would prefer to get C++ REST SDK from CodePlex; probably, I’ll describe this alternative in a future article.

Next is some code sample. You can notice it uses lambda expressions and for that reason I included this article in Using Lambdas in MFC Applications series.

Using lambdas and C++ REST SDK with MFC – sample code

This sample sends a JSON structure in URI query part to a site which gives in response also a JSON which says if the sent one is valid or not and gives an error description if necessary.

void CJsonVerifierDlg::OnClickButtonVerifyJson()
{
    UpdateData();

    try
    {
        auto task = _VerifyJsonTask();
        task.wait();
        auto spJsonValue = task.get();
        if (spJsonValue)
            _DisplayResponse(spJsonValue);
    }
    catch (std::exception& e)
    {
        CString strMessage;
        strMessage.Format(L"Exception: %S", e.what());
        AfxMessageBox(strMessage, MB_ICONERROR);
    }
}

pplx::task<std::shared_ptr<web::json::value>> CJsonVerifierDlg::_VerifyJsonTask()
{
    return pplx::create_task([this]()
        {
            // base URI
            auto sBaseURI = utility::string_t(m_strBaseURI.GetString());
            // build request URI
            auto sRequestURI = web::http::uri_builder()
                .append_query(L"json", m_strInputJson.GetString()).to_string();
            // create http client
            web::http::client::http_client httpClient(sBaseURI);
            // create GET http request
            web::http::http_request httpRequest(U("GET")); //web::http::methods::GET);
            // set request URI
            httpRequest.set_request_uri(sRequestURI);
            // send request
            return httpClient.request(httpRequest);
            // then receive the response
        }).then([](web::http::http_response httpResponse)
            {
                std::shared_ptr<web::json::value> spJsonValue;
                if (web::http::status_codes::OK == httpResponse.status_code())
                {
                    spJsonValue = 
                        std::make_shared<web::json::value>(httpResponse.extract_json().get());
                }
                return spJsonValue;
            });
}

void CJsonVerifierDlg::_DisplayResponse(const std::shared_ptr<web::json::value>& spJsonValue)
{
    if ((*spJsonValue)[U("validate")].as_bool())
    {
        m_strIsValid = _T("YES");
        m_nParseTime = (*spJsonValue)[U("parse_time_nanoseconds")].as_integer();
    }
    else
    {
        m_strIsValid = _T("NO");
        m_strError = (*spJsonValue)[U("error")].as_string().c_str();
    }
    UpdateData(FALSE);
}

More details about project settings and source code can be found in the attached demo project.

Using lambdas and C++ REST SDK with MFC – demo project

Download: JSON Verifier - Demo Application using Casablanca.zip (93)

JSON Verifier - Demo Application using Casablanca

JSON Verifier – Demo Application using Casablanca

References and related articles


Easy Browse for Folder with MFC

$
0
0

SHBrowseForFolder shell API function shows a dialog which allows users to select a folder. As can be seen in MSDN documentation or other examples, it requires pretty much additional code around, so you would prefer to write your own wrapper in order to reuse code along applications. But luckily, MFC library has that wrapper already done.

Browse for folder using MFC

All you have to do is the following:

  1. derive your application class from CWinAppEx;
  2. whenever you want to show a “browse for folder” dialog
    • call CWinAppEx::GetShellManager to get a pointer to a global CShellManager object then
    • call CShellManager::BrowseForFolder.

A simple example of using CShellManager::BrowseForFolder

CString strOutFolder;
    // note: the application class is derived from CWinAppEx
    CShellManager* pShellManager = theApp.GetShellManager();
    ASSERT_VALID(pShellManager);
    if(pShellManager->BrowseForFolder(strOutFolder))
    {
        // do something cool!
    }

Notes

  • CWinAppEx as well as CShellManager classes were introduced with MFC Feature Pack. That means you need Visual Studio 2008 or newer;
  • CShellManager::BrowseForFolder has additional default parameters which can be used for more customization;
  • a little bit more complete example can be found in the demo application.

MFC Browse For Folder demo application

Download: MFC BrowseForFolder Demo.zip (70)

MFC BrowseForFolder demo application

MFC Browse For Folder demo application

Resources and related stuff

Easy Shadow Drawing with MFC

$
0
0

Years ago I had to draw shadows in a custom image thumbnails viewer and the code I used  was prety complicated and slow. Now can do that much easier by using CDrawingManager MFC class, defined in afxdrawmanager.h header file.

Draw shadows using CDrawingManager::DrawShadow

Here is a simple example:

void CChildView::OnPaint() 
{
    CPaintDC dc(this);

    CDrawingManager draw(dc);
    CRect rcDraw(10, 10, 200, 150);

    draw.DrawRect(rcDraw, RGB(0, 255, 255), RGB(0, 0, 128));
    const int nShadowDepth = 6;
    draw.DrawShadow(rcDraw, nShadowDepth);
}

You can even more speed up the drawing by passing two CBitmap objects, one for the bottom and one for the right shadow.

// ...

    CPaintDC dc(this);

    CDrawingManager draw(dc);
    CRect rcDraw;

    CBitmap bmpSaveBottom;
    CBitmap bmpSaveRight;
    const int nMinBrightness = 100;
    const int nMaxBrightness = 50; 
    const int nShadowDepth = 6;

    rcDraw.SetRect(10, 10, 200, 150);
    draw.DrawRect(rcDraw, RGB(0, 255, 255), RGB(0, 0, 0));
    draw.DrawShadow(rcDraw, nShadowDepth, nMinBrightness, nMaxBrightness, &bmpSaveBottom, &bmpSaveRight);

    rcDraw.OffsetRect(0, 160);
    draw.DrawRect(rcDraw, RGB(255, 0, 255), RGB(0, 0, 0));
    draw.DrawShadow(rcDraw, nShadowDepth, 0, 0, &bmpSaveBottom, &bmpSaveRight);

    // ...

A little bit more practical example can be found in the demo project attached to this article.

A simple thumbnail viewer using CDrawingManager for drawing shadows

Download demo project: MFC Shadow Drawing Demo.zip (30)

Thumbnail Viewer

Thumbnail Viewer

Resources and related articles

MFC Support for DirectWrite – Part 1

$
0
0

A previous article shows how easy is to make an image viewer with MFC by enabling Direct2D support and using MFC wrappers over Direct2D interfaces. Now let’s discover MFC wrapper classes for DirectWrite.

A simple example of drawing texts by using MFC DirectWrite wrapper classes

Basically, must follow these steps:

  1. Enable MFC support for Direct2D. A good place for doing it is in WM_CREATE message handler.
    int CDirectWriteDemoView::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        if (CView::OnCreate(lpCreateStruct) == -1)
            return -1;
    
        EnableD2DSupport(); // Enable D2D support for this window
        return 0;
    }
  2. If the view class is derived from CView, override CView::OnDraw pure virtual method. Do nothing inside it, because all drawing will be done in AFX_WM_DRAW2D message handler.
    void CDirectWriteDemoView::OnDraw(CDC* /*pDC*/)
    {
        // only override the pure virtual method from base class;
        // the rendering is done in AFX_WM_DRAW2D registered message handler.
    }
  3. Map a handler function for AFX_WM_DRAW2D MFC registered message.
    class CDirectWriteDemoView : public CView
    {
        // ...
        afx_msg LRESULT OnDraw2D(WPARAM wParam, LPARAM lParam);
    };

    // ...
    BEGIN_MESSAGE_MAP(CDirectWriteDemoView, CView)
        // ...
        ON_REGISTERED_MESSAGE(AFX_WM_DRAW2D, &CDirectWriteDemoView::OnDraw2D)
    END_MESSAGE_MAP()
    // ...
  4. In AFX_WM_DRAW2D message handler implementation, get the CHwndRenderTarget pointer which comes in lParam then use CD2DTextFormat and CD2DTextLayout to format and draw the text.
    LRESULT CDirectWriteDemoView::OnDraw2D(WPARAM wParam, LPARAM lParam)
    {
        CHwndRenderTarget* pRenderTarget = (CHwndRenderTarget*)lParam;
        ASSERT_VALID(pRenderTarget);
    
        // clear the drawing area
        pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::LightSkyBlue));
    
        // other drawing (e.g. an image) may come here...
        // ...
    
        // construct a CD2DTextFormat object which describes the font properties used to format text
        CD2DTextFormat textFormat(pRenderTarget,     // pointer to the render target
                                  strFontFamilyName, // font family name
                                  fFontSize,         // font size in DIP (1/96 inch)
                                  fontWeight,        // font weight (normal, bold, etc.)
                                  fontStyle,         // font style (normal, oblique or italic)
                                  fontStretch);      // font stretch 
    
        // construct a CD2DTextLayout object which represents a block of formatted text 
        CD2DTextLayout textLayout(pRenderTarget,     // pointer to the render target 
                                  strText,           // text to be drawn
                                  textFormat,        // text format
                                  sizeTarget);       // size of the layout box 
    
        // finally, draw the text
        pRenderTarget->DrawTextLayout(CD2DPointF(5, 5),    // top-left corner of the text 
                                      &textLayout,         // text layout object
                                      &CD2DSolidColorBrush // brush used for text
                                      (pRenderTarget, 
                                      D2D1::ColorF(D2D1::ColorF::DarkBlue))); 
    
        return 0;
    }

Notes

  • More details can be found in the demo application attached to this article.
  • DirectWrite has more capabilities like for example formatting a particular range in text and applying special efects; I will present them in a future article.
  • Minimum required Visual Studio version is 2010 SP1.
  • Minimum required operating systems are:
    • Windows 7, Windows Vista with SP2 and Platform Update for Windows Vista;
    • Windows Server 2008 R2, Windows Server 2008 with SP2 and Platform Update for Windows Server 2008.

Demo application

Download: MFC Support for DirectWrite Demo.zip (17)

MFC Support for DirectWrite - Demo Application.jpg

MFC Support for DirectWrite – Demo Application.jpg

References and related articles

MFC Support for DirectWrite – Part 2: Text Block Formatting

$
0
0

Beside the font attributes which can be set in the CD2DTextFormat MFC class constructor, DirectWrite supports more formatting for a block of text, e.g. text alignment, word wrapping, line spacing and so on. However, if taking a look at CD2DTextFormat documentation, we cannot find functions for doing that. Don’t worry, that’s not a problem at all: just call CD2DTextFormat::Get to obtain a pointer to contained IDWriteTextFormat interface, then directly call its methods like SetTextAlignment, SetWordWrapping, SetLineSpacing etc.

Formatting a block of text with DirectDraw in MFC applications

Here is a simple example, completing the one from the previous article:

// ...
    // We've constructed a CD2DTextFormat object with basic text attributes: 
    // font family name, size, weight, style and stretch.
    // Further, let's apply more text block formatting!

    // Get IDWriteTextFormat* and set the text alignment
    DWRITE_TEXT_ALIGNMENT textAlignment = _GetSelectedTextAlignment();
    HRESULT hr = textFormat.Get()->SetTextAlignment(textAlignment);
    // ...

     // Get IDWriteTextFormat* and set the word wrapping
    DWRITE_WORD_WRAPPING wordWrapping = _GetSelectedWordWrapping();
    hr = textFormat.Get()->SetWordWrapping(wordWrapping);
    // ...

    // Finally, construct a CD2DTextLayout object and draw the text in the render target.
    // ...

More details can be found in the attached demo application.

Demo application

Download: MFC Support for DirectWrite Demo (Part 2).zip (19)

MFC Support for DirectWrite (Part 2) – Demo Application

MFC Support for DirectWrite (Part 2) – Demo Application

Notes

  • This example shows only how to set text alignment and word wrapping. Of course, you can use any other format supported by IDWriteTextFormat interface.
  • Some of Direct2D & DirectWrite features are supported in Windows 8.1 and later and/or may be missing in Windows SDK prior to v8.1, e.g DWRITE_WORD_WRAPPING_CHARACTER value for DWRITE_WORD_WRAPPING enum, which used as argument for IDWriteTextFormat::SetWordWrapping. See the DirectWrite documentation in MSDN.

Resources and related articles

MFC Support for DirectWrite – Part 3: Text Range Format

$
0
0

Once having a CD2DTextLayout object containing a formatted block of text and before drawing in the render target, we can change the formatting for particular ranges of text. For that purpose, call CD2DTextLayout::Get then directly use IDWriteTextLayout interface methods like SetFontWeight, SetUnderline, SetStrikethrough and whatever else we need and is available.

An example of using IDWriteTextLayout to format ranges of text

std::shared_ptr<CD2DTextLayout> CDirectWriteDemoView::_InitTextLayout(CHwndRenderTarget* pRenderTarget, 
    const std::shared_ptr<CD2DTextFormat> spTextFormat)
{
    CString strText = _GetDocument()->GetText();
    CD2DSizeF sizeTarget = pRenderTarget->GetSize();

    // Construct a CD2DTextLayout object which represents a block of formatted text 
    std::shared_ptr<CD2DTextLayout> spTextLayout = 
        std::make_shared<CD2DTextLayout>(
                pRenderTarget,     // pointer to the render target 
                strText,           // text block to be drawn
                *spTextFormat,     // text format (a CD2DTextFormat object)
                sizeTarget);       // size of the layout box 

    // Call IDWriteTextLayout methods to format ranges of text
    IDWriteTextLayout* pIDWriteTextLayout = spTextLayout->Get();
    // "Arial" font family
    DWRITE_TEXT_RANGE textRange = {42, 17};
    pIDWriteTextLayout->SetFontFamilyName(_T("Arial"), textRange);
    // Extra-black font weight
    textRange.startPosition = 61; textRange.length = 18;
    pIDWriteTextLayout->SetFontWeight(DWRITE_FONT_WEIGHT_EXTRA_BLACK, textRange);
    // Italic style
    textRange.startPosition = 81; textRange.length = 12;
    pIDWriteTextLayout->SetFontStyle(DWRITE_FONT_STYLE_ITALIC, textRange);
    // Underline 
    textRange.startPosition = 95; textRange.length = 8;
    pIDWriteTextLayout->SetUnderline(TRUE, textRange);
    // Strike-through
    textRange.startPosition = 105; textRange.length = 13;
    pIDWriteTextLayout->SetStrikethrough(TRUE, textRange);
    // Double font size
    textRange.startPosition = 120; textRange.length = 24;
    FLOAT fFontSize = 2 * _GetSelectedFontSize(pRenderTarget);
    pIDWriteTextLayout->SetFontSize(fFontSize, textRange);
    // Some combination
    textRange.startPosition = 134; textRange.length = 22;
    pIDWriteTextLayout->SetFontStyle(DWRITE_FONT_STYLE_OBLIQUE, textRange);
    pIDWriteTextLayout->SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, textRange);
    pIDWriteTextLayout->SetUnderline(TRUE, textRange);
    pIDWriteTextLayout->SetFontFamilyName(_T("Arial"), textRange);

    return spTextLayout;
}

Notes

  • This example is just for demo purpose and contains hard-coded ranges. In a practical application you would probably need to get the ranges by parsing a text containing tags for text formatting (HTML, BBCode etc).
  • More details can be found in attached demo application

Demo application

Download: MFC Support for DirectWrite Demo (Part 3).zip (12)

MFC Support for DirectWrite (Part 3) – Demo Application

MFC Support for DirectWrite (Part 3) – Demo Application

Resources and related articles

MFC Support for DirectWrite (Part 4) – Inline Images

$
0
0

DirectWrite allows inserting objects in a block of text, e.g. images, as shown in the picture.

MFC Support for DirectWrite – Inline Images

MFC Support for DirectWrite (Part 4) – Inline Images

Let’s see how can be done!

Implement IDWriteInlineObject interface methods to insert inline images

Once we have created a CD2DTextLayout object, remains the following to do:

  1. Define a class for implementing IDWriteInlineObject interface, e.g. CInlineImage. We can derive it from IDWriteInlineObject then override and implement IDWriteInlineObject and IUnknown methods. However, if we already are using MFC, it’s a little bit handier to derive from CCmdTarget, then use MFC’s interface maps DECLARE_INTERFACE_MAP, BEGIN_INTERFACE_PART and so on.
    class CInlineImage : public CCmdTarget
    {
    public:
        CInlineImage();
        virtual ~CInlineImage();
        HRESULT Create(CHwndRenderTarget* pRenderTarget, UINT nResID, LPCTSTR pszResType);
        HRESULT Create(CHwndRenderTarget* pRenderTarget, LPCTSTR pszFileName);
        BOOL IsValid();
        operator IDWriteInlineObject*();
    
    public:
        DECLARE_INTERFACE_MAP()
        BEGIN_INTERFACE_PART(InlineImage, IDWriteInlineObject)
            STDMETHOD(Draw)(void*, IDWriteTextRenderer*, FLOAT, FLOAT, BOOL, BOOL, IUnknown*);
            STDMETHOD(GetMetrics)(DWRITE_INLINE_OBJECT_METRICS*);
            STDMETHOD(GetOverhangMetrics)(DWRITE_OVERHANG_METRICS*);
            STDMETHOD(GetBreakConditions)(DWRITE_BREAK_CONDITION*, DWRITE_BREAK_CONDITION*);
            CHwndRenderTarget* m_pRenderTarget;
            CD2DBitmap* m_pBitmap;
        END_INTERFACE_PART(InlineImage)
    };

    Detailed implementation can be found in the demo application attached to this article. Here is listed just an implementation example of overridden IDWriteInlineObject::Draw.
    STDMETHODIMP CInlineImage::XInlineImage::Draw(void* clientDrawingContext, 
        IDWriteTextRenderer* textRenderer, FLOAT fOriginX, FLOAT fOriginY,
        BOOL bIsSideways, BOOL bIsRightToLeft, IUnknown* clientDrawingEffect)
    {
        METHOD_PROLOGUE(CInlineImage, InlineImage);
        ASSERT_VALID(m_pRenderTarget);
        ASSERT_VALID(m_pBitmap);
    
        if(m_pRenderTarget->IsValid() && m_pBitmap->IsValid())
        {
            CD2DSizeF sizeBitmap = m_pBitmap->GetSize();
            m_pRenderTarget->DrawBitmap(m_pBitmap, 
                CD2DRectF(fOriginX, fOriginY, 
                    fOriginX + sizeBitmap.width, fOriginY + sizeBitmap.height));
        }
        return S_OK;
    }
  2. Create and initialize a CInlineImage object.
    class CDirectWriteDemoView : public CView
    {
        // ...
        CInlineImage m_inlineImage;
        // ...
    };

    int CDirectWriteDemoView::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        // ...
        // Enable D2D support for this window
        EnableD2DSupport();
    
        CHwndRenderTarget* pRenderTarget = GetRenderTarget();
    
        // create inline image objects
        HRESULT hr = m_inlineImageRings.Create(pRenderTarget, IDB_RINGS, T("PNG"));
        ASSERT(SUCCEEDED(hr));
        // ...
    }
  3. Pass the CInlineImage object to IDWriteTextLayout::SetInlineObject before drawing the text layout.
    LRESULT CDirectWriteDemoView::OnDraw2D(WPARAM wParam, LPARAM lParam)
    {
        CHwndRenderTarget* pRenderTarget = (CHwndRenderTarget*)lParam;
        // ...
    
        // init the inline objects
        _InitInlineObjects(pRenderTarget, spTextLayout);
    
        // Draw the text in the render target.
        pRenderTarget->DrawTextLayout(CD2DPointF(),         // upper-left corner of the text 
                                      spTextLayout.get(),   // text layout object
                                      &CD2DSolidColorBrush  // brush used for text
                                      (pRenderTarget, 
                                      D2D1::ColorF(D2D1::ColorF::DarkRed)));
    }

    void CDirectWriteDemoView::_InitInlineObjects(CHwndRenderTarget* pRenderTarget, 
        const std::shared_ptr<CD2DTextLayout> spTextLayout)
    {
        // ...
        CDirectWriteDemoDoc* pDoc = _GetDocument();
    
        DWRITE_TEXT_RANGE textRange;
    
        if(pDoc->GetTextRange(_T("[IMAGE:RINGS]"), textRange) && m_inlineImageRings.IsValid())
        {
            spTextLayout->Get()->SetInlineObject(m_inlineImageRings, textRange);
        }
        // ...
    }

Demo application

Download: MFC Support for DirectWrite Demo (Part 4).zip (5)

Notes

  • CInlineImage class implementation is intentionally left as simple as possible, for learning purpose. Of course, it can be improved, for example, by scaling images to fit the text height.
  • Pictures used in the demo application are taken from http://www.icondrawer.com/gift-icons.php

Resources and related articles

MFC Support for DirectWrite (Part 5) – Typography

$
0
0

Some fonts like Microsoft’s Gabriola support a number of typographic features which allow users to control how they look, e.g by enabling some fancy stylistic text rendering. With DirectWrite we can do that by passing a IDWriteTypography object to IDWriteTextLayout::SetTypography method.

Set text layout typography with DirectWrite in an MFC application

We have not an MFC class which wraps IDWriteTypography but that’s not a problem. First, we can get a pointer to IDWriteFactory from a global AFX_GLOBAL_DATA structure.  Further create an IDWriteTypography instance, add desired font feature(s) and finally, set the typography in a range of the text layout. Here is an example:

void CDirectWriteDemoView::_SetTextLayoutFontTypographicFeature(
    std::shared_ptr<CD2DTextLayout> spTextLayout, 
    DWRITE_FONT_FEATURE_TAG featureTag, DWRITE_TEXT_RANGE textRange)
{
    // get the global IDWriteFactory pointer
    IDWriteFactory*  pIDWriteFactory = afxGlobalData.GetWriteFactory();

    // create IDWriteTypography instance
    CComPtr<IDWriteTypography> spIDWriteTypography;
    HRESULT hr = pIDWriteFactory->CreateTypography(&spIDWriteTypography);
    ASSERT(SUCCEEDED(hr));

    // add font feature
    DWRITE_FONT_FEATURE fontFeature;
    fontFeature.nameTag = featureTag;
    fontFeature.parameter = 1;
    spIDWriteTypography->AddFontFeature(fontFeature);

    // set the typography in a range of the text layout
    spTextLayout->Get()->SetTypography(spIDWriteTypography, textRange);
}

Demo application

Download: MFC Support for DirectWrite Demo (Part 5).zip (7)

MFC Support for DirectWrite (Part 5) – Typography

MFC Support for DirectWrite (Part 5) – Typography

Resources and related articles


MFC Support for DirectWrite (Part 6) – Effects

$
0
0

We can change the color of a text range by passing a brush object to IDWriteTextLayout::SetDrawingEffect. Here is a simple example.

An example of using IDWriteTextLayout::SetDrawingEffect

LRESULT CDirectWriteDemoView::OnDraw2D(WPARAM wParam, LPARAM lParam)
{
   CHwndRenderTarget* pRenderTarget = (CHwndRenderTarget*)lParam;

   // Please, see the demo application for more details.
   // ...

   // init color effects 
   _InitColorEffects(pRenderTarget, spTextLayout);

   // Draw the text in the render target.
   pRenderTarget->DrawTextLayout(CD2DPointF(),         // upper-left corner of the text 
                                 spTextLayout.get(),   // text layout object
                                 &CD2DSolidColorBrush  // default brush used for text
                                 (pRenderTarget, 
                                 D2D1::ColorF(D2D1::ColorF::Black))); 
   return 0;
}

void CDirectWriteDemoView::_InitColorEffects(CHwndRenderTarget* pRenderTarget, 
                                   std::shared_ptr<CD2DTextLayout> spTextLayout)
{
   CDirectWriteDemoDoc* pDoc = _GetDocument();
   DWRITE_TEXT_RANGE textRange;

   if(pDoc->FindTextRange(_T("BLUE"), textRange))
       _SetTextLayoutDrawingEffect(pRenderTarget, spTextLayout, 
                                   D2D1::ColorF(D2D1::ColorF::Blue), textRange);

   if(pDoc->FindTextRange(_T("YELLOW"), textRange))
       _SetTextLayoutDrawingEffect(pRenderTarget, spTextLayout,
                                   D2D1::ColorF(D2D1::ColorF::Yellow), textRange);

   if(pDoc->FindTextRange(_T("RED"), textRange))
       _SetTextLayoutDrawingEffect(pRenderTarget, spTextLayout,
                                   D2D1::ColorF(D2D1::ColorF::Red), textRange);
}

void CDirectWriteDemoView::_SetTextLayoutDrawingEffect(CHwndRenderTarget* pRenderTarget, 
            std::shared_ptr<CD2DTextLayout> spTextLpayout,
            D2D1_COLOR_F color, DWRITE_TEXT_RANGE textRange)
{
    CD2DSolidColorBrush* pBrush = new CD2DSolidColorBrush(pRenderTarget, color);
    pBrush->Create(pRenderTarget);
    spTextLayout->Get()->SetDrawingEffect(static_cast<IUnknown*>(pBrush->Get()), textRange);
}

So far, there’s no much “special effects” in the above example. Although a little bit more difficult, it is possible to make more customized rendering like text highlighting, double/triple underline/strikethrough and so on. This will be the subject of a future article.

Demo application

Download: MFC Support for DirectWrite Demo - Part 6.zip (51)

MFC Support for DirectWrite (Part 6) – Effects

MFC Support for DirectWrite (Part 6) – Effects

Resources and related articles

Codexpert – 2015 Articles Summary

$
0
0

Microsoft Libraries and C++ Programming Language

What’s next?

We planned to continue the Direct2D/DirectWrite series but also other articles about programming with C++ using Microsoft Windows libraries like MFC, ATL, Windows API and so on.

See also

Easy PNG Resource Loading with MFC

$
0
0

If ask Internet about how to “load PNG from resource” it gives in return a lot of examples using a bunch of code. We can use some stuff found there, but once using MFC and enabling Direct2D support, that becomes easier: just have to construct and create a CD2DBitmap object, passing the resource ID and the resource type.
Here is an example:

Create a CD2DBitmap object from a PNG resource

HRESULT CSomeWindow::LoadDemoImage(CHwndRenderTarget* pRenderTarget)
{
    // Load Direct2D bitmap from "PNG" resource
    m_pBitmamLogo = new CD2DBitmap(pRenderTarget,
        IDB_PNG_DEMO,                             // resource ID
        _T("PNG"));                               // resource type

    return m_pBitmamLogo->Create(pRenderTarget);
}

It’s also possible to easily load a Direct2D brush.

Create a CD2DBitmapBrush object from a PNG resource

HRESULT CSomeWindow::LoadDemoBrush(CHwndRenderTarget* pRenderTarget)
{
    // Load Direct2D brush from "PNG" resource
    m_pBrushBackground = new CD2DBitmapBrush(pRenderTarget,
        IDB_PNG_DEMO,                                       // resource ID
        _T("PNG"),                                          // resource type
        CD2DSizeU(0, 0),
        &D2D1::BitmapBrushProperties(D2D1_EXTEND_MODE_WRAP, 
        D2D1_EXTEND_MODE_WRAP));

    return m_pBrushBackground->Create(pRenderTarget);
}

Notes

  • Loading PNG is just an example. We can load other WIC-supported formats (includes “standard” formats like JPEG, TIFF etc. and other graphic formats which have a WIC decoder installed).
  • To see how to enable Direct2D support and further render the loaded images, see the previous articles and/or have a look in the demo application attached here.

Demo application

Download: PNG Resource Loading Demo.zip (53)

PNG Resource Loading Demo

PNG Resource Loading Demo

References and related articles

MFC Static Control Using Direct2D and DirectWrite – version 1.0

$
0
0

While doing some refactoring on sample source code presented in MFC Support for DirectWrite series, I had the idea to make a control easy to be used for rendering text. It could be a custom MFC control derived from generic CWnd class or an ActiveX but finally I chose to derive from CStatic, just for simplicity.
By short, it consist in two main classes:

  • CDirect2DStatic extends MFC class CStatic, doing the following:
    • enables Direct2D MFC support;
    • handles AFX_WM_DRAW2D and AFX_WM_RECREATED2DRESOURCES registered messages, which are sent by the MFC framework to a window with Direct2D MFC support enabled.
  • CDirectWriteStaticCtrl, derived from CDirect2DStatic, effectively implements text formatting and rendering; it has the following groups of public methods:
    • operations: setting text and background brush;
    • text block format: set the format for the entire text;
    • text range format: set the format in a given range;
    • data access.

Notes

  • Implementation details can be found in the demo application, attached here for download.
  • So far, it doesn’t yet implement inline images, typography and effects but these will be done in a future version along with other Direct2D/DirectWrite features.

Demo application

Download: DirectWrite Static Control.zip (7)

DirectWrite Static Control

DirectWrite Static Control

 

Resources

See also

MFC Static Control Using Direct2D and DirectWrite (updated)

$
0
0

I have update the MFC static control presented in the previous articles by adding methods for setting text range color, typography and inline images.

Code samples

Setting text range color

void CDemoDlg::_DoDemo_Effects()
{
    m_staticDemo.Clear();

    const CString strTagRed = _T("RED");
    const CString strTagGreen = _T("GREEN");
    const CString strTagBlue = _T("BLUE");

    CString strText;
    strText.Format(_T("IDWriteTextLayout::SetDrawingEffect can change the color")
        _T(" of a text range as for example: \n\t%s\n\t%s\n\t%s"),
        strTagRed, strTagGreen, strTagBlue);

    m_staticDemo.SetText(strText);

    // set text range colors
    m_staticDemo.SetTextColor(strText.Find(strTagRed), strTagRed.GetLength(), RGB(255,0,0));
    m_staticDemo.SetTextColor(strText.Find(strTagGreen), strTagGreen.GetLength(), RGB(0, 255, 0));
    m_staticDemo.SetTextColor(strText.Find(strTagBlue), strTagBlue.GetLength(), RGB(0, 0, 255));
}

Setting text range typography

void CDemoDlg::_DoDemo_Typography()
{
    m_staticDemo.Clear();

    const CString strTagFontTypography = _T("Fancy Typography Rendering");

    CString strText;
    strText.Format(_T("Some OpenType fonts (e.g. Microsoft’s Gabriola) support %s"), 
        strTagFontTypography);

    m_staticDemo.SetText(strText);
    const UINT32 nStartPos = strText.Find(strTagFontTypography);
    const UINT32 nLength = strTagFontTypography.GetLength();

    // set an OpenType font which supports typography features
    m_staticDemo.SetFontFamilyName(nStartPos, nLength, _T("Gabriola"));
    // set feature
    m_staticDemo.SetFontTypography(nStartPos, nLength, DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_7);
}

Setting inline images

void CDemoDlg::_DoDemo_InlineImages()
{
    m_staticDemo.Clear();

    const CString strTagImageRings = _T("[IMAGE:RINGS]");
    const CString strTagImageRose = _T("[IMAGE:ROSE]");
    const CString strTagImageDog = _T("[IMAGE:DOG]");

    CString strText;
    strText.Format(_T("Here are two rings %s a beautiful rose %s  and a little cute dog %s"),
        strTagImageRings, strTagImageRose, strTagImageDog);

    m_staticDemo.SetText(strText);

    // set inline images
    m_staticDemo.SetInlineImage(strText.Find(strTagImageRings), strTagImageRings.GetLength(), 
        _T("PNG"), IDB_PNG_RINGS);
    m_staticDemo.SetInlineImage(strText.Find(strTagImageRose), strTagImageRose.GetLength(), 
        _T("PNG"), IDB_PNG_ROSE);
    m_staticDemo.SetInlineImage(strText.Find(strTagImageDog), strTagImageDog.GetLength(), 
        _T("PNG"), IDB_PNG_DOG);
}

Demo application

Download: DirectWrite Static Control (updated).zip (21)

MFC DirectWrite Static Control

MFC DirectWrite Static Control

Resources

See also

Viewing all 41 articles
Browse latest View live