From 72d95e978d2fcd4b560b70e5697cbd0920a53640 Mon Sep 17 00:00:00 2001 From: FPtje Date: Thu, 5 Nov 2015 19:28:07 +0100 Subject: Render tree to bitmap instead of CDC This has quite a performance gain. The generation of the tree view went down from about four seconds to 700ms --- windirstat/Controls/treemap.cpp | 114 +++++++++++++++++++++++++++++++--------- windirstat/Controls/treemap.h | 23 ++++---- 2 files changed, 102 insertions(+), 35 deletions(-) (limited to 'windirstat') diff --git a/windirstat/Controls/treemap.cpp b/windirstat/Controls/treemap.cpp index 5d1950e..9183e06 100644 --- a/windirstat/Controls/treemap.cpp +++ b/windirstat/Controls/treemap.cpp @@ -29,6 +29,8 @@ #define new DEBUG_NEW #endif +#define BGR(b,g,r) ((COLORREF)(((BYTE)(b)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(r))<<16))) + // I define the "brightness" of an rgb value as (r+b+g)/3/255. // The EqualizeColors() method creates a palette with colors // all having the same brightness of 0.6 @@ -314,6 +316,8 @@ void CTreemap::DrawTreemap(CDC *pdc, CRect rc, Item *root, const Options *option return; } + m_renderArea = rc; + if(root->TmiGetSize() > 0) { double surface[4]; @@ -322,7 +326,34 @@ void CTreemap::DrawTreemap(CDC *pdc, CRect rc, Item *root, const Options *option surface[i]= 0; } - RecurseDrawGraph(pdc, root, rc, true, surface, m_options.height, 0); + // Create a temporary CDC that represents only the tree map + CDC dcTreeView; + dcTreeView.CreateCompatibleDC(pdc); + + // This temporary CDC will be filled with this bitmap + CBitmap bmp; + + // That bitmap in turn will be created from this array + CArray *bitmap = new CArray; + bitmap->SetSize(rc.Width() * rc.Height()); + + // Recursively draw the tree graph + RecurseDrawGraph(bitmap, root, rc, true, surface, m_options.height, 0); + + // Fill the bitmap with the array + bmp.CreateBitmap(rc.Width(), rc.Height(), 1, 32, &(*bitmap)[0]); + + // Render bitmap to the temporary CDC + dcTreeView.SelectObject(&bmp); + + // And lastly, draw the temporary CDC to the real one + pdc->BitBlt(rc.TopLeft().x, rc.TopLeft().y, rc.Width(), rc.Height(), &dcTreeView, 0, 0, SRCCOPY); + + // Free memory + bmp.DeleteObject(); + dcTreeView.DeleteDC(); + delete bitmap; + #ifdef STRONGDEBUG // slow, but finds bugs! #ifdef _DEBUG @@ -486,7 +517,31 @@ void CTreemap::DrawColorPreview(CDC *pdc, const CRect& rc, COLORREF color, const AddRidge(rc, surface, m_options.height * m_options.scaleFactor); - RenderRectangle(pdc, rc, surface, color); + m_renderArea = rc; + + // Create a temporary CDC that represents only the tree map + CDC dcTreeView; + dcTreeView.CreateCompatibleDC(pdc); + + // This temporary CDC will be filled with this bitmap + CBitmap bmp; + + // That bitmap in turn will be created from this array + CArray *bitmap = new CArray; + bitmap->SetSize(rc.Width() * rc.Height()); + + // Recursively draw the tree graph + RenderRectangle(bitmap, CRect(0, 0, rc.Width(), rc.Height()), surface, color); + + // Fill the bitmap with the array + bmp.CreateBitmap(rc.Width(), rc.Height(), 1, 32, &(*bitmap)[0]); + + // Render bitmap to the temporary CDC + dcTreeView.SelectObject(&bmp); + + // And lastly, draw the temporary CDC to the real one + pdc->BitBlt(rc.TopLeft().x, rc.TopLeft().y, rc.Width(), rc.Height(), &dcTreeView, 0, 0, SRCCOPY); + if(m_options.grid) { CPen pen(PS_SOLID, 1, m_options.gridColor); @@ -494,10 +549,15 @@ void CTreemap::DrawColorPreview(CDC *pdc, const CRect& rc, COLORREF color, const CSelectStockObject sobrush(pdc, NULL_BRUSH); pdc->Rectangle(rc); } + + // Free memory + bmp.DeleteObject(); + dcTreeView.DeleteDC(); + delete bitmap; } void CTreemap::RecurseDrawGraph( - CDC *pdc, + CArray *bitmap, Item *item, const CRect& rc, bool asroot, @@ -542,14 +602,14 @@ void CTreemap::RecurseDrawGraph( if(item->TmiIsLeaf()) { - RenderLeaf(pdc, item, surface); + RenderLeaf(bitmap, item, surface); } else { WEAK_ASSERT(item->TmiGetChildrenCount() > 0); WEAK_ASSERT(item->TmiGetSize() > 0); - DrawChildren(pdc, item, surface, h, flags); + DrawChildren(bitmap, item, surface, h, flags); } } @@ -559,7 +619,7 @@ void CTreemap::RecurseDrawGraph( // pointers, factory methods and explicit destruction. It's not worth. void CTreemap::DrawChildren( - CDC *pdc, + CArray *bitmap, Item *parent, const double *surface, double h, @@ -570,19 +630,19 @@ void CTreemap::DrawChildren( { case KDirStatStyle: { - KDirStat_DrawChildren(pdc, parent, surface, h, flags); + KDirStat_DrawChildren(bitmap, parent, surface, h, flags); } break; case SequoiaViewStyle: { - SequoiaView_DrawChildren(pdc, parent, surface, h, flags); + SequoiaView_DrawChildren(bitmap, parent, surface, h, flags); } break; case SimpleStyle: { - Simple_DrawChildren(pdc, parent, surface, h, flags); + Simple_DrawChildren(bitmap, parent, surface, h, flags); } break; } @@ -592,7 +652,7 @@ void CTreemap::DrawChildren( // I learned this squarification style from the KDirStat executable. // It's the most complex one here but also the clearest, imho. // -void CTreemap::KDirStat_DrawChildren(CDC *pdc, Item *parent, const double *surface, double h, DWORD /*flags*/) +void CTreemap::KDirStat_DrawChildren(CArray *bitmap, Item *parent, const double *surface, double h, DWORD /*flags*/) { WEAK_ASSERT(parent->TmiGetChildrenCount() > 0); @@ -661,7 +721,7 @@ void CTreemap::KDirStat_DrawChildren(CDC *pdc, Item *parent, const double *surfa } #endif - RecurseDrawGraph(pdc, child, rcChild, false, surface, h * m_options.scaleFactor, 0); + RecurseDrawGraph(bitmap, child, rcChild, false, surface, h * m_options.scaleFactor, 0); if(lastChild) { @@ -816,7 +876,7 @@ double CTreemap::KDirStat_CalcutateNextRow(Item *parent, const int nextChild, do // The classical squarification method. // -void CTreemap::SequoiaView_DrawChildren(CDC *pdc, Item *parent, const double *surface, double h, DWORD /*flags*/) +void CTreemap::SequoiaView_DrawChildren(CArray *bitmap, Item *parent, const double *surface, double h, DWORD /*flags*/) { // Rest rectangle to fill CRect remaining(parent->TmiGetRectangle()); @@ -971,7 +1031,7 @@ void CTreemap::SequoiaView_DrawChildren(CDC *pdc, Item *parent, const double *su WEAK_ASSERT(rc.top >= remaining.top); WEAK_ASSERT(rc.bottom <= remaining.bottom); - RecurseDrawGraph(pdc, parent->TmiGetChild(i), rc, false, surface, h * m_options.scaleFactor, 0); + RecurseDrawGraph(bitmap, parent->TmiGetChild(i), rc, false, surface, h * m_options.scaleFactor, 0); if(lastChild) break; @@ -1015,12 +1075,12 @@ void CTreemap::SequoiaView_DrawChildren(CDC *pdc, Item *parent, const double *su // No squarification. Children are arranged alternately horizontally and vertically. // -void CTreemap::Simple_DrawChildren(CDC *pdc, Item *parent, const double *surface, double h, DWORD flags) +void CTreemap::Simple_DrawChildren(CArray *bitmap, Item *parent, const double *surface, double h, DWORD flags) { #if 1 WEAK_ASSERT(0); // Not used in WinDirStat. - pdc; parent; surface; h; flags; + bitmap; parent; surface; h; flags; #else WEAK_ASSERT(parent->TmiGetChildrenCount() > 0); @@ -1072,7 +1132,7 @@ void CTreemap::Simple_DrawChildren(CDC *pdc, Item *parent, const double *surface } RecurseDrawGraph( - pdc, + bitmap, parent->TmiGetChild(i), rcChild, false, @@ -1103,7 +1163,7 @@ bool CTreemap::IsCushionShading() && m_options.scaleFactor > 0.0; } -void CTreemap::RenderLeaf(CDC *pdc, Item *item, const double *surface) +void CTreemap::RenderLeaf(CArray *bitmap, Item *item, const double *surface) { CRect rc = item->TmiGetRectangle(); @@ -1117,10 +1177,10 @@ void CTreemap::RenderLeaf(CDC *pdc, Item *item, const double *surface) } } - RenderRectangle(pdc, rc, surface, item->TmiGetGraphColor()); + RenderRectangle(bitmap, rc, surface, item->TmiGetGraphColor()); } -void CTreemap::RenderRectangle(CDC *pdc, const CRect& rc, const double *surface, DWORD color) +void CTreemap::RenderRectangle(CArray *bitmap, const CRect& rc, const double *surface, DWORD color) { double brightness = m_options.brightness; @@ -1145,15 +1205,15 @@ void CTreemap::RenderRectangle(CDC *pdc, const CRect& rc, const double *surface, if(IsCushionShading()) { - DrawCushion(pdc, rc, surface, color, brightness); + DrawCushion(bitmap, rc, surface, color, brightness); } else { - DrawSolidRect(pdc, rc, color, brightness); + DrawSolidRect(bitmap, rc, color, brightness); } } -void CTreemap::DrawSolidRect(CDC *pdc, const CRect& rc, COLORREF col, double brightness) +void CTreemap::DrawSolidRect(CArray *bitmap, const CRect& rc, COLORREF col, double brightness) { int red = RGB_GET_RVALUE(col); int green = RGB_GET_GVALUE(col); @@ -1167,10 +1227,14 @@ void CTreemap::DrawSolidRect(CDC *pdc, const CRect& rc, COLORREF col, double bri CColorSpace::NormalizeColor(red, green, blue); - pdc->FillSolidRect(rc, RGB(red, green, blue)); + for (int iy = rc.top; iy < rc.bottom; iy++) + for (int ix = rc.left; ix < rc.right; ix++) + { + (*bitmap)[ix + iy * m_renderArea.Width()] = BGR(blue, green, red); + } } -void CTreemap::DrawCushion(CDC *pdc, const CRect& rc, const double *surface, COLORREF col, double brightness) +void CTreemap::DrawCushion(CArray *bitmap, const CRect& rc, const double *surface, COLORREF col, double brightness) { // Cushion parameters const double Ia = m_options.ambientLight; @@ -1221,7 +1285,7 @@ void CTreemap::DrawCushion(CDC *pdc, const CRect& rc, const double *surface, COL CColorSpace::NormalizeColor(red, green, blue); // ... and set! - pdc->SetPixel(ix, iy, RGB(red, green, blue)); + (*bitmap)[ix + iy * m_renderArea.Width()] = BGR(blue, green, red); } } diff --git a/windirstat/Controls/treemap.h b/windirstat/Controls/treemap.h index 0d29382..505289f 100644 --- a/windirstat/Controls/treemap.h +++ b/windirstat/Controls/treemap.h @@ -72,7 +72,7 @@ public: // If you prefer to use the getHead()/getNext() pattern rather // than using an array for the children, you will have to // rewrite CTreemap. - // + // class Item { public: @@ -187,7 +187,7 @@ public: protected: // The recursive drawing function void RecurseDrawGraph( - CDC *pdc, + CArray *bitmap, Item *item, const CRect& rc, bool asroot, @@ -198,7 +198,7 @@ protected: // This function switches to KDirStat-, SequoiaView- or Simple_DrawChildren void DrawChildren( - CDC *pdc, + CArray *bitmap, Item *parent, const double *surface, double h, @@ -206,15 +206,15 @@ protected: ); // KDirStat-like squarification - void KDirStat_DrawChildren(CDC *pdc, Item *parent, const double *surface, double h, DWORD flags); + void KDirStat_DrawChildren(CArray *bitmap, Item *parent, const double *surface, double h, DWORD flags); bool KDirStat_ArrangeChildren(Item *parent, CArray& childWidth, CArray& rows, CArray& childrenPerRow); double KDirStat_CalcutateNextRow(Item *parent, const int nextChild, double width, int& childrenUsed, CArray& childWidth); // Classical SequoiaView-like squarification - void SequoiaView_DrawChildren(CDC *pdc, Item *parent, const double *surface, double h, DWORD flags); + void SequoiaView_DrawChildren(CArray *bitmap, Item *parent, const double *surface, double h, DWORD flags); // No squarification (simple style, not used in WinDirStat) - void Simple_DrawChildren(CDC *pdc, Item *parent, const double *surface, double h, DWORD flags); + void Simple_DrawChildren(CArray *bitmap, Item *parent, const double *surface, double h, DWORD flags); // Sets brightness to a good value, if system has only 256 colors void SetBrightnessFor256(); @@ -223,16 +223,17 @@ protected: bool IsCushionShading(); // Leaves space for grid and then calls RenderRectangle() - void RenderLeaf(CDC *pdc, Item *item, const double *surface); + void RenderLeaf(CArray *bitmap, Item *item, const double *surface); // Either calls DrawCushion() or DrawSolidRect() - void RenderRectangle(CDC *pdc, const CRect& rc, const double *surface, DWORD color); + void RenderRectangle(CArray *bitmap, const CRect& rc, const double *surface, DWORD color); + // void RenderRectangle(CDC *pdc, const CRect& rc, const double *surface, DWORD color); // Draws the surface using SetPixel() - void DrawCushion(CDC *pdc, const CRect& rc, const double *surface, COLORREF col, double brightness); + void DrawCushion(CArray *bitmap, const CRect& rc, const double *surface, COLORREF col, double brightness); // Draws the surface using FillSolidRect() - void DrawSolidRect(CDC *pdc, const CRect& rc, COLORREF col, double brightness); + void DrawSolidRect(CArray *bitmap, const CRect& rc, COLORREF col, double brightness); // Adds a new ridge to surface static void AddRidge(const CRect& rc, double *surface, double h); @@ -242,6 +243,8 @@ protected: static const COLORREF _defaultCushionColors[]; // Standard palette for WinDirStat static const COLORREF _defaultCushionColors256[]; // Palette for 256-colors mode + CRect m_renderArea; + Options m_options; // Current options double m_Lx; // Derived parameters double m_Ly; -- cgit v1.2.3