|
Alternative to MFC for GDI programmingContents
The demo programEssentially, what the demo program needs to do is draw a bunch of things by either using OLIGDI or MFC, and time the operation and display the difference between the two paint methods. My starting point for the demo program is the cute clover program written by Charles Petzold for his book Programming Windows class cHighResolutionTimer { public: cHighResolutionTimer(); void start(); double stop(); private: LARGE_INTEGER frequency, startTime; }; cHighResolutionTimer::cHighResolutionTimer() { startTime.QuadPart = 0; LASTERRORDISPLAYD(QueryPerformanceFrequency(&frequency)); } void cHighResolutionTimer::start() { LASTERRORDISPLAYD(QueryPerformanceCounter(&startTime)); } double cHighResolutionTimer::stop() { LARGE_INTEGER stopTime; LASTERRORDISPLAYD(QueryPerformanceCounter(&stopTime)); return (double)(stopTime.QuadPart - startTime.QuadPart)/frequency.QuadPart; } The most challenging part of programming the demo program has been to output meaningful numbers out of the timing measurements. Something that I have noticed during the development is that, measuring the same drawing method multiple times results in large variations in the timing. This could be caused by multiple factors such as software inconsistencies (task switching) and hardware inconsistencies (GDI device driver having to wait for a particular moment in the video card refresh cycle to perform writes). Since the timing variations are of the same order as the speed differences, I had great difficulties to highlight this difference. After many attempts with different methods, I have devised the following scheme:
#define NUMSAMP 12 class CTimingStat { public: CTimingStat() { reset(); } void reset(void) { m_nSamples = 0; } void set(double s) { m_samplArr[m_nSamples++] = s; } const UINT getnSamples(void) const { return m_nSamples; } double getAverage(void); private: double m_samplArr[NUMSAMP]; UINT m_nSamples; static int __cdecl compare(const void *elem1, const void *elem2); }; double CTimingStat::getAverage(void) { int a; double xa = 0.0; qsort(m_samplArr,NUMSAMP,sizeof(double), CTimingStat::compare); for( a = NUMSAMP/3; a < (2*NUMSAMP/3); a++ ) { xa += m_samplArr[a]; } xa /= NUMSAMP/3.0; return xa; } int CTimingStat::compare(const void *elem1, const void *elem2) { return (int)(*(double *)elem1 - *(double *)elem2); }
dc.SelectClipRgn((HRGN)RgnClip.GetSafeHandle()); /* * Since Clip region is in device point, it is important to offset * it because the double buffering DC window origin is set at the top * corner of the invalidated rect. */ dc.OffsetClipRgn(p.x,p.y); Another interesting point of the demo program is that I have derived a
specialized StatusBar class int CCloverStatusBar::OnCreate(LPCREATESTRUCT lpCreateStruct) { RECT toolRect; if (CStrechyStatusBar::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here if (!SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { return -1; // fail to create } /* * Set the first strechy indicator width to its minimum to * make sure that the right side indicators do not disapear when * the status bar width is reduced. */ MakeStrechy(); m_ttctl.Create(this); GetItemRect(PAINTTIMEPANE,&toolRect); m_ttctl.AddRectTool(m_hWnd,MAKEINTRESOURCE(IDS_ONOLIGDI),&toolRect,PAINTTIMEPANE); return 0; } /* * Function CCloverStatusBar::SetPaintMethod */ void CCloverStatusBar::SetPaintMethod(UINT nID) { static LPCTSTR MethodNameTb[3] = { __TEXT("\tOLIGDI"), __TEXT("\tMFC"), __TEXT("\tAlternate") }; m_paintMethod = nID; SetPaneText(METHODNAMEPANE, MethodNameTb[m_paintMethod]); m_ttctl.UpdateTipText(nID+IDS_ONOLIGDI,this,PAINTTIMEPANE); if( m_paintMethod == USEALTERNATE ) { initStats(); } } /* * Function CCloverStatusBar::OnSize */ void CCloverStatusBar::OnSize(UINT nType, int cx, int cy) { RECT toolRect; CStrechyStatusBar::OnSize(nType,cx,cy); GetItemRect(PAINTTIMEPANE,&toolRect); m_ttctl.SetToolRect(this,PAINTTIMEPANE,&toolRect); } Please note that ConclusionThe results are very disappointing. On my machine, I got a shy improvement varying from 1% to 3%. It seems that the result depends largely on the hardware on which the demo program is run; as I tested it on different machines, with few exceptions where I witnessed 10%-15% improvement, the improvement is generally below 5%. Without measurements, the difference is not visually perceptible. The conclusion that can be drawn from this experiment is that despite MFC's overhead, it is negligible compare to the time spent inside the GDI functions themselves. That is it! I hope you enjoyed this C++ Windows programming tutorial. In the next section, you will find the books that I have consulted to build this C++ Windows programming tutorial. Those books are great and filled with Windows programming gems that you should know. It is strongly recommended that you get yourself a copy of these books especially since from time to time, you can find these books at a bargain price. Take the time to check the prices. This is maybe your lucky day today! Also, if you get amazing results with the demo program on your machine, or if you found an application for this code, I would love to hear from you! BibliographyHistory
|
Home :: Fractals :: tutorials :: Books :: Archive :: My blog :: My LinkedIn Profile :: Contact