Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

2D Water Effect in WTL

0.00/5 (No votes)
28 Apr 2011 1  
A WTL control class to add water effect to an image, like what's done in the TortoiseSVN About dialog
Sample Image - maximum width is 600 pixels

Introduction

I have been a long time TortoiseSVN user impressed by the water effects shown on the About dialog banner picture. It feels like putting a glass of water on top of a picture, when you move the mouse over the picture, the water is disturbed and whirls around, meanwhile, water drops randomly fall into the water and yield small circles here and there.

tortoisesvn about dialog effects

Since TortoiseSVN is open source and I happened to be bored around these days, I decided to look into the implementation algorithm, and to get myself some work to do. I ported it to WTL, hence this article.

Background

The algorithm used in TortoiseSVN is based on this article, which is explanatory enough. The main changes I've made are related to porting the code from MFC to WTL as outlined below. The TortoiseSVN implementation is based on MFC, specifically, it relies on an MFC class CPictureHolder to interact with an image file. In my version, it's replaced with a newer ATL/MFC shared class CImage. Such a change brings some immediate advantages:

More image format support

While CPictureHolder supports only BMP and ICO files, CImage provides enhanced bitmap support, including the ability to load images in JPEG, GIF, BMP, and Portable Network Graphics (PNG) formats. PNG and JPG files can be loaded directly without additional decoding.

More flexible image loading

While CPictureHolder supports loading the image from resource only, CImage provides an interface to load the image from a file path.

More Efficient memory usage

Because CImage supports direct access to pixels, it saves the need to create a temporary compatible device context(DC) and an associated compatible bitmap in memory, which is adopted in the TortoiseSVN's MFC version to retrieve pixels in CPictureHolder. There's also a CImage pixel access performance optimization available on this CodeProject site, if the image is too big and CImage's GetPixel() becomes a performance bottleneck (yes, it happens if your image is relatively large).

More Image Rendering options

CImage supports PlgBlt(), MaskBlt(), AlphaBlend(), TransparentBlt(), which means you have options to choose while rendering the loaded image.

Aggressive refactoring has been applied to the original code such as renaming, interface changing, so that except for the core pixel manipulation code, the code is mostly new. However, refactoring is refactoring, the attribute still goes to the original author, whose license remains where it was. Only obsolete code comments are removed.

Using the Code

To use the code, add the following files into your project:

WaterEffectImplBase.h

Contains classes WaterEffectImplBase<Derived> and WaterEffectCtl ready to be used by client code directly.

Render.h/cpp

Contain class CRenderer to load an image file, create a memory buffer for it and render it to a target window area after water effect manipulation, used by classes WaterEffectImplBase<Derived> as implementation details.

WaterEffect.h/cpp

Contain class CWaterEffect to apply the 2D water transformation to the memory buffer exposed by class CRenderer, used by classes WaterEffectImplBase<Derived> as implementation details.

auto_buffer.h

Contains class auto_buffer<T>, a little class to buffer creation and destroy.

cimage_pixel_access_opt.h

Contains class CImagePixelAccessOptimizer for CImage pixel access optimization.

And then #include "WaterEffectImplBase.h" , which implements two classes WaterEffectImplBase<Derived> and WaterEffectCtl, which means you have two ways of adding water effect to your dialog.

Method 1

The WTL developer's familiar CRTP way, derive your window class from WaterEffectImplBase.

#include "WaterEffectImplBase.h"

class CDemoDlg: public CDialogImpl<CDemoDlg>,
                public WaterEffectImplBase<CDemoDlg>
{...};

Then in order to chain the windows messages to, you need to add this line to the message map:

BEGIN_MSG_MAP(CMainDlg)
	...
	CHAIN_MSG_MAP(WaterEffectImplBase<CDemoDlg>)
END_MSG_MAP()

And finally, in the OnInitDialog()method, add one line to init()it:

LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, 
LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	// ...

	//draw on the dialog directly starting from the topleft
	init(IDB_LOGOFLIPPED, CPoint(0, 0)); 

	// ...

	return TRUE;
}

In this way, the picture is directly drawn on the dialog at the designated position.

Method 2

Alternatively, you can use WaterEffectCtl as a control by declaring a class member of WaterEffectCtl object.

#include "WaterEffectImplBase.h"

class CDemoDlg : public CDialogImpl<CDemoDlg>
{
public:
	WaterEffectCtl we;
...};

Then, in the OnInitDialog()method, add this following line to init() it:

LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, 
LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	// ...

	// draw on the member static control
	we.init(GetDlgItem(IDC_WATER2), IDB_LOGOFLIPPED, CPoint(0, 0));

	// ...

	return TRUE;
}

In this case, a Window handle is required for the WaterEffectCtl object to attach to.

Synopsis

Class template<class Derived> class WaterEffectImplBase

void init(_U_STRINGorID nIDResource, const CPoint& topleft = CPoint(0,0))
void init(const CImage& image, const CPoint& topleft = CPoint(0,0))

Parameters

  • _U_STRINGorID nIDResource, the ID of an image resource to be loaded from .rc file
  • const CImage& image, an image can be pre-loaded into a CImage object and passed in
  • CPoint& topleft, the topleft position of the image to be placed, the image would be rendered with no size change

Class class WaterEffectCtl

void init(HWND hWnd, _U_STRINGorID nIDResource, const CPoint& topleft = CPoint(0,0))
void init(HWND hWnd, const CImage& image, const CPoint& topleft = CPoint(0,0))

Parameters

  • HWND hWnd, a child control to be attached to so that the image can be rendered
  • _U_STRINGorID nIDResource, the ID of an image resource to be loaded from .rc file
  • const CImage& image, an image can be pre-loaded in to a CImage object and passed in
  • CPoint& topleft, the topleft position of the image to be placed, the image would be rendered with no size change

History

  • First release, 2011-4-27

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here